diff --git a/core/core.services.yml b/core/core.services.yml index fb62634..ba75d6d 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -147,6 +147,8 @@ services: factory_method: get factory_service: logger.factory arguments: ['system'] + logger.log_message_parser: + class: Drupal\Core\Logger\LogMessageParser serialization.json: class: Drupal\Component\Serialization\Json diff --git a/core/includes/common.inc b/core/includes/common.inc index 8e0d53e..6e08ce0 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -30,6 +30,7 @@ use Drupal\Core\Template\Attribute; use Drupal\Core\Render\Element; use Drupal\Core\Session\AnonymousUserSession; +use Psr\Log\LogLevel; /** * @defgroup php_wrappers PHP wrapper functions @@ -4099,14 +4100,14 @@ function element_set_attributes(array &$element, array $map) { */ function watchdog_severity_levels() { return array( - WATCHDOG_EMERGENCY => t('Emergency'), - WATCHDOG_ALERT => t('Alert'), - WATCHDOG_CRITICAL => t('Critical'), - WATCHDOG_ERROR => t('Error'), - WATCHDOG_WARNING => t('Warning'), - WATCHDOG_NOTICE => t('Notice'), - WATCHDOG_INFO => t('Info'), - WATCHDOG_DEBUG => t('Debug'), + LogLevel::EMERGENCY => t('Emergency'), + LogLevel::ALERT => t('Alert'), + LogLevel::CRITICAL => t('Critical'), + LogLevel::ERROR => t('Error'), + LogLevel::WARNING => t('Warning'), + LogLevel::NOTICE => t('Notice'), + LogLevel::INFO => t('Info'), + LogLevel::DEBUG => t('Debug'), ); } diff --git a/core/lib/Drupal/Core/Logger/LogMessageParser.php b/core/lib/Drupal/Core/Logger/LogMessageParser.php new file mode 100644 index 0000000..ba99129 --- /dev/null +++ b/core/lib/Drupal/Core/Logger/LogMessageParser.php @@ -0,0 +1,46 @@ + $start) { + $has_psr3 = TRUE; + // Transform PSR3 style messages containing placeholders to + // \Drupal\Component\Utility\String::format() style. + $message = preg_replace('/\{(.*)\}/U', '@$1', $message); + } + foreach ($context as $key => $variable) { + // PSR3 style placeholders. + if ($has_psr3) { + // Keys are not prefixed with anything according to PSR3 specs. + // If the message is "User {username} created" the variable key will be + // just "username". + if (strpos($message, '@' . $key) !== FALSE) { + $key = '@' . $key; + } + } + if (!empty($key) && ($key[0] === '@' || $key[0] === '%' || $key[0] === '!')) { + // The key is now in \Drupal\Component\Utility\String::format() style. + $variables[$key] = $variable; + } + } + + return $variables; + } + +} diff --git a/core/lib/Drupal/Core/Logger/LogMessageParserInterface.php b/core/lib/Drupal/Core/Logger/LogMessageParserInterface.php new file mode 100644 index 0000000..545b039 --- /dev/null +++ b/core/lib/Drupal/Core/Logger/LogMessageParserInterface.php @@ -0,0 +1,39 @@ + $start) { - $has_psr3 = TRUE; - // Transform PSR3 style messages containing placeholders to - // \Drupal\Component\Utility\String::format() style. - $message = preg_replace('/\{(.*)\}/U', '@$1', $message); - } - foreach ($context as $key => $variable) { - // PSR3 style placeholders. - if ($has_psr3) { - // Keys are not prefixed with anything according to PSR3 specs. - // If the message is "User {username} created" the variable key will be - // just "username". - if (strpos($message, '@' . $key) !== FALSE) { - $key = '@' . $key; - } - } - if (!empty($key) && ($key[0] === '@' || $key[0] === '%' || $key[0] === '!')) { - // The key is now in \Drupal\Component\Utility\String::format() style. - $variables[$key] = $variable; - } - } - $context['variables'] = $variables; - } - -} diff --git a/core/lib/Drupal/Core/Logger/LoggerChannel.php b/core/lib/Drupal/Core/Logger/LoggerChannel.php index 3cc64e7..86bf2cf 100644 --- a/core/lib/Drupal/Core/Logger/LoggerChannel.php +++ b/core/lib/Drupal/Core/Logger/LoggerChannel.php @@ -21,6 +21,8 @@ class LoggerChannel implements LoggerChannelInterface { /** * The name of the channel of this logger instance. + * + * @var string */ protected $channel; @@ -33,14 +35,14 @@ class LoggerChannel implements LoggerChannelInterface { * @var array */ protected $levelTranslation = array( - LogLevel::EMERGENCY => WATCHDOG_EMERGENCY, - LogLevel::ALERT => WATCHDOG_ALERT, - LogLevel::CRITICAL => WATCHDOG_CRITICAL, - LogLevel::ERROR => WATCHDOG_ERROR, - LogLevel::WARNING => WATCHDOG_WARNING, - LogLevel::NOTICE => WATCHDOG_NOTICE, - LogLevel::INFO => WATCHDOG_INFO, - LogLevel::DEBUG => WATCHDOG_DEBUG, + WATCHDOG_EMERGENCY => LogLevel::EMERGENCY, + WATCHDOG_ALERT => LogLevel::ALERT, + WATCHDOG_CRITICAL => LogLevel::CRITICAL, + WATCHDOG_ERROR => LogLevel::ERROR, + WATCHDOG_WARNING => LogLevel::WARNING, + WATCHDOG_NOTICE => LogLevel::NOTICE, + WATCHDOG_INFO => LogLevel::INFO, + WATCHDOG_DEBUG => LogLevel::DEBUG, ); /** @@ -100,8 +102,10 @@ public function log($level, $message, array $context = array()) { $context['ip'] = $this->request->getClientIP(); } - if (is_string($level)) { - // Convert to integer equivalent for BC. + if (!is_string($level)) { + // Convert integer levels (WATCHDOG_* constants) to the string equivalents + // of \Psr\Log\LogLevel. + // @todo Remove this when watchdog() is removed. $level = $this->levelTranslation[$level]; } // Call all available loggers. diff --git a/core/modules/dblog/dblog.install b/core/modules/dblog/dblog.install index b7454c1..a6ceaf0 100644 --- a/core/modules/dblog/dblog.install +++ b/core/modules/dblog/dblog.install @@ -44,12 +44,11 @@ function dblog_schema() { 'description' => 'Serialized array of variables that match the message string and that is passed into the t() function.', ), 'severity' => array( - 'type' => 'int', - 'unsigned' => TRUE, + 'type' => 'varchar', + 'length' => 64, 'not null' => TRUE, - 'default' => 0, - 'size' => 'tiny', - 'description' => 'The severity level of the event; ranges from 0 (Emergency) to 7 (Debug)', + 'default' => '', + 'description' => 'The severity level of the event. See \Psr\Log\LogLevel', ), 'link' => array( 'type' => 'varchar', diff --git a/core/modules/dblog/dblog.services.yml b/core/modules/dblog/dblog.services.yml index c07cb32..38836e1 100644 --- a/core/modules/dblog/dblog.services.yml +++ b/core/modules/dblog/dblog.services.yml @@ -1,6 +1,6 @@ services: logger.dblog: class: Drupal\dblog\Logger\DbLog - arguments: ['@database'] + arguments: ['@database', '@logger.log_message_parser'] tags: - { name: logger } diff --git a/core/modules/dblog/lib/Drupal/dblog/Controller/DbLogController.php b/core/modules/dblog/lib/Drupal/dblog/Controller/DbLogController.php index 72ddf22..08123b3 100644 --- a/core/modules/dblog/lib/Drupal/dblog/Controller/DbLogController.php +++ b/core/modules/dblog/lib/Drupal/dblog/Controller/DbLogController.php @@ -15,6 +15,7 @@ use Drupal\Core\Datetime\Date; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormBuilderInterface; +use Psr\Log\LogLevel; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -89,14 +90,14 @@ public function __construct(Connection $database, ModuleHandlerInterface $module */ public static function getLogLevelClassMap() { return array( - WATCHDOG_DEBUG => 'dblog-debug', - WATCHDOG_INFO => 'dblog-info', - WATCHDOG_NOTICE => 'dblog-notice', - WATCHDOG_WARNING => 'dblog-warning', - WATCHDOG_ERROR => 'dblog-error', - WATCHDOG_CRITICAL => 'dblog-critical', - WATCHDOG_ALERT => 'dblog-alert', - WATCHDOG_EMERGENCY => 'dblog-emergency', + LogLevel::DEBUG => 'dblog-debug', + LogLevel::INFO => 'dblog-info', + LogLevel::NOTICE => 'dblog-notice', + LogLevel::WARNING => 'dblog-warning', + LogLevel::ERROR => 'dblog-error', + LogLevel::CRITICAL => 'dblog-critical', + LogLevel::ALERT => 'dblog-alert', + LogLevel::EMERGENCY => 'dblog-emergency', ); } diff --git a/core/modules/dblog/lib/Drupal/dblog/Logger/DbLog.php b/core/modules/dblog/lib/Drupal/dblog/Logger/DbLog.php index 80d7136..14916b3 100644 --- a/core/modules/dblog/lib/Drupal/dblog/Logger/DbLog.php +++ b/core/modules/dblog/lib/Drupal/dblog/Logger/DbLog.php @@ -8,7 +8,7 @@ namespace Drupal\dblog\Logger; use Drupal\Core\Database\Connection; -use Drupal\Core\Logger\LogMessageParserTrait; +use Drupal\Core\Logger\LogMessageParserInterface; use Psr\Log\LoggerInterface; use Psr\Log\LoggerTrait; @@ -17,7 +17,6 @@ */ class DbLog implements LoggerInterface { use LoggerTrait; - use LogMessageParserTrait; /** * The database connection object. @@ -27,13 +26,23 @@ class DbLog implements LoggerInterface { protected $database; /** + * The message's placeholders parser. + * + * @var \Drupal\Core\Logger\LogMessageParserInterface + */ + protected $parser; + + /** * Constructs a DbLog object. * * @param \Drupal\Core\Database\Connection $database * The database connection object. + * @param \Drupal\Core\Logger\LogMessageParserInterface $parser + * The parser to use when extracting message variables. */ - public function __construct(Connection $database) { + public function __construct(Connection $database, LogMessageParserInterface $parser) { $this->database = $database; + $this->parser = $parser; } /** @@ -45,7 +54,7 @@ public function log($level, $message, array $context = array()) { // Convert PSR3-style messages to String::format() style, so they can be // translated too in runtime. - $this->parseMessagePlaceholders($message, $context); + $message_placeholders = $this->parser->parseMessagePlaceholders($message, $context); $this->database ->insert('watchdog') @@ -53,7 +62,7 @@ public function log($level, $message, array $context = array()) { 'uid' => $context['uid'], 'type' => substr($context['channel'], 0, 64), 'message' => $message, - 'variables' => serialize($context['variables']), + 'variables' => serialize($message_placeholders), 'severity' => $level, 'link' => substr($context['link'], 0, 255), 'location' => $context['request_uri'], diff --git a/core/modules/dblog/lib/Drupal/dblog/Tests/DbLogTest.php b/core/modules/dblog/lib/Drupal/dblog/Tests/DbLogTest.php index 2ac6c53..a16625b 100644 --- a/core/modules/dblog/lib/Drupal/dblog/Tests/DbLogTest.php +++ b/core/modules/dblog/lib/Drupal/dblog/Tests/DbLogTest.php @@ -11,6 +11,7 @@ use Drupal\Core\Language\Language; use Drupal\dblog\Controller\DbLogController; use Drupal\simpletest\WebTestBase; +use Psr\Log\LogLevel; /** * Tests logging messages to the database. @@ -123,9 +124,10 @@ private function verifyCron($row_limit) { * @param string $type * (optional) The type of watchdog entry. Defaults to 'custom'. * @param int $severity - * (optional) The severity of the watchdog entry. Defaults to WATCHDOG_NOTICE. + * (optional) The severity of the watchdog entry. + * Defaults to \Psr\Log\LogLevel::NOTICE. */ - private function generateLogEntries($count, $type = 'custom', $severity = WATCHDOG_NOTICE) { + private function generateLogEntries($count, $type = 'custom', $severity = LogLevel::NOTICE) { global $base_root; // Prepare the fields to be logged @@ -416,7 +418,7 @@ protected function testDBLogAddAndClear() { 'channel' => 'system', 'message' => 'Log entry added to test the doClearTest clear down.', 'variables' => array(), - 'severity' => WATCHDOG_NOTICE, + 'severity' => LogLevel::NOTICE, 'link' => NULL, 'user' => $this->big_user, 'uid' => $this->big_user->id(), @@ -452,12 +454,15 @@ protected function testFilter() { $types = array(); for ($i = 0; $i < 3; $i++) { $type_names[] = $type_name = $this->randomName(); - $severity = WATCHDOG_EMERGENCY; for ($j = 0; $j < 3; $j++) { + $severity = LogLevel::EMERGENCY; + if ($j) { + $severity = $j === 1 ? LogLevel::ALERT : LogLevel::CRITICAL; + } $types[] = $type = array( 'count' => $j + 1, 'type' => $type_name, - 'severity' => $severity++, + 'severity' => $severity, ); $this->generateLogEntries($type['count'], $type['type'], $type['severity']); } @@ -531,7 +536,7 @@ protected function getLogEntries() { foreach ($table->tbody->tr as $row) { $entries[] = array( 'severity' => $this->getSeverityConstant($row['class']), - 'channel' => $this->asText($row->td[1]), + 'type' => $this->asText($row->td[1]), 'message' => $this->asText($row->td[3]), 'user' => $this->asText($row->td[4]), ); @@ -554,7 +559,7 @@ protected function getTypeCount(array $types) { $count = array_fill(0, count($types), 0); foreach ($entries as $entry) { foreach ($types as $key => $type) { - if ($entry['channel'] == $type['type'] && $entry['severity'] == $type['severity']) { + if ($entry['type'] == $type['type'] && $entry['severity'] == $type['severity']) { $count[$key]++; break; } diff --git a/core/modules/syslog/lib/Drupal/syslog/Logger/SysLog.php b/core/modules/syslog/lib/Drupal/syslog/Logger/SysLog.php index 17dd040..7b3ecd0 100644 --- a/core/modules/syslog/lib/Drupal/syslog/Logger/SysLog.php +++ b/core/modules/syslog/lib/Drupal/syslog/Logger/SysLog.php @@ -8,16 +8,16 @@ namespace Drupal\syslog\Logger; use Drupal\Core\Config\ConfigFactory; -use Drupal\Core\Logger\LogMessageParserTrait; +use Drupal\Core\Logger\LogMessageParserInterface; use Psr\Log\LoggerInterface; use Psr\Log\LoggerTrait; +use Psr\Log\LogLevel; /** * Redirects logging messages to syslog. */ class SysLog implements LoggerInterface { use LoggerTrait; - use LogMessageParserTrait; /** * A configuration object containin syslog settings. @@ -27,6 +27,13 @@ class SysLog implements LoggerInterface { protected $config; /** + * The message's placeholders parser. + * + * @var \Drupal\Core\Logger\LogMessageParserInterface + */ + protected $parser; + + /** * Stores whether there is a system logger connection opened or not. * * @var bool @@ -34,13 +41,35 @@ class SysLog implements LoggerInterface { protected $connectionOpened = FALSE; /** + * Map of PSR Log constants to Watchdog log constants. + * + * @todo Move watchdog constants to syslog's module namespace, after + * watchdog() is removed. + * + * @var array + */ + protected $levelTranslation = array( + LogLevel::EMERGENCY => WATCHDOG_EMERGENCY, + LogLevel::ALERT => WATCHDOG_ALERT, + LogLevel::CRITICAL => WATCHDOG_CRITICAL, + LogLevel::ERROR => WATCHDOG_ERROR, + LogLevel::WARNING => WATCHDOG_WARNING, + LogLevel::NOTICE => WATCHDOG_NOTICE, + LogLevel::INFO => WATCHDOG_INFO, + LogLevel::DEBUG => WATCHDOG_DEBUG, + ); + + /** * Constructs a SysLog object. * * @param \Drupal\Core\Config\ConfigFactory $config_factory * The configuration factory object. + * @param \Drupal\Core\Logger\LogMessageParserInterface $parser + * The parser to use when extracting message variables. */ - public function __construct(ConfigFactory $config_factory) { + public function __construct(ConfigFactory $config_factory, LogMessageParserInterface $parser) { $this->config = $config_factory->get('syslog.settings'); + $this->parser = $parser; } /** @@ -65,8 +94,9 @@ public function log($level, $message, array $context = array()) { // Ensure we have a connection available. $this->openConnection(); - // Populate the $context['variables'] with the message placeholders. - $this->parseMessagePlaceholders($message, $context); + // Populate the message placeholders and then replace them in the message. + $message_placeholders = $this->parser->parseMessagePlaceholders($message, $context); + $message = empty($message_placeholders) ? $message : strtr($message, $message_placeholders); $entry = strtr($this->config->get('format'), array( '!base_url' => $base_url, @@ -77,10 +107,10 @@ public function log($level, $message, array $context = array()) { '!referer' => $context['referer'], '!uid' => $context['uid'], '!link' => strip_tags($context['link']), - '!message' => strip_tags(empty($context['variables']) ? $message : strtr($message, $context['variables'])), + '!message' => strip_tags($message), )); - syslog($level, $entry); + syslog($this->levelTranslation[$level], $entry); } } diff --git a/core/modules/syslog/syslog.services.yml b/core/modules/syslog/syslog.services.yml index 98f6041..78c7d38 100644 --- a/core/modules/syslog/syslog.services.yml +++ b/core/modules/syslog/syslog.services.yml @@ -1,6 +1,6 @@ services: logger.syslog: class: Drupal\syslog\Logger\SysLog - arguments: ['@config.factory'] + arguments: ['@config.factory', '@logger.log_message_parser'] tags: - { name: logger } diff --git a/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTraitTest.php b/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php similarity index 79% rename from core/tests/Drupal/Tests/Core/Logger/LogMessageParserTraitTest.php rename to core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php index a7b81cf..e30f551 100644 --- a/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTraitTest.php +++ b/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php @@ -7,23 +7,24 @@ namespace Drupal\Tests\Core\Logger; +use Drupal\Core\Logger\LogMessageParser; use Drupal\Tests\UnitTestCase; /** - * Tests the log message parser trait. + * Tests the log message parser. * - * @see \Drupal\Core\Logger\LogMessageParserTrait - * @coversDefaultClass \Drupal\Core\Logger\LogMessageParserTrait + * @see \Drupal\Core\Logger\LogMessageParser + * @coversDefaultClass \Drupal\Core\Logger\LogMessageParser * * @group Drupal * @group Logger */ -class LogMessageParserTraitTest extends UnitTestCase { +class LogMessageParserTest extends UnitTestCase { public static function getInfo() { return array( 'name' => 'Log message parser', - 'description' => 'Unit tests for the log message parser trait.', + 'description' => 'Unit tests for the log message parser.', 'group' => 'Logger', ); } @@ -32,7 +33,7 @@ public static function getInfo() { * {@inheritdoc} */ protected function setUp() { - $this->logger = $this->getObjectForTrait('Drupal\Core\Logger\LogMessageParserTrait'); + $this->parser = new LogMessageParser(); } /** @@ -51,12 +52,9 @@ protected function setUp() { * @covers ::parseMessagePlaceholders */ public function testParseMessagePlaceholders(array $value, array $expected) { - $class = new \ReflectionClass($this->logger); - $method = $class->getMethod('parseMessagePlaceholders'); - $method->setAccessible(TRUE); - $method->invokeArgs($this->logger, array(&$value['message'], &$value['context'])); + $message_placeholders = $this->parser->parseMessagePlaceholders($value['message'], $value['context']); $this->assertEquals($expected['message'], $value['message']); - $this->assertEquals($expected['context'], $value['context']['variables']); + $this->assertEquals($expected['context'], $message_placeholders); } /** diff --git a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelFactoryTest.php b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelFactoryTest.php index 9c29831..82e20b2 100644 --- a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelFactoryTest.php +++ b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelFactoryTest.php @@ -18,10 +18,13 @@ define('WATCHDOG_ALERT', 1); define('WATCHDOG_CRITICAL', 2); define('WATCHDOG_WARNING', 4); - define('WATCHDOG_NOTICE', 5); define('WATCHDOG_INFO', 6); define('WATCHDOG_DEBUG', 7); } +// WATCHDOG_NOTICE is also defined in FormValidatorTest, so check independently. +if (!defined('WATCHDOG_NOTICE')) { + define('WATCHDOG_NOTICE', 5); +} // WATCHDOG_ERROR is also defined in FormBuilderTest, so check independently. if (!defined('WATCHDOG_ERROR')) { define('WATCHDOG_ERROR', 3); diff --git a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php index c62cf41..2cc0a50 100644 --- a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php +++ b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php @@ -18,10 +18,13 @@ define('WATCHDOG_ALERT', 1); define('WATCHDOG_CRITICAL', 2); define('WATCHDOG_WARNING', 4); - define('WATCHDOG_NOTICE', 5); define('WATCHDOG_INFO', 6); define('WATCHDOG_DEBUG', 7); } +// WATCHDOG_NOTICE is also defined in FormValidatorTest, so check independently. +if (!defined('WATCHDOG_NOTICE')) { + define('WATCHDOG_NOTICE', 5); +} // WATCHDOG_ERROR is also defined in FormBuilderTest, so check independently. if (!defined('WATCHDOG_ERROR')) { define('WATCHDOG_ERROR', 3);