diff --git a/core/authorize.php b/core/authorize.php index f2da377aa8e7bff8a6afdaf43309bc5ad288be3f..f938f3903324c6ad706d1bfa61e09ab7819af82d 100644 --- a/core/authorize.php +++ b/core/authorize.php @@ -147,7 +147,7 @@ function authorize_access_allowed() { } else { drupal_add_http_header('Status', '403 Forbidden'); - watchdog('access denied', 'authorize.php', NULL, WATCHDOG_WARNING); + watchdog('access denied', 'authorize.php', array(), WATCHDOG_WARNING); $page_title = t('Access denied'); $output = t('You are not allowed to access this page.'); } diff --git a/core/core.services.yml b/core/core.services.yml index e15618ff1ba6c7f2c38505f2373ca459a399effc..66b3d9bc786cf58619ef001c510573a43bcf4c2d 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -142,6 +142,18 @@ services: arguments: ['@serialization.phpserialize', '@database'] tags: - { name: needs_destruction } + logger.factory: + class: Drupal\Core\Logger\LoggerChannelFactory + parent: container.trait + tags: + - { name: service_collector, tag: logger, call: addLogger } + logger.channel.default: + class: Drupal\Core\Logger\LoggerChannel + 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 @@ -258,6 +270,7 @@ services: arguments: ['@service_container'] controller_resolver: class: Drupal\Core\Controller\ControllerResolver + arguments: ['@logger.channel.default'] parent: container.trait title_resolver: class: Drupal\Core\Controller\TitleResolver @@ -330,7 +343,7 @@ services: - { name: service_collector, tag: route_filter, call: addRouteFilter } url_generator: class: Drupal\Core\Routing\UrlGenerator - arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@config.factory', '@settings'] + arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@config.factory', '@settings', '@logger.channel.default'] calls: - [setRequest, ['@?request']] - [setContext, ['@?router.request_context']] diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index d33d1ee5596472f227108e5a740c7333cdd592f0..ac209926e3f1d4eb2d1e8803eead9cc4e1527944 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -1137,58 +1137,18 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia * @param $link * A link to associate with the message. * + * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0. + * Use \Drupal::logger($channel)->log($severity, $message, $context), or any + * of the shortcut methods of \Psr\Log\LoggerTrait. + * * @see watchdog_severity_levels() * @see hook_watchdog() */ -function watchdog($type, $message, array $variables = NULL, $severity = WATCHDOG_NOTICE, $link = NULL) { - global $user, $base_root; - - static $in_error_state = FALSE; - - // It is possible that the error handling will itself trigger an error. In that case, we could - // end up in an infinite loop. To avoid that, we implement a simple static semaphore. - if (!$in_error_state && \Drupal::hasService('module_handler')) { - $in_error_state = TRUE; - - // The user object may not exist in all conditions, so 0 is substituted if needed. - $user_uid = isset($user) ? $user->id() : 0; - - // Prepare the fields to be logged - $log_entry = array( - 'type' => $type, - 'message' => $message, - 'variables' => $variables, - 'severity' => $severity, - 'link' => $link, - 'user' => $user, - 'uid' => $user_uid, - 'request_uri' => '', - 'referer' => '', - 'ip' => '', - // Request time isn't accurate for long processes, use time() instead. - 'timestamp' => time(), - ); - - try { - $request = \Drupal::request(); - $log_entry['request_uri'] = $request->getUri(); - $log_entry['referer'] = $request->headers->get('Referer', ''); - $log_entry['ip'] = $request->getClientIP(); - } - catch (DependencyInjectionRuntimeException $e) { - // We are not in a request context. - } - - // Call the logging hooks to log/process the message - foreach (\Drupal::moduleHandler()->getImplementations('watchdog') as $module) { - $function = $module . '_watchdog'; - $function($log_entry); - } - - // It is critical that the semaphore is only cleared here, in the parent - // watchdog() call (not outside the loop), to prevent recursive execution. - $in_error_state = FALSE; +function watchdog($type, $message, array $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) { + if ($link) { + $variables['link'] = $link; } + \Drupal::service('logger.factory')->get($type)->log($severity, $message, $variables); } /** diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php index 5c724354fb1c62dc68a0f3fa62c2b191f13266f6..83fe15fa49b3313ddb17108e9d2461374db1e1f2 100644 --- a/core/lib/Drupal.php +++ b/core/lib/Drupal.php @@ -622,4 +622,18 @@ public static function isConfigSyncing() { return static::$container->get('config.installer')->isSyncing(); } + /** + * Returns a channel logger object. + * + * @param string $channel + * The name of the channel. Can be any string, but the general practice is + * to use the name of the subsystem calling this. + * + * @return \Drupal\Core\Logger\LoggerChannelInterface + * The logger for this channel. + */ + public static function logger($channel) { + return static::$container->get('logger.factory')->get($channel); + } + } diff --git a/core/lib/Drupal/Core/Controller/ControllerResolver.php b/core/lib/Drupal/Core/Controller/ControllerResolver.php index c37ed834d938575c467b8dbd22db20dfd31a09b0..c80242c87f74194d6a3b4261e5e8a847ef350312 100644 --- a/core/lib/Drupal/Core/Controller/ControllerResolver.php +++ b/core/lib/Drupal/Core/Controller/ControllerResolver.php @@ -7,9 +7,9 @@ namespace Drupal\Core\Controller; +use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ControllerResolver as BaseControllerResolver; -use Symfony\Component\HttpKernel\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; @@ -42,7 +42,7 @@ class ControllerResolver extends BaseControllerResolver implements ControllerRes /** * Constructs a new ControllerResolver. * - * @param \Symfony\Component\HttpKernel\Log\LoggerInterface $logger + * @param \Psr\Log\LoggerInterface $logger * (optional) A LoggerInterface instance. */ public function __construct(LoggerInterface $logger = NULL) { diff --git a/core/lib/Drupal/Core/Controller/ExceptionController.php b/core/lib/Drupal/Core/Controller/ExceptionController.php index f95930b3074f0324699f577509bdeaffe4b4dc0a..320cb064be0d5cd8df6efb3eb09c8f4705038148 100644 --- a/core/lib/Drupal/Core/Controller/ExceptionController.php +++ b/core/lib/Drupal/Core/Controller/ExceptionController.php @@ -143,7 +143,7 @@ public function on405Html(FlattenException $exception, Request $request) { */ public function on403Html(FlattenException $exception, Request $request) { $system_path = $request->attributes->get('_system_path'); - watchdog('access denied', $system_path, NULL, WATCHDOG_WARNING); + watchdog('access denied', $system_path, array(), WATCHDOG_WARNING); $system_config = $this->container->get('config.factory')->get('system.site'); $path = $this->container->get('path.alias_manager')->getPathByAlias($system_config->get('page.403')); @@ -185,7 +185,7 @@ public function on403Html(FlattenException $exception, Request $request) { * A response object. */ public function on404Html(FlattenException $exception, Request $request) { - watchdog('page not found', String::checkPlain($request->attributes->get('_system_path')), NULL, WATCHDOG_WARNING); + watchdog('page not found', String::checkPlain($request->attributes->get('_system_path')), array(), WATCHDOG_WARNING); // Check for and return a fast 404 page if configured. $config = \Drupal::config('system.performance'); diff --git a/core/lib/Drupal/Core/Form/FormValidator.php b/core/lib/Drupal/Core/Form/FormValidator.php index 9e73738f14b13dc36e9fec80aa00c80db12711db..eda8238a47ea3966e0351988ea6831894f3429a3 100644 --- a/core/lib/Drupal/Core/Form/FormValidator.php +++ b/core/lib/Drupal/Core/Form/FormValidator.php @@ -502,7 +502,7 @@ public function getAnyErrors() { /** * Wraps watchdog(). */ - protected function watchdog($type, $message, array $variables = NULL, $severity = WATCHDOG_NOTICE, $link = NULL) { + protected function watchdog($type, $message, array $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) { watchdog($type, $message, $variables, $severity, $link); } diff --git a/core/lib/Drupal/Core/Logger/LogMessageParser.php b/core/lib/Drupal/Core/Logger/LogMessageParser.php new file mode 100644 index 0000000000000000000000000000000000000000..ba991299eccd7bf1f423b4117d376a6e48a7bd88 --- /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 0000000000000000000000000000000000000000..545b03984371cc050fd3b4e43582f45b83686297 --- /dev/null +++ b/core/lib/Drupal/Core/Logger/LogMessageParserInterface.php @@ -0,0 +1,39 @@ + 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, + ); + + /** + * An array of arrays of \Psr\Log\LoggerInterface keyed by priority. + * + * @var array + */ + protected $loggers = array(); + + /** + * The request object. + * + * @var \Symfony\Component\HttpFoundation\Request + */ + protected $request; + + /** + * The current user object. + * + * @var \Drupal\Core\Session\AccountInterface + */ + protected $currentUser; + + /** + * Constructs a LoggerChannel object + * + * @param string $channel + * The channel name for this instance. + */ + public function __construct($channel) { + $this->channel = $channel; + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = array()) { + // Merge in defaults. + $context += array( + 'channel' => $this->channel, + 'link' => '', + 'user' => NULL, + 'uid' => 0, + 'request_uri' => '', + 'referer' => '', + 'ip' => '', + 'timestamp' => time(), + ); + if ($this->currentUser) { + $context['user'] = $this->currentUser; + $context['uid'] = $this->currentUser->id(); + } + // Some context values are only available when in a request context. + if ($this->request) { + $context['request_uri'] = $this->request->getUri(); + $context['referer'] = $this->request->headers->get('Referer', ''); + $context['ip'] = $this->request->getClientIP(); + } + + if (is_string($level)) { + // Convert to integer equivalent for consistency with RFC 3164. + $level = $this->levelTranslation[$level]; + } + // Call all available loggers. + foreach ($this->sortLoggers() as $logger) { + $logger->log($level, $message, $context); + } + } + + /** + * {@inheritdoc} + */ + public function setRequest(Request $request = NULL) { + $this->request = $request; + } + + /** + * {@inheritdoc} + */ + public function setCurrentUser(AccountInterface $current_user = NULL) { + $this->currentUser = $current_user; + } + + /** + * {@inheritdoc} + */ + public function setLoggers(array $loggers) { + $this->loggers = $loggers; + } + + /** + * {@inheritdoc} + */ + public function addLogger(LoggerInterface $logger, $priority = 0) { + $this->loggers[$priority][] = $logger; + } + + /** + * Sorts loggers according to priority. + * + * @return array + * An array of sorted loggers by priority. + */ + protected function sortLoggers() { + $sorted = array(); + krsort($this->loggers); + + foreach ($this->loggers as $loggers) { + $sorted = array_merge($sorted, $loggers); + } + return $sorted; + } + +} diff --git a/core/lib/Drupal/Core/Logger/LoggerChannelFactory.php b/core/lib/Drupal/Core/Logger/LoggerChannelFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..1ec171002d5c01dd606bbb671ba3d28edcac403f --- /dev/null +++ b/core/lib/Drupal/Core/Logger/LoggerChannelFactory.php @@ -0,0 +1,74 @@ +channels[$channel])) { + $instance = new LoggerChannel($channel); + + // If the service container is set and request is available, set it with + // the current user to the channel. + if ($this->container) { + try { + $instance->setRequest($this->container->get('request')); + $instance->setCurrentUser($this->container->get('current_user')); + } + catch (RuntimeException $e) { + // We are not in a request context. + } + } + + // Pass the loggers to the channel. + $instance->setLoggers($this->loggers); + $this->channels[$channel] = $instance; + } + + return $this->channels[$channel]; + } + + /** + * {@inheritdoc} + */ + public function addLogger(LoggerInterface $logger, $priority = 0) { + // Store it so we can pass it to potential new logger instances. + $this->loggers[$priority][] = $logger; + // Add the logger to already instantiated channels. + foreach ($this->channels as $channel) { + $channel->addLogger($logger, $priority); + } + } + +} diff --git a/core/lib/Drupal/Core/Logger/LoggerChannelFactoryInterface.php b/core/lib/Drupal/Core/Logger/LoggerChannelFactoryInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..67d178ccb1ea25246f8784868b4e3cb5cb977019 --- /dev/null +++ b/core/lib/Drupal/Core/Logger/LoggerChannelFactoryInterface.php @@ -0,0 +1,40 @@ +translate('The configuration was imported with errors.'), 'warning'); } diff --git a/core/modules/dblog/dblog.module b/core/modules/dblog/dblog.module index 56ce643b195026a6b2526a3a5459706adbe8d93c..1eb61b015453bbffd02df327b13decec19cf5690 100644 --- a/core/modules/dblog/dblog.module +++ b/core/modules/dblog/dblog.module @@ -11,7 +11,6 @@ * @see watchdog() */ -use Drupal\Core\Database\Database; use Symfony\Component\HttpFoundation\Request; /** @@ -107,31 +106,6 @@ function _dblog_get_message_types() { return array_combine($types, $types); } -/** - * Implements hook_watchdog(). - * - * Note: Some values may be truncated to meet database column size restrictions. - */ -function dblog_watchdog(array $log_entry) { - // Remove any backtraces since they may contain an unserializable variable. - unset($log_entry['variables']['backtrace']); - - Database::getConnection('default', 'default')->insert('watchdog') - ->fields(array( - 'uid' => $log_entry['uid'], - 'type' => substr($log_entry['type'], 0, 64), - 'message' => $log_entry['message'], - 'variables' => serialize($log_entry['variables']), - 'severity' => $log_entry['severity'], - 'link' => substr($log_entry['link'], 0, 255), - 'location' => $log_entry['request_uri'], - 'referer' => $log_entry['referer'], - 'hostname' => substr($log_entry['ip'], 0, 128), - 'timestamp' => $log_entry['timestamp'], - )) - ->execute(); -} - /** * Implements hook_form_FORM_ID_alter() for system_logging_settings(). */ diff --git a/core/modules/dblog/dblog.services.yml b/core/modules/dblog/dblog.services.yml new file mode 100644 index 0000000000000000000000000000000000000000..38836e16338f1b47fb9660482a09e39da577c6e9 --- /dev/null +++ b/core/modules/dblog/dblog.services.yml @@ -0,0 +1,6 @@ +services: + logger.dblog: + class: Drupal\dblog\Logger\DbLog + arguments: ['@database', '@logger.log_message_parser'] + tags: + - { name: logger } diff --git a/core/modules/dblog/lib/Drupal/dblog/Logger/DbLog.php b/core/modules/dblog/lib/Drupal/dblog/Logger/DbLog.php new file mode 100644 index 0000000000000000000000000000000000000000..14916b351cebf09631634bc03a9834ca596a2f81 --- /dev/null +++ b/core/modules/dblog/lib/Drupal/dblog/Logger/DbLog.php @@ -0,0 +1,76 @@ +database = $database; + $this->parser = $parser; + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = array()) { + // Remove any backtraces since they may contain an unserializable variable. + unset($context['backtrace']); + + // Convert PSR3-style messages to String::format() style, so they can be + // translated too in runtime. + $message_placeholders = $this->parser->parseMessagePlaceholders($message, $context); + + $this->database + ->insert('watchdog') + ->fields(array( + 'uid' => $context['uid'], + 'type' => substr($context['channel'], 0, 64), + 'message' => $message, + 'variables' => serialize($message_placeholders), + 'severity' => $level, + 'link' => substr($context['link'], 0, 255), + 'location' => $context['request_uri'], + 'referer' => $context['referer'], + 'hostname' => substr($context['ip'], 0, 128), + 'timestamp' => $context['timestamp'], + )) + ->execute(); + } + +} diff --git a/core/modules/dblog/lib/Drupal/dblog/Tests/DbLogTest.php b/core/modules/dblog/lib/Drupal/dblog/Tests/DbLogTest.php index 5b93c34a01ec88c6eeddd4d4e760caaef88cf489..84c05cfd7c59d0c09e1863b6ec399c26287bd20c 100644 --- a/core/modules/dblog/lib/Drupal/dblog/Tests/DbLogTest.php +++ b/core/modules/dblog/lib/Drupal/dblog/Tests/DbLogTest.php @@ -130,7 +130,7 @@ private function generateLogEntries($count, $type = 'custom', $severity = WATCHD // Prepare the fields to be logged $log = array( - 'type' => $type, + 'channel' => $type, 'message' => 'Log entry added to test the dblog row limit.', 'variables' => array(), 'severity' => $severity, @@ -145,7 +145,7 @@ private function generateLogEntries($count, $type = 'custom', $severity = WATCHD $message = 'Log entry added to test the dblog row limit. Entry #'; for ($i = 0; $i < $count; $i++) { $log['message'] = $message . $i; - dblog_watchdog($log); + $this->container->get('logger.dblog')->log($severity, $log['message'], $log); } } @@ -413,7 +413,7 @@ protected function testDBLogAddAndClear() { // Get a count of how many watchdog entries already exist. $count = db_query('SELECT COUNT(*) FROM {watchdog}')->fetchField(); $log = array( - 'type' => 'custom', + 'channel' => 'system', 'message' => 'Log entry added to test the doClearTest clear down.', 'variables' => array(), 'severity' => WATCHDOG_NOTICE, @@ -426,7 +426,7 @@ protected function testDBLogAddAndClear() { 'timestamp' => REQUEST_TIME, ); // Add a watchdog entry. - dblog_watchdog($log); + $this->container->get('logger.dblog')->log($log['severity'], $log['message'], $log); // Make sure the table count has actually been incremented. $this->assertEqual($count + 1, db_query('SELECT COUNT(*) FROM {watchdog}')->fetchField(), format_string('dblog_watchdog() added an entry to the dblog :count', array(':count' => $count))); // Login the admin user. diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterSettingsTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterSettingsTest.php index a11db5dad937891741bd954956aa964c1c4102c9..f37d48b9f934480fcbac9ff1e9f1d4d61902d400 100644 --- a/core/modules/filter/lib/Drupal/filter/Tests/FilterSettingsTest.php +++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterSettingsTest.php @@ -34,13 +34,11 @@ public static function getInfo() { */ function testFilterDefaults() { $filter_info = $this->container->get('plugin.manager.filter')->getDefinitions(); - $filters = array_fill_keys(array_keys($filter_info), array()); // Create text format using filter default settings. $filter_defaults_format = entity_create('filter_format', array( 'format' => 'filter_defaults', 'name' => 'Filter defaults', - 'filters' => $filters, )); $filter_defaults_format->save(); diff --git a/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php b/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php index b4f1779fd8e08156d327652e51ee08a5daf2fd31..ca3806e44fd472a9ed44937fa5aea981273dd2c1 100644 --- a/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php +++ b/core/modules/rest/lib/Drupal/rest/Tests/DBLogTest.php @@ -41,9 +41,9 @@ public function setUp() { */ public function testWatchdog() { // Write a log message to the DB. - watchdog('rest_test', 'Test message'); + watchdog('rest', 'Test message'); // Get the ID of the written message. - $id = db_query_range("SELECT wid FROM {watchdog} WHERE type = :type ORDER BY wid DESC", 0, 1, array(':type' => 'rest_test')) + $id = db_query_range("SELECT wid FROM {watchdog} WHERE type = :type ORDER BY wid DESC", 0, 1, array(':type' => 'rest')) ->fetchField(); // Create a user account that has the required permissions to read @@ -56,7 +56,7 @@ public function testWatchdog() { $this->assertHeader('content-type', $this->defaultMimeType); $log = Json::decode($response); $this->assertEqual($log['wid'], $id, 'Log ID is correct.'); - $this->assertEqual($log['type'], 'rest_test', 'Type of log message is correct.'); + $this->assertEqual($log['type'], 'rest', 'Type of log message is correct.'); $this->assertEqual($log['message'], 'Test message', 'Log message text is correct.'); // Request an unknown log entry. diff --git a/core/modules/syslog/lib/Drupal/syslog/Logger/SysLog.php b/core/modules/syslog/lib/Drupal/syslog/Logger/SysLog.php new file mode 100644 index 0000000000000000000000000000000000000000..8dbb6189305ec2ff9c360a3860e05a82eddfbe52 --- /dev/null +++ b/core/modules/syslog/lib/Drupal/syslog/Logger/SysLog.php @@ -0,0 +1,96 @@ +config = $config_factory->get('syslog.settings'); + $this->parser = $parser; + } + + /** + * Opens a connection to the system logger. + */ + protected function openConnection() { + if (!$this->connectionOpened) { + $facility = $this->config->get('facility'); + if ($facility === '') { + $facility = defined('LOG_LOCAL0') ? LOG_LOCAL0 : LOG_USER; + } + $this->connectionOpened = openlog($this->config->get('identity'), LOG_NDELAY, $facility); + } + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = array()) { + global $base_url; + + // Ensure we have a connection available. + $this->openConnection(); + + // 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, + '!timestamp' => $context['timestamp'], + '!type' => $context['channel'], + '!ip' => $context['ip'], + '!request_uri' => $context['request_uri'], + '!referer' => $context['referer'], + '!uid' => $context['uid'], + '!link' => strip_tags($context['link']), + '!message' => strip_tags($message), + )); + + syslog($level, $entry); + } + +} diff --git a/core/modules/syslog/syslog.module b/core/modules/syslog/syslog.module index ba54118e577ce4d8478c76d77791d752b5a5831e..aff3d5680801ad2f53c06e1bff9286ec1a9eee64 100644 --- a/core/modules/syslog/syslog.module +++ b/core/modules/syslog/syslog.module @@ -89,36 +89,3 @@ function syslog_facility_list() { LOG_LOCAL7 => 'LOG_LOCAL7', ); } - -/** - * Implements hook_watchdog(). - */ -function syslog_watchdog(array $log_entry) { - global $base_url; - - $log_init = &drupal_static(__FUNCTION__, FALSE); - $config = \Drupal::config('syslog.settings'); - - if (!$log_init) { - $log_init = TRUE; - $facility = $config->get('facility'); - if ($facility === '') { - $facility = defined('LOG_LOCAL0') ? LOG_LOCAL0 : LOG_USER; - } - openlog($config->get('identity'), LOG_NDELAY, $facility); - } - - $message = strtr($config->get('format'), array( - '!base_url' => $base_url, - '!timestamp' => $log_entry['timestamp'], - '!type' => $log_entry['type'], - '!ip' => $log_entry['ip'], - '!request_uri' => $log_entry['request_uri'], - '!referer' => $log_entry['referer'], - '!uid' => $log_entry['uid'], - '!link' => strip_tags($log_entry['link']), - '!message' => strip_tags(!isset($log_entry['variables']) ? $log_entry['message'] : strtr($log_entry['message'], $log_entry['variables'])), - )); - - syslog($log_entry['severity'], $message); -} diff --git a/core/modules/syslog/syslog.services.yml b/core/modules/syslog/syslog.services.yml new file mode 100644 index 0000000000000000000000000000000000000000..78c7d38ec8f2a3158fb23d61ba3fe12cd6535842 --- /dev/null +++ b/core/modules/syslog/syslog.services.yml @@ -0,0 +1,6 @@ +services: + logger.syslog: + class: Drupal\syslog\Logger\SysLog + arguments: ['@config.factory', '@logger.log_message_parser'] + tags: + - { name: logger } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php index 712b6285c635ba4863d36ebfd85efdeb4afe56a0..252fb0926e308f62dc9e9995066a1c9d7bc8b6b6 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php @@ -48,9 +48,9 @@ function testEntityInfoChanges() { /** * Tests entity info cache after enabling a module with a dependency on an entity providing module. * - * @see entity_cache_test_watchdog() + * @see entity_cache_test_modules_enabled() */ - function testEntityInfoCacheWatchdog() { + function testEntityInfoCacheModulesEnabled() { \Drupal::moduleHandler()->install(array('entity_cache_test')); $entity_type = \Drupal::state()->get('entity_cache_test'); $this->assertEqual($entity_type->getLabel(), 'Entity Cache Test', 'Entity info label is correct.'); diff --git a/core/modules/system/lib/Drupal/system/Tests/Pager/PagerTest.php b/core/modules/system/lib/Drupal/system/Tests/Pager/PagerTest.php index dc8ead4abd241f316d459a962fef537d6b0f0562..845aa28d700cf63c42cc5122f540358933771025 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Pager/PagerTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Pager/PagerTest.php @@ -36,7 +36,7 @@ function setUp() { // Insert 300 log messages. for ($i = 0; $i < 300; $i++) { - watchdog('pager_test', $this->randomString(), NULL, WATCHDOG_DEBUG); + watchdog('pager_test', $this->randomString(), array(), WATCHDOG_DEBUG); } $this->admin_user = $this->drupalCreateUser(array( diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index c69838aeb5f3bb906ad44f4d179c36371bdc7d91..15f893fdf2a5c8c139235b068e109bd2db75077e 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -1180,94 +1180,6 @@ function hook_template_preprocess_default_variables_alter(&$variables) { $variables['is_admin'] = user_access('access administration pages'); } -/** - * Log an event message. - * - * This hook allows modules to route log events to custom destinations, such as - * SMS, Email, pager, syslog, ...etc. - * - * @param array $log_entry - * An associative array containing the following keys: - * - type: The type of message for this entry. - * - user: The user object for the user who was logged in when the event - * happened. - * - uid: The user ID for the user who was logged in when the event happened. - * - request_uri: The request URI for the page the event happened in. - * - referer: The page that referred the user to the page where the event - * occurred. - * - ip: The IP address where the request for the page came from. - * - timestamp: The UNIX timestamp of the date/time the event occurred. - * - severity: The severity of the message; one of the following values as - * defined in @link http://www.faqs.org/rfcs/rfc3164.html RFC 3164: @endlink - * - WATCHDOG_EMERGENCY: Emergency, system is unusable. - * - WATCHDOG_ALERT: Alert, action must be taken immediately. - * - WATCHDOG_CRITICAL: Critical conditions. - * - WATCHDOG_ERROR: Error conditions. - * - WATCHDOG_WARNING: Warning conditions. - * - WATCHDOG_NOTICE: Normal but significant conditions. - * - WATCHDOG_INFO: Informational messages. - * - WATCHDOG_DEBUG: Debug-level messages. - * - link: An optional link provided by the module that called the watchdog() - * function. - * - message: The text of the message to be logged. Variables in the message - * are indicated by using placeholder strings alongside the variables - * argument to declare the value of the placeholders. See t() for - * documentation on how the message and variable parameters interact. - * - variables: An array of variables to be inserted into the message on - * display. Will be NULL or missing if a message is already translated or if - * the message is not possible to translate. - */ -function hook_watchdog(array $log_entry) { - global $base_url; - $language_interface = \Drupal::languageManager()->getCurrentLanguage(); - - $severity_list = 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'), - ); - - $to = 'someone@example.com'; - $params = array(); - $params['subject'] = t('[@site_name] @severity_desc: Alert from your web site', array( - '@site_name' => \Drupal::config('system.site')->get('name'), - '@severity_desc' => $severity_list[$log_entry['severity']], - )); - - $params['message'] = "\nSite: @base_url"; - $params['message'] .= "\nSeverity: (@severity) @severity_desc"; - $params['message'] .= "\nTimestamp: @timestamp"; - $params['message'] .= "\nType: @type"; - $params['message'] .= "\nIP Address: @ip"; - $params['message'] .= "\nRequest URI: @request_uri"; - $params['message'] .= "\nReferrer URI: @referer_uri"; - $params['message'] .= "\nUser: (@uid) @name"; - $params['message'] .= "\nLink: @link"; - $params['message'] .= "\nMessage: \n\n@message"; - - $params['message'] = t($params['message'], array( - '@base_url' => $base_url, - '@severity' => $log_entry['severity'], - '@severity_desc' => $severity_list[$log_entry['severity']], - '@timestamp' => format_date($log_entry['timestamp']), - '@type' => $log_entry['type'], - '@ip' => $log_entry['ip'], - '@request_uri' => $log_entry['request_uri'], - '@referer_uri' => $log_entry['referer'], - '@uid' => $log_entry['uid'], - '@name' => $log_entry['user']->name, - '@link' => strip_tags($log_entry['link']), - '@message' => strip_tags($log_entry['message']), - )); - - drupal_mail('emaillog', 'entry', $to, $language_interface->id, $params); -} - /** * Prepare a message based on parameters; called from drupal_mail(). * diff --git a/core/modules/system/tests/modules/entity_cache_test/entity_cache_test.module b/core/modules/system/tests/modules/entity_cache_test/entity_cache_test.module index 9b288154e0b438b8a2afda5b97393df928b6d74a..f91afde41fe126a615de2dce2af3d53f31d7f34a 100644 --- a/core/modules/system/tests/modules/entity_cache_test/entity_cache_test.module +++ b/core/modules/system/tests/modules/entity_cache_test/entity_cache_test.module @@ -6,7 +6,7 @@ */ /** - * Implements hook_watchdog(). + * Implements hook_modules_installed(). * * This hook is called during \Drupal\Core\Extension\ModuleHandler::install() * and since this hook implementation is invoked, we have to expect that this @@ -14,13 +14,11 @@ * expect to be able to retrieve the entity information that has been registered * by the required dependency module. * - * @see EnableDisableTestCase::testEntityCache() + * @see EntityApiInfoTest::testEntityInfoCacheModulesEnabled() */ -function entity_cache_test_watchdog($log_entry) { - if ($log_entry['type'] == 'system' && $log_entry['message'] == '%module module installed.') { - $info = \Drupal::entityManager()->getDefinition('entity_cache_test'); - // Store the information in a system variable to analyze it later in the - // test case. - \Drupal::state()->set('entity_cache_test', $info); - } +function entity_cache_test_modules_installed($modules_enabled) { + $info = \Drupal::entityManager()->getDefinition('entity_cache_test'); + // Store the information in a system variable to analyze it later in the + // test case. + \Drupal::state()->set('entity_cache_test', $info); } diff --git a/core/tests/Drupal/Tests/Core/EventSubscriber/SpecialAttributesRouteSubscriberTest.php b/core/tests/Drupal/Tests/Core/EventSubscriber/SpecialAttributesRouteSubscriberTest.php index 0bb50069c3cd80a5ebc1d13d81e881d22a1d4a10..65ba07d3ba192f7bc9705f23b6da7e7f8d807675 100644 --- a/core/tests/Drupal/Tests/Core/EventSubscriber/SpecialAttributesRouteSubscriberTest.php +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/SpecialAttributesRouteSubscriberTest.php @@ -118,7 +118,7 @@ public function testOnRouteBuildingInvalidVariables(Route $route) { namespace { if (!function_exists('watchdog')) { - function watchdog($type, $message, array $args = NULL) { + function watchdog($type, $message, array $args = array()) { } } if (!function_exists('drupal_set_message')) { diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php index 8b09681f40433990657f3a1bd5cc5151e3ad8f0a..03ba5ebe8f16301fcfe2f10a1107c88fcae928bf 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php @@ -446,6 +446,7 @@ public static function create(ContainerInterface $container) { namespace { function test_form_id_custom_submit(array &$form, array &$form_state) { } + // @todo Remove once watchdog() is removed. if (!defined('WATCHDOG_ERROR')) { define('WATCHDOG_ERROR', 3); } diff --git a/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php b/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e30f551a26476af6dbae94cbcee39decdab46d80 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php @@ -0,0 +1,93 @@ + 'Log message parser', + 'description' => 'Unit tests for the log message parser.', + 'group' => 'Logger', + ); + } + + /** + * {@inheritdoc} + */ + protected function setUp() { + $this->parser = new LogMessageParser(); + } + + /** + * Test for LogMessageParserTrait::parseMessagePlaceholders() + * + * @param array $value + * An array containing: + * - message: A string that contains a message with placeholders. + * - context: An array with placeholder values. + * @param array $expected + * An array with the expected values after the test has run. + * - message: The expected parsed message. + * - context: The expected values of the placeholders. + * + * @dataProvider providerTestParseMessagePlaceholders + * @covers ::parseMessagePlaceholders + */ + public function testParseMessagePlaceholders(array $value, array $expected) { + $message_placeholders = $this->parser->parseMessagePlaceholders($value['message'], $value['context']); + $this->assertEquals($expected['message'], $value['message']); + $this->assertEquals($expected['context'], $message_placeholders); + } + + /** + * Data provider for testParseMessagePlaceholders(). + */ + public function providerTestParseMessagePlaceholders() { + return array( + // PSR3 only message. + array( + array('message' => 'User {username} created', 'context' => array('username' => 'Dries')), + array('message' => 'User @username created', 'context' => array('@username' => 'Dries')), + ), + // PSR3 style mixed in a format_string style message. + array( + array('message' => 'User {username} created @time', 'context' => array('username' => 'Dries', '@time' => 'now')), + array('message' => 'User @username created @time', 'context' => array('@username' => 'Dries', '@time' => 'now')), + ), + // format_string style message only. + array( + array('message' => 'User @username created', 'context' => array('@username' => 'Dries')), + array('message' => 'User @username created', 'context' => array('@username' => 'Dries')), + ), + // Messsage without placeholders but wildcard characters. + array( + array('message' => 'User W-\\};~{&! created @', 'context' => array('' => '')), + array('message' => 'User W-\\};~{&! created @', 'context' => array()), + ), + // Messsage with double PSR3 style messages. + array( + array('message' => 'Test {with} two {encapsuled} strings', 'context' => array('with' => 'together', 'encapsuled' => 'awesome')), + array('message' => 'Test @with two @encapsuled strings', 'context' => array('@with' => 'together', '@encapsuled' => 'awesome')), + ), + ); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelFactoryTest.php b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelFactoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..82e20b2969d8910d7633e2bc7160804732780a66 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelFactoryTest.php @@ -0,0 +1,66 @@ + 'Logger channel factory', + 'description' => 'Unit tests for the logger channel factory object.', + 'group' => 'Logger', + ); + } + + /** + * Tests LoggerChannelFactory::get(). + * + * @covers ::get + */ + public function testGet() { + $factory = new LoggerChannelFactory(); + $factory->setContainer($this->getMock('Symfony\Component\DependencyInjection\ContainerInterface')); + + // Ensure that when called with the same argument, always the same instance + // will be returned. + $this->assertEquals($factory->get('test'), $factory->get('test')); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2cc0a509430f6ed42db519f61304e14ebaea7f83 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php @@ -0,0 +1,158 @@ + 'Logger channel', + 'description' => 'Unit tests for the logger channel object.', + 'group' => 'Logger', + ); + } + + /** + * Tests LoggerChannel::log(). + * + * @param callable $expected + * An anonymous function to use with $this->callback() of the logger mock. + * The function should check the $context array for expected values. + * @param \Symfony\Component\HttpFoundation\Request $request + * Will be passed to the channel under test if present. + * @param \Drupal\Core\Session\AccountInterface $current_user + * Will be passed to the channel under test if present. + * + * @dataProvider providerTestLog + * @covers ::log + * @covers ::setCurrentUser + * @covers ::setRequest + */ + public function testLog(callable $expected, Request $request = NULL, AccountInterface $current_user = NULL) { + $channel = new LoggerChannel('test'); + $message = $this->randomName(); + $logger = $this->getMock('Psr\Log\LoggerInterface'); + $logger->expects($this->once()) + ->method('log') + ->with($this->anything(), $message, $this->callback($expected)); + $channel->addLogger($logger); + if ($request) { + $channel->setRequest($request); + } + if ($current_user) { + $channel->setCurrentUser($current_user); + } + $channel->log(rand(0, 7), $message); + } + + /** + * Tests LoggerChannel::addLoggers(). + * + * @covers ::addLogger + * @covers ::sortLoggers + */ + public function testSortLoggers() { + $channel = new LoggerChannel($this->randomName()); + $index_order = ''; + for ($i = 0; $i < 4; $i++) { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + $logger->expects($this->once()) + ->method('log') + ->will($this->returnCallback(function () use ($i, &$index_order) { + // Append the $i to the index order, so that we know the order that + // loggers got called with. + $index_order .= $i; + })); + $channel->addLogger($logger, $i); + } + + $channel->log(rand(0, 7), $this->randomName()); + // Ensure that the logger added in the end fired first. + $this->assertEquals($index_order, '3210'); + } + + /** + * Data provider for self::testLog(). + */ + public function providerTestLog() { + $account_mock = $this->getMock('Drupal\Core\Session\AccountInterface'); + $account_mock->expects($this->exactly(2)) + ->method('id') + ->will($this->returnValue(1)); + + $request_mock = $this->getMock('Symfony\Component\HttpFoundation\Request'); + $request_mock->expects($this->exactly(2)) + ->method('getClientIp') + ->will($this->returnValue('127.0.0.1')); + $request_mock->headers = $this->getMock('Symfony\Component\HttpFoundation\ParameterBag'); + + // No request or account. + $cases [] = array( + function ($context) { + return $context['channel'] == 'test' && empty($contex['uid']) && empty($context['ip']); + }, + ); + // With account but not request. + $cases [] = array( + function ($context) { + return $context['uid'] === 1 && empty($context['ip']); + }, + NULL, + $account_mock, + ); + // With request but not account. + $cases [] = array( + function ($context) { + return $context['ip'] === '127.0.0.1' && empty($contex['uid']); + }, + $request_mock, + ); + // Both request and account. + $cases [] = array( + function ($context) { + return $context['ip'] === '127.0.0.1' && $context['uid'] === 1; + }, + $request_mock, + $account_mock, + ); + return $cases; + } + +} diff --git a/core/update.php b/core/update.php index 3bac378545eb76bef0475e4560ec0dda65fba1df..b6accf3a5f42d97d5682de51bc3bebd58e816167 100644 --- a/core/update.php +++ b/core/update.php @@ -226,7 +226,7 @@ function update_info_page() { function update_access_denied_page() { drupal_add_http_header('Status', '403 Forbidden'); header(\Drupal::request()->server->get('SERVER_PROTOCOL') . ' 403 Forbidden'); - watchdog('access denied', 'update.php', NULL, WATCHDOG_WARNING); + watchdog('access denied', 'update.php', array(), WATCHDOG_WARNING); $output = '

Access denied. You are not authorized to access this page. Log in using either an account with the administer software updates permission or the site maintenance account (the account you created during installation). If you cannot log in, you will have to edit settings.php to bypass this access check. To do this:

  1. With a text editor find the settings.php file on your system. From the main Drupal directory that you installed all the files into, go to sites/your_site_name if such directory exists, or else to sites/default which applies otherwise.