Commit e44d15a7 authored by catch's avatar catch

Issue #1289536 by ParisLiakos, RobLoach, Crell, larowlan, fgm, pounard,...

Issue #1289536 by ParisLiakos, RobLoach, Crell, larowlan, fgm, pounard, Pancho, dawehner, scor, tim.plunkett, alexpott, socketwench: Switch Watchdog to a PSR-3 logging framework.
parent 85d39d84
......@@ -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.');
}
......
......@@ -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']]
......@@ -752,11 +765,6 @@ services:
- { name: needs_destruction }
authentication:
class: Drupal\Core\Authentication\AuthenticationManager
authentication.cookie:
class: Drupal\Core\Authentication\Provider\Cookie
arguments: ['@session_manager']
tags:
- { name: authentication_provider, priority: 0 }
authentication_subscriber:
class: Drupal\Core\EventSubscriber\AuthenticationSubscriber
tags:
......
......@@ -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);
}
/**
......
......@@ -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);
}
}
......@@ -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) {
......
......@@ -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');
......
......@@ -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);
}
......
<?php
/**
* @file
* Contains \Drupal\Core\Logger\LogMessageParser.
*/
namespace Drupal\Core\Logger;
/**
* Parses log messages and their placeholders.
*/
class LogMessageParser implements LogMessageParserInterface {
/**
* {@inheritdoc}
*/
public function parseMessagePlaceholders(&$message, array &$context) {
$variables = array();
$has_psr3 = FALSE;
if (($start = strpos($message, '{')) !== FALSE && strpos($message, '}') > $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;
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Logger\LogMessageParserInterface.
*/
namespace Drupal\Core\Logger;
/**
* Defines an interface for parsing log messages and their placeholders.
*/
interface LogMessageParserInterface {
/**
* Parses and transforms message and its placeholders to a common format.
*
* For a value to be considered as a placeholder should be in the following
* formats:
* - PSR3 format:
* @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#12-message
* - Drupal specific string placeholder format:
* @see \Drupal\Component\Utility\String::format()
*
* Values in PSR3 format will be transformed to String::format() format.
*
* @param string $message
* The message that contains the placeholders.
* If the message is in PSR3 style, it will be transformed to
* \Drupal\Component\Utility\String::format() style.
* @param array $context
* An array that may or may not contain placeholder variables.
*
* @return array
* An array of the extracted message placeholders.
*/
public function parseMessagePlaceholders(&$message, array &$context);
}
<?php
/**
* @file
* Contains \Drupal\Core\Logger\LoggerChannel.
*/
namespace Drupal\Core\Logger;
use Drupal\Core\Session\AccountInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\LoggerTrait;
use Psr\Log\LogLevel;
use Symfony\Component\HttpFoundation\Request;
/**
* Defines a logger channel that most implementations will use.
*/
class LoggerChannel implements LoggerChannelInterface {
use LoggerTrait;
/**
* The name of the channel of this logger instance.
*
* @var string
*/
protected $channel;
/**
* Map of PSR Log constants to Watchdog log constants.
*
* @todo Move watchdog constants here when 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,
);
/**
* 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;
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Logger\LoggerChannelFactory.
*/
namespace Drupal\Core\Logger;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
/**
* Defines a factory for logging channels.
*/
class LoggerChannelFactory implements LoggerChannelFactoryInterface, ContainerAwareInterface {
use ContainerAwareTrait;
/**
* Array of all instantiated logger channels keyed by channel name.
*
* @var Drupal\Core\Logger\LoggerChannelInterface[]
*/
protected $channels = array();
/**
* An array of arrays of \Psr\Log\LoggerInterface keyed by priority.
*
* @var array
*/
protected $loggers = array();
/**
* {@inheritdoc}
*/
public function get($channel) {
if (!isset($this->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);
}
}
}
<?php
/**
* @file
* Contains \Drupal\Core\Logger\LoggerChannelFactoryInterface.
*/
namespace Drupal\Core\Logger;
use Psr\Log\LoggerInterface;
/**
* Logger channel factory interface.
*/
interface LoggerChannelFactoryInterface {
/**
* Retrieves the registered logger for the requested channel.
*
* @return \Drupal\Core\Logger\LoggerChannelInterface
* The registered logger for this channel.
*/
public function get($channel);
/**
* Adds a logger.
*
* Here is were all services tagged as 'logger' are being retrieved and then
* passed to the channels after instantiation.
*
* @param \Psr\Log\LoggerInterface $logger
* The PSR-3 logger to add.
* @param int $priority
* The priority of the logger being added.
*
* @see \Drupal\Core\DependencyInjection\Compiler\RegisterLoggersPass
*/
public function addLogger(LoggerInterface $logger, $priority = 0);
}
<?php
/**
* @file
* Contains \Drupal\Core\Logger\LoggerChannelInterface.
*/
namespace Drupal\Core\Logger;
use Drupal\Core\Session\AccountInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Logger channel interface.
*/
interface LoggerChannelInterface extends LoggerInterface {
/**
* Sets the request.
*
* @param \Symfony\Component\HttpFoundation\Request|null $request
* The current request object.
*/
public function setRequest(Request $request = NULL);
/**
* Sets the current user.
*
* @param \Drupal\Core\Session\AccountInterface|null $current_user
* The current user object.
*/
public function setCurrentUser(AccountInterface $current_user = NULL);
/**
* Sets the loggers for this channel.
*
* @param array $loggers
* An array of arrays of \Psr\Log\LoggerInterface keyed by priority.
*/
public function setLoggers(array $loggers);
/**
* Adds a logger.
*
* @param \Psr\Log\LoggerInterface $logger
* The PSR-3 logger to add.
* @param int $priority
* The priority of the logger being added.
*/
public function addLogger(LoggerInterface $logger, $priority = 0);
}
......@@ -7,8 +7,8 @@
namespace Drupal\Core\Routing;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\Routing\Route as SymfonyRoute;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
......@@ -88,7 +88,7 @@ class UrlGenerator extends ProviderBasedGenerator implements UrlGeneratorInterfa
* The config factory.
* @param \Drupal\Core\Site\Settings $settings
* The read only settings.
* @param \Symfony\Component\HttpKernel\Log\LoggerInterface $logger
* @param \Psr\Log\LoggerInterface $logger
* An optional logger for recording errors.
*/
public function __construct(RouteProviderInterface $provider, OutboundPathProcessorInterface $path_processor, OutboundRouteProcessorInterface $route_processor, ConfigFactoryInterface $config, Settings $settings, LoggerInterface $logger = NULL) {
......
......@@ -348,7 +348,7 @@ public static function finishBatch($success, $results, $operations) {
if (!empty($results['errors'])) {
foreach ($results['errors'] as $error) {
drupal_set_message($error, 'error');
watchdog('config_sync', $error, NULL, WATCHDOG_ERROR);
watchdog('config_sync', $error, array(), WATCHDOG_ERROR);
}
drupal_set_message(\Drupal::translation()->translate('The configuration was imported with errors.'), 'warning');
}
......
......@@ -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().
*/
......
services:
logger.dblog:
class: Drupal\dblog\Logger\DbLog
arguments: ['@database', '@logger.log_message_parser']
tags:
- { name: logger }
<?php
/**
* @file
* Contains \Drupal\dblog\Logger\DbLog.
*/
namespace Drupal\dblog\Logger;
use Drupal\Core\Database\Connection;
use Drupal\Core\Logger\LogMessageParserInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\LoggerTrait;
/**
* Logs events in the watchdog database table.
*/
class DbLog implements LoggerInterface {
use LoggerTrait;
/**
* The database connection object.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* The message's placeholders parser.