diff options
author | Matthieu Napoli <matthieu@mnapoli.fr> | 2014-12-01 04:07:26 +0300 |
---|---|---|
committer | Matthieu Napoli <matthieu@mnapoli.fr> | 2014-12-01 04:07:26 +0300 |
commit | 65c96bb562d1da6d1163aa1ad70ddde37c7f7af5 (patch) | |
tree | 2f29bb1cd46652437aac237414070bcccbeee91a | |
parent | 1d5aedc49ab2e5d07c10e600fcd5a12a1f9ce378 (diff) |
#6622 Logger refactoring: formatting messages is now done through Formatters instead of events
-rw-r--r-- | CHANGELOG.md | 5 | ||||
-rw-r--r-- | core/Error.php | 19 | ||||
-rw-r--r-- | core/ExceptionHandler.php | 20 | ||||
-rw-r--r-- | core/Log.php | 26 | ||||
-rw-r--r-- | core/Log/Backend/Backend.php | 28 | ||||
-rw-r--r-- | core/Log/Backend/DatabaseBackend.php | 37 | ||||
-rw-r--r-- | core/Log/Backend/FileBackend.php | 47 | ||||
-rw-r--r-- | core/Log/Backend/ScreenBackend.php | 79 | ||||
-rw-r--r-- | core/Log/Backend/StdErrBackend.php | 56 | ||||
-rw-r--r-- | core/Log/Backend/StdOutBackend.php | 24 | ||||
-rw-r--r-- | core/Log/Formatter/AddRequestIdFormatter.php | 40 | ||||
-rw-r--r-- | core/Log/Formatter/ErrorHtmlFormatter.php | 4 | ||||
-rw-r--r-- | core/Log/Formatter/ErrorTextFormatter.php | 4 | ||||
-rw-r--r-- | core/Log/Formatter/ExceptionHtmlFormatter.php | 4 | ||||
-rw-r--r-- | core/Log/Formatter/ExceptionTextFormatter.php | 4 | ||||
-rw-r--r-- | core/Log/Formatter/Formatter.php | 31 | ||||
-rw-r--r-- | core/Log/Formatter/HtmlPreFormatter.php | 34 | ||||
-rw-r--r-- | core/Log/Formatter/LineMessageFormatter.php | 58 | ||||
-rw-r--r-- | core/Log/LoggerFactory.php | 62 |
19 files changed, 328 insertions, 254 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index d85406f477..c02dfd9c1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ This is a changelog for Piwik platform developers. All changes for our HTTP API' ### Breaking Changes * Some duplicate reports from UserSettings plugin have been removed. Widget URLs for those reports will still work till May 1st 2015. Please update those to the new reports of DevicesDetection plugin. +* The following events have been removed: + * `Log.formatFileMessage` + * `Log.formatDatabaseMessage` + * `Log.formatScreenMessage` + * These events where very specific events for an internal need (logging exceptions) and have been replaced by a more extensible solution. ### Deprecations * The API method `UserSettings.getBrowserVersion` is deprecated and will be removed from May 1st 2015. Use `DevicesDetection.getBrowserVersions` instead diff --git a/core/Error.php b/core/Error.php index 54d575d881..33b53132d9 100644 --- a/core/Error.php +++ b/core/Error.php @@ -8,9 +8,6 @@ */ namespace Piwik; -use Piwik\Log\Formatter\ErrorHtmlFormatter; -use Piwik\Log\Formatter\ErrorTextFormatter; - require_once PIWIK_INCLUDE_PATH . '/core/Log.php'; /** @@ -123,24 +120,8 @@ class Error } } - public static function formatFileAndDBLogMessage(&$message, $level, $tag, $datetime, Log $log) - { - $formatter = new ErrorTextFormatter(); - $message = $formatter->format($message, $level, $tag, $datetime, $log); - } - - public static function formatScreenMessage(&$message, $level, $tag, $datetime, $log) - { - $formatter = new ErrorHtmlFormatter(); - $message = $formatter->format($message, $level, $tag, $datetime, $log); - } - public static function setErrorHandler() { - Piwik::addAction('Log.formatFileMessage', array('Piwik\Error', 'formatFileAndDBLogMessage')); - Piwik::addAction('Log.formatDatabaseMessage', array('Piwik\Error', 'formatFileAndDBLogMessage')); - Piwik::addAction('Log.formatScreenMessage', array('Piwik\Error', 'formatScreenMessage')); - set_error_handler(array('Piwik\Error', 'errorHandler')); } diff --git a/core/ExceptionHandler.php b/core/ExceptionHandler.php index dfadcc2763..9bbb5a23ac 100644 --- a/core/ExceptionHandler.php +++ b/core/ExceptionHandler.php @@ -8,10 +8,6 @@ */ namespace Piwik; -use Piwik\Log\Formatter\ExceptionHtmlFormatter; -use Piwik\Log\Formatter\ExceptionTextFormatter; -use Piwik\Plugin; - /** * Contains Piwik's uncaught exception handler and log file formatting for exception * instances. @@ -27,25 +23,9 @@ class ExceptionHandler public static function setUp() { - Piwik::addAction('Log.formatFileMessage', array('Piwik\ExceptionHandler', 'formatFileAndDBLogMessage')); - Piwik::addAction('Log.formatDatabaseMessage', array('Piwik\ExceptionHandler', 'formatFileAndDBLogMessage')); - Piwik::addAction('Log.formatScreenMessage', array('Piwik\ExceptionHandler', 'formatScreenMessage')); - set_exception_handler(array('Piwik\ExceptionHandler', 'logException')); } - public static function formatFileAndDBLogMessage(&$message, $level, $tag, $datetime, Log $log) - { - $formatter = new ExceptionTextFormatter(); - $message = $formatter->format($message, $level, $tag, $datetime, $log); - } - - public static function formatScreenMessage(&$message, $level, $tag, $datetime, $log) - { - $formatter = new ExceptionHtmlFormatter(); - $message = $formatter->format($message, $level, $tag, $datetime, $log); - } - public static function logException(\Exception $exception) { Log::error($exception); diff --git a/core/Log.php b/core/Log.php index 52123d3593..2892c4406d 100644 --- a/core/Log.php +++ b/core/Log.php @@ -10,7 +10,8 @@ namespace Piwik; use Piwik\Container\StaticContainer; use Piwik\Db; -use Piwik\Log\Backend\ScreenBackend; +use Piwik\Log\Backend\StdOutBackend; +use Piwik\Log\LoggerFactory; /** * Logging utility class. @@ -308,16 +309,6 @@ class Log extends Singleton foreach ($this->writers as $writer) { call_user_func($writer, $level, $tag, $datetime, $message, $this); } - - // TODO this hack should be removed - if ($level == self::ERROR) { - $screenBackend = new ScreenBackend($this->logMessageFormat); - $message = $screenBackend->getMessageFormattedScreen($level, $tag, $datetime, $message, $this); - $this->writeErrorToStandardErrorOutput($message); - if (!isset($this->writers['screen'])) { - echo $message; - } - } } private static function logMessage($level, $message, $sprintfParams) @@ -358,19 +349,6 @@ class Log extends Singleton } /** - * @param $message - */ - private function writeErrorToStandardErrorOutput($message) - { - if (defined('PIWIK_TEST_MODE')) { - // do not log on stderr during tests (prevent display of errors in CI output) - return; - } - $fe = fopen('php://stderr', 'w'); - fwrite($fe, $message); - } - - /** * Returns the name of the plugin/class that triggered the log. * * @return string diff --git a/core/Log/Backend/Backend.php b/core/Log/Backend/Backend.php index 6c3e64545c..e5e77753de 100644 --- a/core/Log/Backend/Backend.php +++ b/core/Log/Backend/Backend.php @@ -9,6 +9,7 @@ namespace Piwik\Log\Backend; use Piwik\Log; +use Piwik\Log\Formatter\Formatter; /** * Log backend. @@ -16,41 +17,30 @@ use Piwik\Log; abstract class Backend { /** - * The log message format string that turns a tag name, date-time and message into - * one string to log. - * - * @var string + * @var Formatter */ - private $logMessageFormat; + private $formatter; - public function __construct($logMessageFormat) + public function __construct(Formatter $formatter) { - $this->logMessageFormat = $logMessageFormat; + $this->formatter = $formatter; } public abstract function __invoke($level, $tag, $datetime, $message, Log $logger); /** - * 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 `[log] string_message_format` INI config option. By default it will - * create log messages like: - * - * **LEVEL [tag:datetime] log message** + * Formats the log message using the configured formatter. * * @param int $level * @param string $tag * @param string $datetime * @param string $message + * @param Log $logger * @return string */ - protected function formatMessage($level, $tag, $datetime, $message) + protected function formatMessage($level, $tag, $datetime, $message, Log $logger) { - return str_replace( - array("%tag%", "%message%", "%datetime%", "%level%"), - array($tag, trim($message), $datetime, $this->getStringLevel($level)), - $this->logMessageFormat - ); + return trim($this->formatter->format($message, $level, $tag, $datetime, $logger)); } protected function getStringLevel($level) diff --git a/core/Log/Backend/DatabaseBackend.php b/core/Log/Backend/DatabaseBackend.php index 2ffb7386d3..5c7e8710bb 100644 --- a/core/Log/Backend/DatabaseBackend.php +++ b/core/Log/Backend/DatabaseBackend.php @@ -11,7 +11,6 @@ namespace Piwik\Log\Backend; use Piwik\Common; use Piwik\Db; use Piwik\Log; -use Piwik\Piwik; /** * Writes log to database. @@ -20,7 +19,8 @@ class DatabaseBackend extends Backend { public function __invoke($level, $tag, $datetime, $message, Log $logger) { - $message = $this->getMessageFormattedDatabase($level, $tag, $datetime, $message, $logger); + $message = $this->formatMessage($level, $tag, $datetime, $message, $logger); + if (empty($message)) { return; } @@ -31,37 +31,4 @@ class DatabaseBackend extends Backend Db::query($sql, array($tag, $datetime, self::getStringLevel($level), (string)$message)); } - - private function getMessageFormattedDatabase($level, $tag, $datetime, $message, $logger) - { - if (is_string($message)) { - $message = $this->formatMessage($level, $tag, $datetime, $message); - } else { - /** - * Triggered when trying to log an object to a database table. Plugins can use - * this event to convert objects to strings before they are logged. - * - * **Example** - * - * public function formatDatabaseMessage(&$message, $level, $tag, $datetime, $logger) { - * if ($message instanceof MyCustomDebugInfo) { - * $message = $message->formatForDatabase(); - * } - * } - * - * @param mixed &$message The object that is being logged. Event handlers should - * check if the object is of a certain type and if it is, - * set `$message` to the string that should be logged. - * @param int $level The log level used with this log entry. - * @param string $tag The current plugin that started logging (or if no plugin, - * the current class). - * @param string $datetime Datetime of the logging call. - * @param Log $logger The Log singleton. - */ - Piwik::postEvent(Log::FORMAT_DATABASE_MESSAGE_EVENT, array(&$message, $level, $tag, $datetime, $logger)); - } - $message = trim($message); - - return $message; - } } diff --git a/core/Log/Backend/FileBackend.php b/core/Log/Backend/FileBackend.php index 4df90b91f2..6759827a77 100644 --- a/core/Log/Backend/FileBackend.php +++ b/core/Log/Backend/FileBackend.php @@ -10,7 +10,7 @@ namespace Piwik\Log\Backend; use Piwik\Filechecks; use Piwik\Log; -use Piwik\Piwik; +use Piwik\Log\Formatter\Formatter; /** * Writes log to file. @@ -24,19 +24,17 @@ class FileBackend extends Backend */ private $logToFilePath; - public function __construct($logMessageFormat, $logToFilePath) + public function __construct(Formatter $formatter, $logToFilePath) { $this->logToFilePath = $logToFilePath; - parent::__construct($logMessageFormat); + parent::__construct($formatter); } public function __invoke($level, $tag, $datetime, $message, Log $logger) { - $message = $this->getMessageFormattedFile($level, $tag, $datetime, $message, $logger); - if (empty($message)) { - return; - } + $message = $this->formatMessage($level, $tag, $datetime, $message, $logger); + $message = str_replace("\n", "\n ", $message) . "\n"; if (!@file_put_contents($this->logToFilePath, $message, FILE_APPEND) && !defined('PIWIK_TEST_MODE') @@ -45,39 +43,4 @@ class FileBackend extends Backend throw new \Exception($message); } } - - private function getMessageFormattedFile($level, $tag, $datetime, $message, Log $logger) - { - if (is_string($message)) { - $message = $this->formatMessage($level, $tag, $datetime, $message); - } else { - /** - * Triggered when trying to log an object to a file. Plugins can use - * this event to convert objects to strings before they are logged. - * - * **Example** - * - * public function formatFileMessage(&$message, $level, $tag, $datetime, $logger) { - * if ($message instanceof MyCustomDebugInfo) { - * $message = $message->formatForFile(); - * } - * } - * - * @param mixed &$message The object that is being logged. Event handlers should - * check if the object is of a certain type and if it is, - * set `$message` to the string that should be logged. - * @param int $level The log level used with this log entry. - * @param string $tag The current plugin that started logging (or if no plugin, - * the current class). - * @param string $datetime Datetime of the logging call. - * @param Log $logger The Log instance. - */ - Piwik::postEvent(Log::FORMAT_FILE_MESSAGE_EVENT, array(&$message, $level, $tag, $datetime, $logger)); - } - - $message = trim($message); - $message = str_replace("\n", "\n ", $message); - - return $message . "\n"; - } } diff --git a/core/Log/Backend/ScreenBackend.php b/core/Log/Backend/ScreenBackend.php deleted file mode 100644 index 01e8a38fb7..0000000000 --- a/core/Log/Backend/ScreenBackend.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ - -namespace Piwik\Log\Backend; - -use Piwik\Common; -use Piwik\Log; -use Piwik\Piwik; - -/** - * Writes log to screen. - */ -class ScreenBackend extends Backend -{ - public function __invoke($level, $tag, $datetime, $message, Log $logger) - { - $message = $this->getMessageFormattedScreen($level, $tag, $datetime, $message, $logger); - if (empty($message)) { - return; - } - - echo $message; - } - - public function getMessageFormattedScreen($level, $tag, $datetime, $message, Log $logger) - { - static $currentRequestKey; - - if (empty($currentRequestKey)) { - $currentRequestKey = substr(Common::generateUniqId(), 0, 5); - } - - if (is_string($message)) { - if (!defined('PIWIK_TEST_MODE')) { - $message = '[' . $currentRequestKey . '] ' . $message; - } - $message = $this->formatMessage($level, $tag, $datetime, $message); - - if (!Common::isPhpCliMode()) { - $message = Common::sanitizeInputValue($message); - $message = '<pre>' . $message . '</pre>'; - } - } else { - /** - * Triggered when trying to log an object to the screen. Plugins can use - * this event to convert objects to strings before they are logged. - * - * The result of this callback can be HTML so no sanitization is done on the result. - * This means **YOU MUST SANITIZE THE MESSAGE YOURSELF** if you use this event. - * - * **Example** - * - * public function formatScreenMessage(&$message, $level, $tag, $datetime, $logger) { - * if ($message instanceof MyCustomDebugInfo) { - * $message = Common::sanitizeInputValue($message->formatForScreen()); - * } - * } - * - * @param mixed &$message The object that is being logged. Event handlers should - * check if the object is of a certain type and if it is, - * set `$message` to the string that should be logged. - * @param int $level The log level used with this log entry. - * @param string $tag The current plugin that started logging (or if no plugin, - * the current class). - * @param string $datetime Datetime of the logging call. - * @param Log $logger The Log singleton. - */ - Piwik::postEvent(Log::FORMAT_SCREEN_MESSAGE_EVENT, array(&$message, $level, $tag, $datetime, $logger)); - } - $message = trim($message); - - return $message . "\n"; - } -} diff --git a/core/Log/Backend/StdErrBackend.php b/core/Log/Backend/StdErrBackend.php new file mode 100644 index 0000000000..c2ef47cc75 --- /dev/null +++ b/core/Log/Backend/StdErrBackend.php @@ -0,0 +1,56 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Log\Backend; + +use Piwik\Log; +use Piwik\Log\Formatter\Formatter; + +/** + * Writes log to stderr. + */ +class StdErrBackend extends Backend +{ + /** + * @var bool + */ + private $isLoggingToStdOut; + + public function __construct(Formatter $formatter, $isLoggingToStdOut) + { + $this->isLoggingToStdOut = $isLoggingToStdOut; + + parent::__construct($formatter); + } + + public function __invoke($level, $tag, $datetime, $message, Log $logger) + { + if ($level != Log::ERROR) { + return; + } + + $message = $this->formatMessage($level, $tag, $datetime, $message, $logger) . "\n"; + + // Do not log on stderr during tests (prevent display of errors in CI output) + if (! defined('PIWIK_TEST_MODE')) { + $this->writeToStdErr($message); + } + + // This is the result of an old hack, I guess to force the error message to be displayed in case of errors + // TODO we should get rid of it somehow + if (! $this->isLoggingToStdOut) { + echo $message; + } + } + + private function writeToStdErr($message) + { + $fe = fopen('php://stderr', 'w'); + fwrite($fe, $message); + } +} diff --git a/core/Log/Backend/StdOutBackend.php b/core/Log/Backend/StdOutBackend.php new file mode 100644 index 0000000000..2429289404 --- /dev/null +++ b/core/Log/Backend/StdOutBackend.php @@ -0,0 +1,24 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Log\Backend; + +use Piwik\Log; + +/** + * Writes log to stdout. + */ +class StdOutBackend extends Backend +{ + public function __invoke($level, $tag, $datetime, $message, Log $logger) + { + $message = $this->formatMessage($level, $tag, $datetime, $message, $logger); + + echo $message . "\n"; + } +} diff --git a/core/Log/Formatter/AddRequestIdFormatter.php b/core/Log/Formatter/AddRequestIdFormatter.php new file mode 100644 index 0000000000..ee061d1d0d --- /dev/null +++ b/core/Log/Formatter/AddRequestIdFormatter.php @@ -0,0 +1,40 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Log\Formatter; + +use Piwik\Common; +use Piwik\Log; + +/** + * Adds a unique "request id" to the log message to follow log entries for each HTTP request. + */ +class AddRequestIdFormatter extends Formatter +{ + public function format($message, $level, $tag, $datetime, Log $logger) + { + static $currentRequestKey; + + if (empty($currentRequestKey)) { + $currentRequestKey = substr(Common::generateUniqId(), 0, 5); + } + + $message = $this->next($message, $level, $tag, $datetime, $logger); + + if (! is_string($message)) { + return $message; + } + + // Decorate the error message with the "request id" + if (!defined('PIWIK_TEST_MODE')) { + $message = '[' . $currentRequestKey . '] ' . $message; + } + + return $message; + } +} diff --git a/core/Log/Formatter/ErrorHtmlFormatter.php b/core/Log/Formatter/ErrorHtmlFormatter.php index 339798d7e4..3af11282cd 100644 --- a/core/Log/Formatter/ErrorHtmlFormatter.php +++ b/core/Log/Formatter/ErrorHtmlFormatter.php @@ -16,12 +16,12 @@ use Piwik\Version; /** * Formats a log message containing a Piwik\Error object into an HTML string. */ -class ErrorHtmlFormatter implements Formatter +class ErrorHtmlFormatter extends Formatter { public function format($message, $level, $tag, $datetime, Log $logger) { if (! $message instanceof Error) { - return $message; + return $this->next($message, $level, $tag, $datetime, $logger); } $errno = $message->errno & error_reporting(); diff --git a/core/Log/Formatter/ErrorTextFormatter.php b/core/Log/Formatter/ErrorTextFormatter.php index 9ba2393e10..ba9b18d51a 100644 --- a/core/Log/Formatter/ErrorTextFormatter.php +++ b/core/Log/Formatter/ErrorTextFormatter.php @@ -14,12 +14,12 @@ use Piwik\Log; /** * Formats a log message containing a Piwik\Error object into a textual string. */ -class ErrorTextFormatter implements Formatter +class ErrorTextFormatter extends Formatter { public function format($message, $level, $tag, $datetime, Log $logger) { if (! $message instanceof Error) { - return $message; + return $this->next($message, $level, $tag, $datetime, $logger); } $message = $message->errfile . '(' . $message->errline . '): ' . Error::getErrNoString($message->errno) diff --git a/core/Log/Formatter/ExceptionHtmlFormatter.php b/core/Log/Formatter/ExceptionHtmlFormatter.php index 1a7a37db4c..309dbafe7f 100644 --- a/core/Log/Formatter/ExceptionHtmlFormatter.php +++ b/core/Log/Formatter/ExceptionHtmlFormatter.php @@ -15,12 +15,12 @@ use Piwik\Log; /** * Formats a log message containing an exception object into an HTML response. */ -class ExceptionHtmlFormatter implements Formatter +class ExceptionHtmlFormatter extends Formatter { public function format($message, $level, $tag, $datetime, Log $logger) { if (! $message instanceof \Exception) { - return $message; + return $this->next($message, $level, $tag, $datetime, $logger); } Common::sendHeader('Content-Type: text/html; charset=utf-8'); diff --git a/core/Log/Formatter/ExceptionTextFormatter.php b/core/Log/Formatter/ExceptionTextFormatter.php index 43edf1820d..24ab88c270 100644 --- a/core/Log/Formatter/ExceptionTextFormatter.php +++ b/core/Log/Formatter/ExceptionTextFormatter.php @@ -14,12 +14,12 @@ use Piwik\Log; /** * Formats a log message containing an exception object into a textual string. */ -class ExceptionTextFormatter implements Formatter +class ExceptionTextFormatter extends Formatter { public function format($message, $level, $tag, $datetime, Log $logger) { if (! $message instanceof \Exception) { - return $message; + return $this->next($message, $level, $tag, $datetime, $logger); } $message = sprintf("%s(%d): %s\n%s", $message->getFile(), $message->getLine(), $message->getMessage(), diff --git a/core/Log/Formatter/Formatter.php b/core/Log/Formatter/Formatter.php index d533ba13aa..47d501e247 100644 --- a/core/Log/Formatter/Formatter.php +++ b/core/Log/Formatter/Formatter.php @@ -12,10 +12,18 @@ use Piwik\Log; /** * Formats a log message. + * + * Follows the Chain of responsibility design pattern, so don't forget to call `$this->next(...)` + * at the end of the `format()` method. */ -interface Formatter +abstract class Formatter { /** + * @var Formatter|null + */ + protected $next; + + /** * @param mixed $message Log message. * @param int $level The log level used with this log entry. * @param string $tag The current plugin that started logging (or if no plugin, the current class). @@ -24,5 +32,24 @@ interface Formatter * * @return mixed */ - public function format($message, $level, $tag, $datetime, Log $logger); + public abstract function format($message, $level, $tag, $datetime, Log $logger); + + /** + * Chain of responsibility pattern. + * + * @param Formatter $formatter + */ + public function setNext(Formatter $formatter) + { + $this->next = $formatter; + } + + protected function next($message, $level, $tag, $datetime, Log $logger) + { + if (! $this->next) { + return $message; + } + + return $this->next->format($message, $level, $tag, $datetime, $logger); + } } diff --git a/core/Log/Formatter/HtmlPreFormatter.php b/core/Log/Formatter/HtmlPreFormatter.php new file mode 100644 index 0000000000..e8c095c047 --- /dev/null +++ b/core/Log/Formatter/HtmlPreFormatter.php @@ -0,0 +1,34 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Log\Formatter; + +use Piwik\Common; +use Piwik\Log; + +/** + * Formats the message into `<pre></pre>` HTML tags. + */ +class HtmlPreFormatter extends Formatter +{ + public function format($message, $level, $tag, $datetime, Log $logger) + { + $message = $this->next($message, $level, $tag, $datetime, $logger); + + if (! is_string($message)) { + return $message; + } + + if (!Common::isPhpCliMode()) { + $message = Common::sanitizeInputValue($message); + $message = '<pre>' . $message . '</pre>'; + } + + return $message; + } +} diff --git a/core/Log/Formatter/LineMessageFormatter.php b/core/Log/Formatter/LineMessageFormatter.php new file mode 100644 index 0000000000..87bc54dfa1 --- /dev/null +++ b/core/Log/Formatter/LineMessageFormatter.php @@ -0,0 +1,58 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Log\Formatter; + +use Piwik\Log; + +/** + * Formats a log message into a single line of text. + */ +class LineMessageFormatter extends Formatter +{ + /** + * The log message format string that turns a tag name, date-time and message into + * one string to log. + * + * @var string + */ + private $logMessageFormat; + + public function __construct($logMessageFormat) + { + $this->logMessageFormat = $logMessageFormat; + } + + public function format($message, $level, $tag, $datetime, Log $logger) + { + if (! is_string($message)) { + throw new \InvalidArgumentException('Trying to log a message that is not a string'); + } + + $message = str_replace( + array("%tag%", "%message%", "%datetime%", "%level%"), + array($tag, trim($message), $datetime, $this->getStringLevel($level)), + $this->logMessageFormat + ); + + return $this->next($message, $level, $tag, $datetime, $logger); + } + + private function getStringLevel($level) + { + static $levelToName = array( + Log::NONE => 'NONE', + Log::ERROR => 'ERROR', + Log::WARN => 'WARN', + Log::INFO => 'INFO', + Log::DEBUG => 'DEBUG', + Log::VERBOSE => 'VERBOSE' + ); + return $levelToName[$level]; + } +} diff --git a/core/Log/LoggerFactory.php b/core/Log/LoggerFactory.php index 3e48c63fdf..22ff4a0b99 100644 --- a/core/Log/LoggerFactory.php +++ b/core/Log/LoggerFactory.php @@ -14,8 +14,15 @@ use Piwik\Config; use Piwik\Log; use Piwik\Log\Backend\DatabaseBackend; use Piwik\Log\Backend\FileBackend; -use Piwik\Log\Backend\ScreenBackend; -use Piwik\Log\Processor\SprintfProcessor; +use Piwik\Log\Backend\StdOutBackend; +use Piwik\Log\Backend\StdErrBackend; +use Piwik\Log\Formatter\AddRequestIdFormatter; +use Piwik\Log\Formatter\ErrorHtmlFormatter; +use Piwik\Log\Formatter\ErrorTextFormatter; +use Piwik\Log\Formatter\ExceptionHtmlFormatter; +use Piwik\Log\Formatter\ExceptionTextFormatter; +use Piwik\Log\Formatter\HtmlPreFormatter; +use Piwik\Log\Formatter\LineMessageFormatter; use Piwik\Piwik; class LoggerFactory @@ -78,6 +85,10 @@ class LoggerFactory $writers[$writerName] = $availableWriters[$writerName]; } + // Always add the stderr backend + $isLoggingToStdOut = isset($writers['screen']); + $writers['stderr'] = new StdErrBackend(self::getScreenFormatter($messageFormat), $isLoggingToStdOut); + return $writers; } @@ -141,7 +152,7 @@ class LoggerFactory } } - private static function getAvailableWriters($logMessageFormat, $logFilePath) + private static function getAvailableWriters($messageFormat, $logFilePath) { $writers = array(); @@ -173,10 +184,49 @@ class LoggerFactory */ Piwik::postEvent(Log::GET_AVAILABLE_WRITERS_EVENT, array(&$writers)); - $writers['file'] = new FileBackend($logMessageFormat, $logFilePath); - $writers['screen'] = new ScreenBackend($logMessageFormat); - $writers['database'] = new DatabaseBackend($logMessageFormat); + $writers['file'] = new FileBackend(self::getFileAndDbFormatter($messageFormat), $logFilePath); + $writers['screen'] = new StdOutBackend(self::getScreenFormatter($messageFormat)); + $writers['database'] = new DatabaseBackend(self::getFileAndDbFormatter($messageFormat)); return $writers; } + + public static function createScreenBackend($messageFormat) + { + return new StdOutBackend(self::getScreenFormatter($messageFormat)); + } + + private static function getFileAndDbFormatter($messageFormat) + { + // Chain of responsibility pattern + $lineFormatter = new LineMessageFormatter($messageFormat); + + $exceptionFormatter = new ExceptionTextFormatter(); + $exceptionFormatter->setNext($lineFormatter); + + $errorFormatter = new ErrorTextFormatter(); + $errorFormatter->setNext($exceptionFormatter); + + return $errorFormatter; + } + + private static function getScreenFormatter($messageFormat) + { + // Chain of responsibility pattern + $lineFormatter = new LineMessageFormatter($messageFormat); + + $addRequestIdFormatter = new AddRequestIdFormatter(); + $addRequestIdFormatter->setNext($lineFormatter); + + $htmlPreFormatter = new HtmlPreFormatter(); + $htmlPreFormatter->setNext($addRequestIdFormatter); + + $exceptionFormatter = new ExceptionHtmlFormatter(); + $exceptionFormatter->setNext($htmlPreFormatter); + + $errorFormatter = new ErrorHtmlFormatter(); + $errorFormatter->setNext($exceptionFormatter); + + return $errorFormatter; + } } |