diff --git a/core/core.services.yml b/core/core.services.yml index 802d143..4fbb0e0 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -123,6 +123,15 @@ services: tags: - { name: needs_destruction } arguments: ['@database'] + logger.factory: + class: Drupal\Core\Logger\LoggerChannelFactory + calls: + - [setContainer, ['@service_container']] + logger.channel.default: + class: Drupal\Core\Logger\LoggerChannel + factory_method: get + factory_service: logger.factory + arguments: ['system'] settings: class: Drupal\Component\Utility\Settings factory_class: Drupal\Component\Utility\Settings @@ -228,7 +237,7 @@ services: arguments: ['@service_container'] controller_resolver: class: Drupal\Core\Controller\ControllerResolver - arguments: ['@service_container'] + arguments: ['@service_container', '@logger.channel.default'] title_resolver: class: Drupal\Core\Controller\TitleResolver arguments: ['@controller_resolver', '@string_translation'] @@ -295,7 +304,7 @@ services: - [setFinalMatcher, ['@router.matcher.final_matcher']] 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']] @@ -564,7 +573,7 @@ services: class: Drupal\Core\EventSubscriber\ExceptionListener tags: - { name: event_subscriber } - arguments: [['@exception_controller', execute]] + arguments: [['@exception_controller', execute], '@logger.channel.default'] route_processor_manager: class: Drupal\Core\RouteProcessor\RouteProcessorManager path_processor_manager: diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 9e5c50f..314cbae 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -1179,54 +1179,11 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia * @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; + $variables = $variables ?: array(); + if ($link) { + $variables['link'] = $link; } + \Drupal::service('logger.factory')->get($type)->log($severity, $message, $variables); } /** diff --git a/core/lib/Drupal/Core/Controller/ControllerResolver.php b/core/lib/Drupal/Core/Controller/ControllerResolver.php index 7928c36..2ce142b 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\ContainerInterface; @@ -49,7 +49,7 @@ class ControllerResolver extends BaseControllerResolver implements ControllerRes * * @param \Symfony\Component\DependencyInjection\ContainerInterface $container * A ContainerInterface instance. - * @param \Symfony\Component\HttpKernel\Log\LoggerInterface $logger + * @param \Psr\Log\LoggerInterface $logger * (optional) A LoggerInterface instance. */ public function __construct(ContainerInterface $container, LoggerInterface $logger = NULL) { diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index 927189e..c4eea22 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -13,6 +13,7 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass; use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass; +use Drupal\Core\DependencyInjection\Compiler\RegisterLoggersPass; use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass; use Drupal\Core\DependencyInjection\Compiler\RegisterPathProcessorsPass; use Drupal\Core\DependencyInjection\Compiler\RegisterRouteProcessorsPass; @@ -74,6 +75,8 @@ public function register(ContainerBuilder $container) { $container->addCompilerPass(new ListCacheBinsPass()); // Add the compiler pass for appending string translators. $container->addCompilerPass(new RegisterStringTranslatorsPass()); + // Add a compiler pass for registering logging services. + $container->addCompilerPass(new RegisterLoggersPass()); // Add the compiler pass that will process the tagged breadcrumb builder // services. $container->addCompilerPass(new RegisterBreadcrumbBuilderPass()); diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterLoggersPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterLoggersPass.php new file mode 100644 index 0000000..80723c6 --- /dev/null +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterLoggersPass.php @@ -0,0 +1,33 @@ +getDefinition('logger.factory'); + // Loop through all available logger services (eg dblog, syslog) and add + // them to the factory. + foreach ($container->findTaggedServiceIds('logger') as $id => $attributes) { + $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; + $factory->addMethodCall('addLogger', array(new Reference($id), $priority)); + } + } + +} diff --git a/core/lib/Drupal/Core/Logger/LogMessageParserTrait.php b/core/lib/Drupal/Core/Logger/LogMessageParserTrait.php new file mode 100644 index 0000000..808c7a8 --- /dev/null +++ b/core/lib/Drupal/Core/Logger/LogMessageParserTrait.php @@ -0,0 +1,62 @@ + $start) { + $has_ps3 = 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_ps3) { + // 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 new file mode 100644 index 0000000..6ba39d9 --- /dev/null +++ b/core/lib/Drupal/Core/Logger/LoggerChannel.php @@ -0,0 +1,182 @@ +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 BC and PHP syslog() compatibility. + $level = $this->mapLevel($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) { + foreach ($loggers as $priority => $group) { + foreach ($group as $logger) { + if (!$logger instanceof LoggerInterface) { + throw new InvalidArgumentException('All loggers must implement \Psr\Log\LoggerInterface.'); + } + } + } + $this->loggers = $loggers; + } + + /** + * {@inheritdoc} + */ + public function addLogger(LoggerInterface $logger, $priority = 0) { + $this->loggers[$priority][] = $logger; + } + + /** + * Converts a PSR Log level to a Watchdog log level. + * + * This only exists for BC. + * @todo Remove once watchdog() is removed. + * + * @param string $level + * A PSR-3 logging level string. + * + * @return int + * The corresponding Watchdog constant. + */ + protected function mapLevel($level) { + if (empty($this->levelTranslation)) { + $this->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, + ); + } + + return $this->levelTranslation[$level]; + } + + /** + * 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 0000000..986af54 --- /dev/null +++ b/core/lib/Drupal/Core/Logger/LoggerChannelFactory.php @@ -0,0 +1,69 @@ +channels[$channel])) { + $instance = new LoggerChannel($channel); + + // If the request is available, set it with the current user to the channel. + 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 0000000..67d178c --- /dev/null +++ b/core/lib/Drupal/Core/Logger/LoggerChannelFactoryInterface.php @@ -0,0 +1,40 @@ +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(). */ function dblog_form_system_logging_settings_alter(&$form, $form_state) { diff --git a/core/modules/dblog/dblog.services.yml b/core/modules/dblog/dblog.services.yml new file mode 100644 index 0000000..c07cb32 --- /dev/null +++ b/core/modules/dblog/dblog.services.yml @@ -0,0 +1,6 @@ +services: + logger.dblog: + class: Drupal\dblog\Logger\DbLog + arguments: ['@database'] + 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 0000000..034c6ba --- /dev/null +++ b/core/modules/dblog/lib/Drupal/dblog/Logger/DbLog.php @@ -0,0 +1,68 @@ +database = $database; + } + + /** + * {@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. + $this->parseMessagePlaceholders($message, $context); + + $this->database + ->insert('watchdog') + ->fields(array( + 'uid' => $context['uid'], + 'type' => substr($context['channel'], 0, 64), + 'message' => $message, + 'variables' => serialize($context['variables']), + '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 24add00..dd4a575 100644 --- a/core/modules/dblog/lib/Drupal/dblog/Tests/DbLogTest.php +++ b/core/modules/dblog/lib/Drupal/dblog/Tests/DbLogTest.php @@ -129,7 +129,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, @@ -144,7 +144,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); } } @@ -412,7 +412,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, @@ -425,7 +425,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. @@ -530,7 +530,7 @@ protected function getLogEntries() { foreach ($table->tbody->tr as $row) { $entries[] = array( 'severity' => $this->getSeverityConstant($row['class']), - 'type' => $this->asText($row->td[1]), + 'channel' => $this->asText($row->td[1]), 'message' => $this->asText($row->td[3]), 'user' => $this->asText($row->td[4]), ); @@ -553,7 +553,7 @@ protected function getTypeCount(array $types) { $count = array_fill(0, count($types), 0); foreach ($entries as $entry) { foreach ($types as $key => $type) { - if ($entry['type'] == $type['type'] && $entry['severity'] == $type['severity']) { + if ($entry['channel'] == $type['type'] && $entry['severity'] == $type['severity']) { $count[$key]++; break; } diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterSettingsTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterSettingsTest.php index a11db5d..f37d48b 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 53e5ed7..0bebb19 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 0000000..b0e31f8 --- /dev/null +++ b/core/modules/syslog/lib/Drupal/syslog/Logger/SysLog.php @@ -0,0 +1,69 @@ +config = $config_factory->get('syslog.settings'); + $facility = $this->config->get('facility'); + if ($facility === '') { + $facility = defined('LOG_LOCAL0') ? LOG_LOCAL0 : LOG_USER; + } + openlog($this->config->get('identity'), LOG_NDELAY, $facility); + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = array()) { + global $base_url; + + // Populate the $context['variables'] with the message placeholders. + $this->parseMessagePlaceholders($message, $context); + + $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(empty($context['variables']) ? $message : strtr($message, $context['variables'])), + )); + + syslog($level, $entry); + } + +} diff --git a/core/modules/syslog/syslog.module b/core/modules/syslog/syslog.module index a93ee93..ef282d1 100644 --- a/core/modules/syslog/syslog.module +++ b/core/modules/syslog/syslog.module @@ -87,36 +87,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 0000000..98f6041 --- /dev/null +++ b/core/modules/syslog/syslog.services.yml @@ -0,0 +1,6 @@ +services: + logger.syslog: + class: Drupal\syslog\Logger\SysLog + arguments: ['@config.factory'] + 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 93fd554..1849f25 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/system.api.php b/core/modules/system/system.api.php index 8ae6c7c..af2b173 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -1263,94 +1263,6 @@ function hook_template_preprocess_default_variables_alter(&$variables) { } /** - * 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(). * * Note that hook_mail(), unlike hook_mail_alter(), is only called on the 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 9b28815..f91afde 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/Logger/LogMessageParserTraitTest.php b/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTraitTest.php new file mode 100644 index 0000000..a7b81cf --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTraitTest.php @@ -0,0 +1,95 @@ + 'Log message parser', + 'description' => 'Unit tests for the log message parser trait.', + 'group' => 'Logger', + ); + } + + /** + * {@inheritdoc} + */ + protected function setUp() { + $this->logger = $this->getObjectForTrait('Drupal\Core\Logger\LogMessageParserTrait'); + } + + /** + * 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) { + $class = new \ReflectionClass($this->logger); + $method = $class->getMethod('parseMessagePlaceholders'); + $method->setAccessible(TRUE); + $method->invokeArgs($this->logger, array(&$value['message'], &$value['context'])); + $this->assertEquals($expected['message'], $value['message']); + $this->assertEquals($expected['context'], $value['context']['variables']); + } + + /** + * 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 0000000..61b3415 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelFactoryTest.php @@ -0,0 +1,60 @@ + 'Logger channel factory', + 'description' => 'Unit tests for the logger channel factory object.', + 'group' => 'Logger', + ); + } + + /** + * Test for 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 0000000..64c206e --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php @@ -0,0 +1,152 @@ + 'Logger channel', + 'description' => 'Unit tests for the logger channel object.', + 'group' => 'Logger', + ); + } + + /** + * Test for 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); + } + + /** + * Test for 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; + } + +}