Skip to content
Snippets Groups Projects
Commit b358bd0c authored by Frédéric G. Marand's avatar Frédéric G. Marand
Browse files

Issue #2984187 by fgm: avoid warnings with invalid request tracking setups.

- report on missing mod_unique_id situations, hook_requirements as a class.
parent 72b3200a
No related branches found
No related tags found
No related merge requests found
......@@ -40,8 +40,8 @@ INSTALLATION AND SETTINGS
The MongoDB module and sub-modules need some configuration to be useful. This
guide assumes that :
* a [MongoDB][download] 3.0 or later instance is already installed, configured,
and available to connect to from the Drupal instance.
* a [MongoDB][download] 3.0 to 3.6 instance is already installed, configured, and
available to connect to from the Drupal instance.
* the site will be running [Drupal][drupal] 8.[56].x, with [Drush][drush] 8.x.
* the [mongodb][mongodb] (not [mongo][mongo]) PHP extension version 1.1.7 or
later is installed and configured.
......@@ -81,6 +81,7 @@ with the `–httpinterface` option, you may view the web admin interface:
For example, with the following settings:
```php
// In sites/default/settings.local.php.
$settings['mongodb'] = [
'clients' => [
// Client alias => connection constructor parameters.
......@@ -112,6 +113,7 @@ $settings['mongodb'] = [
the existing declarations in the `sites/default/services.yml` file:
```yaml
# In sites/default/services.yml.
factory.keyvalue:
default: keyvalue.mongodb
factory.keyvalue.expirable:
......@@ -227,11 +229,11 @@ DATABASES AND COLLECTIONS REFERENCE
Module | DB alias | Collection(s) | Information
--------------------|------------|--------------------|--------------------------
`mongodb` | `default` | (none) | Alias/client consistency
`mongodb_storage` | `keyvalue` | `kve_*` | Expirable collections
↑ | ↑ | `kvp_*` | Persistent collections
`mongodb_watchdog` | `logger` | `watchdog` | Event types
↑ | ↑ | `watchdog_tracker` | Requests (capped)
↑ | ↑ | `watchdog_*` | Events (capped)
`mongodb_storage` | `keyvalue` | `kve_*` | Expirable collections
↑ | ↑ | `kvp_*` | Persistent collections
Earlier versions used to support a collection aliasing mechanism. With this
version generalizing dedicated databases per module, this is no longer needed
......
......@@ -2,4 +2,4 @@ items: 10000
requests: 100000
limit: 7
items_per_page: 50
request_tracking: true
request_tracking: false
......@@ -5,8 +5,7 @@
* MongoDB watchdog install file.
*/
use Drupal\Core\Site\Settings;
use Drupal\Core\Url;
use Drupal\mongodb_watchdog\Install\Requirements;
use Drupal\mongodb_watchdog\Logger;
/**
......@@ -32,75 +31,22 @@ function mongodb_watchdog_install() {
* @see http://blog.riff.org/2015_08_27_drupal_8_tip_of_the_day_autoloaded_code_in_a_module_install_file
*/
function mongodb_watchdog_requirements($phase) {
$settings = Settings::get('mongodb');
$driver = 'mongodb';
$module = 'mongodb_watchdog';
$ret = [
$module => [
'title' => 'MongoDB watchdog',
],
];
$logger_alias = 'logger';
$databases = $settings['databases'];
if (!isset($databases[$logger_alias])) {
$ret[$module] += [
'severity' => REQUIREMENT_ERROR,
'value' => t('Missing `@alias` database alias in settings.', ['@alias' => $logger_alias]),
];
return $ret;
}
list($logger_client, $logger_db) = $databases[$logger_alias];
unset($databases[$logger_alias]);
$duplicates = [];
foreach ($databases as $alias => $list) {
list($client, $db) = $list;
if ($logger_client == $client && $logger_db == $db) {
$duplicates[] = "`$alias`";
}
if ($phase === 'install') {
// Dependencies may not be installed yet, and module isn't.
drupal_classloader_register($driver, drupal_get_path('module', $driver));
drupal_classloader_register($module, drupal_get_path('module', $module));
// Module is not yet available so its services aren't either.
$requirements = \Drupal::classResolver()
->getInstanceFromDefinition(Requirements::class);
}
if (!empty($duplicates)) {
$ret[$module] += [
'severity' => REQUIREMENT_ERROR,
'value' => t('The `@alias` alias points to the same database as @others.', [
'@alias' => $logger_alias,
'@others' => implode(', ', $duplicates),
]),
'description' => t('Those databases would also be dropped when uninstalling the watchdog module.'),
];
return $ret;
else {
/** @var \Drupal\mongodb_watchdog\Install\Requirements $requirements */
$requirements = \Drupal::service(Logger::SERVICE_REQUIREMENTS);
}
if (PHP_SAPI !== 'cli') {
if ($phase === 'install') {
drupal_classloader_register($module, drupal_get_path('module', 'mongodb_watchdog'));
}
$request_tracking = \Drupal::config(Logger::CONFIG_NAME)->get('request_tracking');
$server = \Drupal::request()->server;
if (!function_exists('apache_get_modules')
|| (!in_array('mod_unique_id', apache_get_modules()))
|| (!$server->has('UNIQUE_ID'))
|| (!$server->has('REDIRECT_UNIQUE_ID'))) {
$ret[$module] += [
'severity' => REQUIREMENT_INFO,
'value' => t('No mod_unique_id'),
'description' => t('The site is not served by Apache with a working mod_unique_id: request tracking is not available.'),
];
return $ret;
}
if (!$request_tracking) {
$ret[$module] += [
'severity' => REQUIREMENT_INFO,
'value' => t('Unused mod_unique_id'),
'description' => t('The site could track requests, but request tracking is not enabled. You could disable mod_unique_id to save resources, or <a href=":config">enable request tracking</a> for a better logging experience.', [
':config' => Url::fromRoute('mongodb_watchdog.config')->toString(),
]),
];
return $ret;
}
}
return $requirements->check($phase);
}
/**
......
......@@ -33,3 +33,12 @@ services:
- '@config.factory'
- '@date.formatter'
- '@mongodb.logger'
mongodb.watchdog_requirements:
class: '\Drupal\mongodb_watchdog\Install\Requirements'
arguments:
- '@settings'
- '@config.factory'
- '@request_stack'
- '@serialization.yaml'
- '@messenger'
<?php
namespace Drupal\mongodb_watchdog\Install;
use Drupal\Component\Serialization\SerializationInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Site\Settings;
use Drupal\Core\Url;
use Drupal\mongodb\MongoDb;
use Drupal\mongodb_watchdog\Logger;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Class Requirements implements hook_requirements().
*/
class Requirements implements ContainerInjectionInterface {
/**
* The module configuration.
*
* @var \Drupal\Core\Config\Config
*/
protected $config;
/**
* The config.factory service.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The messenger service.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* The request_stack service.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* The serialization.yaml service.
*
* @var \Drupal\Component\Serialization\SerializationInterface
*/
protected $serialization;
/**
* The section of Settings related to the MongoDB package.
*
* @var array
*/
protected $settings;
/**
* Requirements constructor.
*
* @param \Drupal\Core\Site\Settings $settings
* The settings service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* The config.factory service.
* @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
* The request_stack service.
* @param \Drupal\Component\Serialization\SerializationInterface $serialization
* The serialization.yaml service.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger service.
*/
public function __construct(
Settings $settings,
ConfigFactoryInterface $configFactory,
RequestStack $requestStack,
SerializationInterface $serialization,
MessengerInterface $messenger
) {
$this->serialization = $serialization;
$this->configFactory = $configFactory;
$this->requestStack = $requestStack;
$this->settings = $settings->get(MongoDb::MODULE);
$this->messenger = $messenger;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) : Requirements {
return new static(
$container->get('settings'),
$container->get('config.factory'),
$container->get('request_stack'),
$container->get('serialization.yaml'),
$container->get('messenger'));
}
/**
* Apply database aliases consistency checks.
*
* @param array $state
* The current state of requirements checks.
*
* @return array
* - array: The current state of requirements checks.
* - bool: true if the checks added an error, false otherwise
*/
protected function checkDatabaseAliasConsistency(array $state) : array {
$databases = $this->settings['databases'];
if (!isset($databases[Logger::DB_LOGGER])) {
$state[Logger::MODULE] += [
'severity' => REQUIREMENT_ERROR,
'value' => t('Missing `@alias` database alias in settings.', ['@alias' => Logger::DB_LOGGER]),
];
return [$state, TRUE];
}
list($logger_client, $logger_db) = $databases[Logger::DB_LOGGER];
unset($databases[Logger::DB_LOGGER]);
$duplicates = [];
foreach ($databases as $alias => $list) {
list($client, $db) = $list;
if ($logger_client == $client && $logger_db == $db) {
$duplicates[] = "`$alias`";
}
}
if (!empty($duplicates)) {
$state[Logger::MODULE] += [
'severity' => REQUIREMENT_ERROR,
'value' => t('The `@alias` alias points to the same database as @others.', [
'@alias' => Logger::DB_LOGGER,
'@others' => implode(', ', $duplicates),
]),
'description' => t('Those databases would also be dropped when uninstalling the watchdog module.'),
];
return [$state, TRUE];
}
return [$state, FALSE];
}
/**
* Load the configuration from default or from active configuration.
*
* @param bool $useDefault
* Use default configuration ?
*/
protected function loadConfig(bool $useDefault) {
if ($useDefault) {
$rawDefaultConfig = file_get_contents(__DIR__ . '/../../config/install/mongodb_watchdog.settings.yml');
$defaultConfigData = $this->serialization->decode($rawDefaultConfig);
$this->config = $this->configFactory->getEditable(Logger::MODULE);
$this->config->initWithData($defaultConfigData);
}
else {
$this->config = $this->configFactory->get(Logger::CONFIG_NAME);
}
}
/**
* Check the consistency of request tracking vs configuration and environment.
*
* @param array $state
* The current state of requirements.
*
* @return array
* - array: The current state of requirements checks.
* - bool: true if the checks added an error, false otherwise
*/
protected function checkRequestTracking(array $state) : array {
$requestTracking = $this->config->get('request_tracking');
if ($this->hasUniqueId()) {
$state[Logger::MODULE] += $requestTracking
? [
'value' => t('Mod_unique_id available and used'),
'severity' => REQUIREMENT_OK,
'description' => t('Request tracking is available and active.'),
]
: [
'value' => t('Unused mod_unique_id'),
'severity' => REQUIREMENT_INFO,
'description' => t('The site could track requests, but request tracking is not enabled. You could disable mod_unique_id to save resources, or <a href=":config">enable request tracking</a> for a better logging experience.',
[
':config' => Url::fromRoute('mongodb_watchdog.config')
->toString(),
]),
];
return [$state, FALSE];
}
$state[Logger::MODULE] += [
'value' => t('No mod_unique_id'),
];
if ($requestTracking) {
if (php_sapi_name() === 'cli') {
$message = t('Request tracking is configured, but the site cannot check the working mod_unique_id configuration from the CLI. Be sure to validate configuration on the <a href=":report">status page</a>.', [
':report' => Url::fromRoute('system.status')->toString(),
]);
$state[Logger::MODULE] += [
'severity' => REQUIREMENT_WARNING,
'description' => $message,
];
$this->messenger->addWarning($message);
return [$state, FALSE];
}
$state[Logger::MODULE] += [
'severity' => REQUIREMENT_ERROR,
'description' => t('Request tracking is configured, but the site is not served by Apache with a working mod_unique_id.'),
];
return [$state, TRUE];
}
$state[Logger::MODULE] += [
'severity' => REQUIREMENT_OK,
'description' => t('Request tracking is not configured.'),
];
return [$state, FALSE];
}
/**
* Implements hook_requirements().
*/
public function check(string $phase) {
$state = [
Logger::MODULE => [
'title' => 'MongoDB watchdog',
],
];
list($state, $err) = $this->checkDatabaseAliasConsistency($state);
if ($err) {
return $state;
}
$this->loadConfig($phase !== 'runtime');
list($state, $err) = $this->checkRequestTracking($state);
if ($err) {
return $state;
}
return $state;
}
/**
* Is mod_unique_id available on this instance ?
*
* @return bool
* Is it ?
*/
protected function hasUniqueId(): bool {
$server = $this->requestStack->getCurrentRequest()->server;
return $server->has('UNIQUE_ID') || $server->has('REDIRECT_UNIQUE_ID');
}
}
......@@ -35,6 +35,10 @@ class Logger extends AbstractLogger {
// The logger database alias.
const DB_LOGGER = 'logger';
const MODULE = 'mongodb_watchdog';
const SERVICE_REQUIREMENTS = 'mongodb.watchdog_requirements';
const TRACKER_COLLECTION = 'watchdog_tracker';
const TEMPLATE_COLLECTION = 'watchdog';
const EVENT_COLLECTION_PREFIX = 'watchdog_event_';
......@@ -217,6 +221,7 @@ class Logger extends AbstractLogger {
* {@inheritdoc}
*
* @see https://drupal.org/node/1355808
* @see https://httpd.apache.org/docs/2.4/en/mod/mod_unique_id.html
*/
public function log($level, $template, array $context = []) {
if ($level > $this->limit) {
......@@ -276,6 +281,10 @@ class Logger extends AbstractLogger {
];
$this->trackerCollection()->insertOne($track);
}
else {
// 24-byte format like mod_unique_id values.
$request_id = '@@Not-a-valid-request@@';
}
$event_collection = $this->eventCollection($template_id);
if ($template_result->getUpsertedCount()) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment