Skip to content
Snippets Groups Projects
Commit bc77db87 authored by Valery Lourie's avatar Valery Lourie
Browse files

Issue #3494951: Delayed logging feature

parent 8f89c6bb
Branches
Tags 8.x-1.12
No related merge requests found
services: services:
logger.solr: logger.solr:
class: Drupal\solrlog\Logger\SolrLogger class: Drupal\solrlog\Logger\SolrLogger
arguments: ['@entity_type.manager', '@logger.log_message_parser'] arguments:
- '@config.factory'
- '@entity_type.manager'
- '@logger.log_message_parser'
tags: tags:
- { name: logger } - { name: logger }
- { name: backend_overridable } - { name: backend_overridable }
terminate_subscriber:
class: Drupal\solrlog\EventSubscriber\TerminateSubscriber
arguments: ['@logger.solr']
tags:
- { name: event_subscriber }
<?php
namespace Drupal\solrlog;
use Psr\Log\LoggerInterface;
/**
* Logger that processes log messages in a bulk.
*
* Normally, messages are dumped in the end of the request (react on
* Symfony\Component\HttpKernel\KernelEvents::TERMINATE).
*/
interface DelayedLoggerInterface extends LoggerInterface {
/**
* Dump the log que.
*/
public function dump() : void;
}
<?php
namespace Drupal\solrlog\EventSubscriber;
use Drupal\solrlog\DelayedLoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\TerminateEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Dump log messages to solr in the end of request.
*
* @package Drupal\solrlog\EventSubscriber
*/
class TerminateSubscriber implements EventSubscriberInterface {
/**
* TerminateSubscriber constructor.
*
* @param \Drupal\solrlog\DelayedLoggerInterface $delayedLogger
* The logger.
*/
public function __construct(
protected DelayedLoggerInterface $delayedLogger) {
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
return [
KernelEvents::TERMINATE => [
['onTerminate', 100],
],
];
}
/**
* Dump delayed log messages.
*
* @param \Symfony\Component\HttpKernel\Event\TerminateEvent $event
* The terminate request event.
*/
public function onTerminate(TerminateEvent $event) : void {
$this->delayedLogger->dump();
}
}
...@@ -69,6 +69,12 @@ class SolrSettingsForm extends ConfigFormBase { ...@@ -69,6 +69,12 @@ class SolrSettingsForm extends ConfigFormBase {
'#markup' => '<p class="messages--error">' . $this->t('No solr servers found.') . '</p>', '#markup' => '<p class="messages--error">' . $this->t('No solr servers found.') . '</p>',
]; ];
} }
$form['delayed_log'] = [
'#type' => 'checkbox',
'#title' => $this->t('Delayed log'),
'#description' => $this->t('Log messages in a bulk in the end of the request.'),
'#default_value' => $config->get('delayed_log'),
];
return $form; return $form;
} }
...@@ -77,7 +83,9 @@ class SolrSettingsForm extends ConfigFormBase { ...@@ -77,7 +83,9 @@ class SolrSettingsForm extends ConfigFormBase {
*/ */
public function submitForm(array &$form, FormStateInterface $form_state) { public function submitForm(array &$form, FormStateInterface $form_state) {
$config = $this->config('solrlog.settings'); $config = $this->config('solrlog.settings');
$config->set('server', $form_state->getValue('server'))->save(); $config->set('server', $form_state->getValue('server'))
->set('delayed_log', $form_state->getValue('delayed_log'))
->save();
parent::submitForm($form, $form_state); parent::submitForm($form, $form_state);
} }
......
...@@ -2,22 +2,24 @@ ...@@ -2,22 +2,24 @@
namespace Drupal\solrlog\Logger; namespace Drupal\solrlog\Logger;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Logger\LogMessageParserInterface; use Drupal\Core\Logger\LogMessageParserInterface;
use Drupal\Core\Logger\RfcLoggerTrait; use Drupal\Core\Logger\RfcLoggerTrait;
use Drupal\solrlog\DelayedLoggerInterface;
use Drupal\solrlog\SolariumTrait; use Drupal\solrlog\SolariumTrait;
use Psr\Log\LoggerInterface;
/** /**
* Logs events in Search API. * Logs events in Search API.
*/ */
class SolrLogger implements LoggerInterface { class SolrLogger implements DelayedLoggerInterface {
use RfcLoggerTrait; use RfcLoggerTrait;
use DependencySerializationTrait; use DependencySerializationTrait;
use SolariumTrait; use SolariumTrait;
protected array $delayedMessages = [];
protected bool $serviceFunctional = TRUE; protected bool $serviceFunctional = TRUE;
/** /**
...@@ -29,18 +31,26 @@ class SolrLogger implements LoggerInterface { ...@@ -29,18 +31,26 @@ class SolrLogger implements LoggerInterface {
*/ */
protected static int $runCount = 0; protected static int $runCount = 0;
protected bool $delayedLogging = FALSE;
/** /**
* SolrLogger constructor. * SolrLogger constructor.
* *
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* Configuration factory.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The entity type manager. * The entity type manager.
* @param \Drupal\Core\Logger\LogMessageParserInterface $parser * @param \Drupal\Core\Logger\LogMessageParserInterface $parser
* The parser to use when extracting message variables. * The parser to use when extracting message variables.
*/ */
public function __construct( public function __construct(
ConfigFactoryInterface $configFactory,
protected EntityTypeManagerInterface $entityTypeManager, protected EntityTypeManagerInterface $entityTypeManager,
protected LogMessageParserInterface $parser, protected LogMessageParserInterface $parser,
) {} ) {
$this->delayedLogging = (bool) $configFactory->get('solrlog.settings')
->get('delayed_log');
}
/** /**
* {@inheritdoc} * {@inheritdoc}
...@@ -52,14 +62,10 @@ class SolrLogger implements LoggerInterface { ...@@ -52,14 +62,10 @@ class SolrLogger implements LoggerInterface {
// Remove backtrace and exception since they may contain // Remove backtrace and exception since they may contain
// an unserializable variable. // an unserializable variable.
unset($context['exception']); unset($context['exception']);
$connector = $this->getConnector();
if (!$connector) {
return;
}
// Convert PSR3-style messages to \Drupal\Component\Render\FormattableMarkup // Convert PSR3-style messages to \Drupal\Component\Render\FormattableMarkup
// style, so they can be translated too in runtime. // style, so they can be translated too in runtime.
$message_placeholders = $this->parser->parseMessagePlaceholders($message, $context); $message_placeholders = $this->parser->parseMessagePlaceholders($message, $context);
$values = [ $log = [
'id' => \time() . (++self::$runCount), 'id' => \time() . (++self::$runCount),
// This helps to clear/filter the documents. // This helps to clear/filter the documents.
'ss_module_id' => 'solrlog', 'ss_module_id' => 'solrlog',
...@@ -74,8 +80,34 @@ class SolrLogger implements LoggerInterface { ...@@ -74,8 +80,34 @@ class SolrLogger implements LoggerInterface {
'ss_hostname' => mb_substr($context['ip'], 0, 128), 'ss_hostname' => mb_substr($context['ip'], 0, 128),
'dt_timestamp' => $context['timestamp'], 'dt_timestamp' => $context['timestamp'],
]; ];
if ($this->delayedLogging) {
$this->delayedMessages[] = $log;
return;
}
$this->logMultiple([$log]);
}
/**
* {@inheritDoc}
*/
public function dump(): void {
$this->logMultiple($this->delayedMessages);
$this->delayedMessages = [];
}
protected function logMultiple(array $values) {
if (empty($values)) {
return;
}
$connector = $this->getConnector();
if (!$connector) {
return;
}
$query = $connector->getUpdateQuery(); $query = $connector->getUpdateQuery();
$query->addDocument($query->createDocument($values))->addCommit(); foreach ($values as $value) {
$query->addDocument($query->createDocument($value));
}
$query->addCommit();
try { try {
$connector->update($query); $connector->update($query);
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
namespace Drupal\solrlog; namespace Drupal\solrlog;
use Drupal\search_api\SearchApiException;
use Drupal\search_api_solr\SolrBackendInterface; use Drupal\search_api_solr\SolrBackendInterface;
use Drupal\search_api_solr\SolrConnectorInterface; use Drupal\search_api_solr\SolrConnectorInterface;
...@@ -10,6 +11,11 @@ use Drupal\search_api_solr\SolrConnectorInterface; ...@@ -10,6 +11,11 @@ use Drupal\search_api_solr\SolrConnectorInterface;
*/ */
trait SolariumTrait { trait SolariumTrait {
/**
* @var \Drupal\search_api_solr\SolrConnectorInterface
*/
protected SolrConnectorInterface $solrConnector;
/** /**
* Array of Solr field names, keyed by Drupal field names. * Array of Solr field names, keyed by Drupal field names.
* *
...@@ -35,6 +41,9 @@ trait SolariumTrait { ...@@ -35,6 +41,9 @@ trait SolariumTrait {
* Solr connector. * Solr connector.
*/ */
protected function getConnector() : ?SolrConnectorInterface { protected function getConnector() : ?SolrConnectorInterface {
if (!empty($this->solrConnector)) {
return $this->solrConnector;
}
$server = \Drupal::config('solrlog.settings') $server = \Drupal::config('solrlog.settings')
->get('server'); ->get('server');
if (!$server) { if (!$server) {
...@@ -52,7 +61,13 @@ trait SolariumTrait { ...@@ -52,7 +61,13 @@ trait SolariumTrait {
catch (\Throwable) { catch (\Throwable) {
return NULL; return NULL;
} }
return $backend->getSolrConnector(); try {
return $backend->getSolrConnector();
}
catch (SearchApiException) {
$this->serviceFunctional = FALSE;
return NULL;
}
} }
/** /**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment