log; $this->setCurrentLogLevelFromConfig($logConfig); $this->setLogWritersFromConfig($logConfig); $this->setLogFilePathFromConfig($logConfig); $this->setStringLogMessageFormat($logConfig); $this->disableLoggingBasedOnConfig($logConfig); } /** * Logs a message using the ERROR log level. * * Note: Messages logged with the ERROR level are always logged to the screen in addition * to configured writers. * * @param string $message The log message. This can be a sprintf format string. * @param ... mixed Optional sprintf params. */ public static function error($message /* ... */) { self::log(self::ERROR, $message, array_slice(func_get_args(), 1)); } /** * Logs a message using the WARNING log level. * * @param string $message The log message. This can be a sprintf format string. * @param ... mixed Optional sprintf params. */ public static function warning($message /* ... */) { self::log(self::WARN, $message, array_slice(func_get_args(), 1)); } /** * Logs a message using the INFO log level. * * @param string $message The log message. This can be a sprintf format string. * @param ... mixed Optional sprintf params. */ public static function info($message /* ... */) { self::log(self::INFO, $message, array_slice(func_get_args(), 1)); } /** * Logs a message using the DEBUG log level. * * @param string $message The log message. This can be a sprintf format string. * @param ... mixed Optional sprintf params. */ public static function debug($message /* ... */) { self::log(self::DEBUG, $message, array_slice(func_get_args(), 1)); } /** * Logs a message using the VERBOSE log level. * * @param string $message The log message. This can be a sprintf format string. * @param ... mixed Optional sprintf params. */ public static function verbose($message /* ... */) { self::log(self::VERBOSE, $message, array_slice(func_get_args(), 1)); } /** * Creates log message combining logging info including a log level, tag name, * date time, and caller provided log message. The log message can be set through * the string_message_format ini option in the [log] section. By default it will * create log messages like: * * [tag:datetime] log message * * @param int $level * @param string $tag * @param string $datetime * @param string $message * @return string */ public function formatMessage($level, $tag, $datetime, $message) { return str_replace( array("%tag%", "%message%", "%datetime%", "%level%"), array($tag, $message, $datetime, $this->getStringLevel($level)), $this->logMessageFormat ); } private function setLogWritersFromConfig($logConfig) { // set the log writers $logWriters = $logConfig[self::LOG_WRITERS_CONFIG_OPTION]; $logWriters = array_map('trim', $logWriters); foreach ($logWriters as $writerName) { $writer = $this->createWriterByName($writerName); if (!empty($writer)) { $this->writers[] = $writer; } if ($writerName == 'screen') { $this->loggingToScreen = true; } } } private function setCurrentLogLevelFromConfig($logConfig) { if (!empty($logConfig[self::LOG_LEVEL_CONFIG_OPTION])) { $logLevel = $this->getLogLevelFromStringName(self::LOG_LEVEL_CONFIG_OPTION); if ($logLevel >= self::NONE // sanity check && $logLevel <= self::VERBOSE ) { $this->currentLogLevel = $logLevel; } } } private function setStringLogMessageFormat($logConfig) { if (isset($logConfig['string_message_format'])) { $this->logMessageFormat = $logConfig['string_message_format']; } } private function setLogFilePathFromConfig($logConfig) { $logPath = $logConfig[self::LOGGER_FILE_PATH_CONFIG_OPTION]; if ($logPath[0] != '/' && $logPath[0] != DIRECTORY_SEPARATOR) { $logPath = PIWIK_USER_PATH . '/' . $logPath; } if (is_dir($logPath)) { $logPath .= '/piwik.log'; } $this->logToFilePath = SettingsPiwik::rewriteTmpPathWithHostname($logPath); } private function createWriterByName($writerName) { $writer = false; if ($writerName == 'file') { $writer = array($this, 'logToFile'); } else if ($writerName == 'screen') { $writer = array($this, 'logToScreen'); } else if ($writerName == 'database') { $writer = array($this, 'logToDatabase'); } return $writer; } private function logToFile($level, $tag, $datetime, $message) { if (is_string($message)) { $message = $this->formatMessage($level, $tag, $datetime, $message); } else { Piwik_PostEvent(self::FORMAT_FILE_MESSAGE_EVENT, array(&$message, $level, $tag, $datetime, $this)); } if (empty($message)) { return; } file_put_contents($this->logToFilePath, $message . "\n", FILE_APPEND); } private function logToScreen($level, $tag, $datetime, $message) { if (is_string($message)) { $message = Common::sanitizeInputValue($this->formatMessage($level, $tag, $datetime, $message)); $message = '
' . $message . '
'; } else { Piwik_PostEvent(self::FORMAT_SCREEN_MESSAGE_EVENT, array(&$message, $level, $tag, $datetime, $this)); } if (empty($message)) { return; } echo $message . "\n"; } private function logToDatabase($level, $tag, $datetime, $message) { if (is_string($message)) { $message = $this->formatMessage($level, $tag, $datetime, $message); } else { Piwik_PostEvent(self::FORMAT_DATABASE_MESSAGE_EVENT, array(&$message, $level, $tag, $datetime, $this)); } if (empty($message)) { return; } $sql = "INSERT INTO " . Common::prefixTable('logger_message') . " (tag, timestamp, level, message)" . " VALUES (?, ?, ?, ?)"; Db::query($sql, array($tag, $datetime, $level, (string)$message)); } private function doLog($level, $message, $sprintfParams = array()) { if ($this->shouldLoggerLog($level)) { $datetime = date("Y-m-d H:i:s"); if (is_string($message) && !empty($sprintfParams) ) { $message = vsprintf($message, $sprintfParams); } $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $tag = Plugin::getPluginNameFromBacktrace($backtrace); // if we can't determine the plugin, use the name of the calling class if ($tag == false) { $tag = $this->getClassNameThatIsLogging($backtrace); } $this->writeMessage($level, $tag, $datetime, $message); } } private function writeMessage($level, $tag, $datetime, $message) { foreach ($this->writers as $writer) { call_user_func($writer, $level, $tag, $datetime, $message); } // errors are always printed to screen if ($level == self::ERROR && !$this->loggingToScreen ) { $this->logToScreen($level, $tag, $datetime, $message); } } private static function log($level, $message, $sprintfParams) { self::getInstance()->doLog($level, $message, $sprintfParams); } private function shouldLoggerLog($level) { return $level <= $this->currentLogLevel; } private function disableLoggingBasedOnConfig($logConfig) { $disableLogging = false; if (!empty($logConfig['log_only_when_cli']) && !Common::isPhpCliMode() ) { $disableLogging = true; } if (!empty($logConfig['log_only_when_debug_parameter']) && !isset($_REQUEST['debug']) ) { $disableLogging = true; } if ($disableLogging) { $this->currentLogLevel = self::NONE; } } private function getLogLevelFromStringName($name) { switch (strtoupper($name)) { case 'NONE': return self::NONE; case 'ERROR': return self::ERROR; case 'WARN': return self::WARN; case 'INFO': return self::INFO; case 'DEBUG': return self::DEBUG; case 'VERBOSE': return self::VERBOSE; default: return -1; } } private function getStringLevel($level) { static $levelToName = array( self::NONE => 'NONE', self::ERROR => 'ERROR', self::WARN => 'WARN', self::INFO => 'INFO', self::DEBUG => 'DEBUG', self::VERBOSE => 'VERBOSE' ); return $levelToName[$level]; } private function getClassNameThatIsLogging($backtrace) { foreach ($backtrace as $tracepoint) { if (isset($tracepoint['class']) && $tracepoint['class'] != "Piwik\\Log" ) { return $tracepoint['class']; } } return false; } }