Skip to content

Commit c210f8c

Browse files
committed
Merge branch 'release/0.2.0'
2 parents 33ad5ef + 4db7b6f commit c210f8c

File tree

8 files changed

+178
-31
lines changed

8 files changed

+178
-31
lines changed

.github/workflows/pr.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
runs-on: ubuntu-latest
2424
strategy:
2525
matrix:
26-
php-versions: [ '8.1' ]
26+
php-versions: [ '8.3' ]
2727
dependency-version: [ prefer-lowest, prefer-stable ]
2828
steps:
2929
- uses: actions/checkout@master
@@ -55,7 +55,7 @@ jobs:
5555
runs-on: ubuntu-latest
5656
strategy:
5757
matrix:
58-
php-versions: [ '8.1' ]
58+
php-versions: [ '8.3' ]
5959
steps:
6060
- uses: actions/checkout@master
6161
- name: Setup PHP, with composer and extensions
@@ -87,7 +87,7 @@ jobs:
8787
runs-on: ubuntu-latest
8888
strategy:
8989
matrix:
90-
php-versions: [ '8.1' ]
90+
php-versions: [ '8.3' ]
9191
steps:
9292
- uses: actions/checkout@master
9393
- name: Setup PHP, with composer and extensions

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
## [Unreleased]
1010

11+
## [0.2.0] - 2024-12-13
12+
13+
- Added queue logic to enhance performance.
14+
15+
## [0.1.2] - 2024-12-12
16+
1117
- Add new module to track user accessing webform submissions.
1218
- Added remote ip to all log lines.
1319

@@ -34,7 +40,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3440
- First version of the module
3541
- Added submodule to log user CUD events.
3642

37-
[Unreleased]: https://github.com/OS2web/os2web_audit/compare/0.1.1...HEAD
43+
[Unreleased]: https://github.com/OS2web/os2web_audit/compare/0.2.0...HEAD
44+
[0.2.0]: https://github.com/OS2web/os2web_audit/compare/0.1.2...0.2.0
45+
[0.1.2]: https://github.com/OS2web/os2web_audit/compare/0.1.1...0.1.2
3846
[0.1.1]: https://github.com/OS2web/os2web_audit/compare/0.1.0...0.1.1
3947
[0.1.0]: https://github.com/OS2web/os2web_audit/compare/0.0.3...0.1.0
4048
[0.0.3]: https://github.com/OS2web/os2web_audit/compare/0.0.2...0.0.3

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ These logging providers are designed using Drupal's plugin APIs. Consequently,
1212
it opens up possibilities for creating new AuditLogger plugins within other
1313
modules, thus enhancing the functionality of this audit logging.
1414

15+
For performance purposes we use a queue system. This avoids hindering
16+
performance more than necessary as the actual logging is done async. Furthermore,
17+
this allows for retries in case any audit log plugins should fail.
18+
1519
## Installation
1620

1721
Enable the module and go to the modules setting page at
@@ -47,3 +51,24 @@ logger as shown below:
4751
$msg = sprintf('Fetch personal data from service with parameter: %s', $param);
4852
$this->auditLogger->info('Lookup', $msg);
4953
```
54+
55+
### Queue
56+
57+
The actual logging is handled by jobs in an [Advanced
58+
Queue](https://www.drupal.org/project/advancedqueue) queue.
59+
60+
The queue, OS2Web audit (`os2web_audit`), must be
61+
processed by a server `cron` job, e.g.
62+
63+
```sh
64+
drush advancedqueue:queue:process os2web_audit
65+
```
66+
67+
List the queue (and all other queues) with
68+
69+
```sh
70+
drush advancedqueue:queue:list
71+
```
72+
73+
or go to `/admin/config/system/queues/jobs/os2web_audit` for a
74+
graphical overview of jobs in the queue.

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"require": {
1919
"ext-curl": "*",
2020
"php": "^8.1",
21+
"drupal/advancedqueue": "^1.2",
2122
"drush/drush": "^11.5|^12.5"
2223
},
2324
"require-dev": {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
status: true
2+
dependencies: { }
3+
id: os2web_audit
4+
label: os2web_audit
5+
backend: database
6+
backend_configuration:
7+
lease_time: 300
8+
processor: daemon
9+
processing_time: 90
10+
locked: false

os2web_audit.services.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ services:
55

66
os2web_audit.logger:
77
class: Drupal\os2web_audit\Service\Logger
8-
arguments: ['@plugin.manager.os2web_audit_logger', '@config.factory', '@current_user', '@logger.factory', '@request_stack']
8+
arguments: ['@plugin.manager.os2web_audit_logger', '@config.factory', '@current_user', '@logger.factory', '@request_stack', '@entity_type.manager']
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace Drupal\os2web_audit\Plugin\AdvancedQueue\JobType;
4+
5+
use Drupal\Component\Plugin\Exception\PluginException;
6+
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
7+
use Drupal\advancedqueue\Job;
8+
use Drupal\advancedqueue\JobResult;
9+
use Drupal\advancedqueue\Plugin\AdvancedQueue\JobType\JobTypeBase;
10+
use Drupal\os2web_audit\Exception\AuditException;
11+
use Drupal\os2web_audit\Exception\ConnectionException;
12+
use Drupal\os2web_audit\Service\Logger;
13+
use Symfony\Component\DependencyInjection\ContainerInterface;
14+
15+
/**
16+
* Log messages job.
17+
*
18+
* @AdvancedQueueJobType(
19+
* id = "Drupal\os2web_audit\Plugin\AdvancedQueue\JobType\LogMessages",
20+
* label = @Translation("Audit Log messages"),
21+
* )
22+
*/
23+
class LogMessages extends JobTypeBase implements ContainerFactoryPluginInterface {
24+
25+
/**
26+
* {@inheritdoc}
27+
*
28+
* @phpstan-param array<string, mixed> $configuration
29+
*/
30+
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
31+
return new static(
32+
$configuration,
33+
$plugin_id,
34+
$plugin_definition,
35+
$container->get('os2web_audit.logger'),
36+
);
37+
}
38+
39+
/**
40+
* {@inheritdoc}
41+
*
42+
* @phpstan-param array<string, mixed> $configuration
43+
*/
44+
public function __construct(
45+
array $configuration,
46+
$plugin_id,
47+
$plugin_definition,
48+
private readonly Logger $logger,
49+
) {
50+
parent::__construct($configuration, $plugin_id, $plugin_definition);
51+
}
52+
53+
/**
54+
* Processes the LogMessages job.
55+
*/
56+
public function process(Job $job): JobResult {
57+
$payload = $job->getPayload();
58+
59+
try {
60+
$this->logger->log($payload['type'], $payload['timestamp'], $payload['line'], $payload['plugin_id'], $payload['metadata']);
61+
62+
return JobResult::success();
63+
}
64+
catch (PluginException | ConnectionException | AuditException $e) {
65+
return JobResult::failure($e->getMessage());
66+
}
67+
}
68+
69+
}

src/Service/Logger.php

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
namespace Drupal\os2web_audit\Service;
44

5-
use Drupal\Component\Plugin\Exception\PluginException;
65
use Drupal\Core\Config\ConfigFactoryInterface;
6+
use Drupal\Core\Entity\EntityTypeManagerInterface;
77
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
88
use Drupal\Core\Session\AccountProxyInterface;
9-
use Drupal\os2web_audit\Exception\AuditException;
10-
use Drupal\os2web_audit\Exception\ConnectionException;
9+
use Drupal\advancedqueue\Job;
1110
use Drupal\os2web_audit\Form\PluginSettingsForm;
1211
use Drupal\os2web_audit\Form\SettingsForm;
12+
use Drupal\os2web_audit\Plugin\AdvancedQueue\JobType\LogMessages;
1313
use Drupal\os2web_audit\Plugin\LoggerManager;
1414
use Symfony\Component\HttpFoundation\RequestStack;
1515

@@ -20,12 +20,16 @@
2020
*/
2121
class Logger {
2222

23+
const string OS2WEB_AUDIT_QUEUE_ID = 'os2web_audit';
24+
const string OS2WEB_AUDIT_LOGGER_CHANNEL = 'os2web_audit_info';
25+
2326
public function __construct(
2427
private readonly LoggerManager $loggerManager,
2528
private readonly ConfigFactoryInterface $configFactory,
2629
private readonly AccountProxyInterface $currentUser,
2730
private readonly LoggerChannelFactoryInterface $watchdog,
2831
private readonly RequestStack $requestStack,
32+
private readonly EntityTypeManagerInterface $entityTypeManager,
2933
) {
3034
}
3135

@@ -43,7 +47,7 @@ public function __construct(
4347
* Additional metadata for the log message. Default is an empty array.
4448
*/
4549
public function info(string $type, string $line, bool $logUser = TRUE, array $metadata = []): void {
46-
$this->log($type, time(), $line, $logUser, $metadata + ['level' => 'info']);
50+
$this->createLoggingJob($type, time(), $line, $logUser, $metadata + ['level' => 'info']);
4751
}
4852

4953
/**
@@ -60,11 +64,11 @@ public function info(string $type, string $line, bool $logUser = TRUE, array $me
6064
* Additional metadata for the log message. Default is an empty array.
6165
*/
6266
public function error(string $type, string $line, bool $logUser = TRUE, array $metadata = []): void {
63-
$this->log($type, time(), $line, $logUser, $metadata + ['level' => 'error']);
67+
$this->createLoggingJob($type, time(), $line, $logUser, $metadata + ['level' => 'error']);
6468
}
6569

6670
/**
67-
* Logs a message using a plugin-specific logger.
71+
* Creates and enqueues logging job.
6872
*
6973
* @param string $type
7074
* The type of event to log (auth, lookup etc.)
@@ -78,11 +82,9 @@ public function error(string $type, string $line, bool $logUser = TRUE, array $m
7882
* @param array<string, string> $metadata
7983
* Additional metadata for the log message. Default is an empty array.
8084
*/
81-
private function log(string $type, int $timestamp, string $line, bool $logUser = FALSE, array $metadata = []): void {
82-
$config = $this->configFactory->get(SettingsForm::$configName);
83-
$plugin_id = $config->get('provider') ?? SettingsForm::OS2WEB_AUDIT_DEFUALT_PROVIDER;
84-
$configuration = $this->configFactory->get(PluginSettingsForm::getConfigName())->get($plugin_id);
85+
private function createLoggingJob(string $type, int $timestamp, string $line, bool $logUser = FALSE, array $metadata = []): void {
8586

87+
// Enhance logging data with current user and current request information.
8688
if ($logUser) {
8789
// Add user id to the log message metadata.
8890
$metadata['userId'] = $this->currentUser->getEmail();
@@ -95,25 +97,57 @@ private function log(string $type, int $timestamp, string $line, bool $logUser =
9597
$line .= sprintf(' Remote ip: %s', $ip_address);
9698
}
9799

100+
$config = $this->configFactory->get(SettingsForm::$configName);
101+
$plugin_id = $config->get('provider') ?? SettingsForm::OS2WEB_AUDIT_DEFUALT_PROVIDER;
102+
103+
$payload = [
104+
'type' => $type,
105+
'timestamp' => $timestamp,
106+
'line' => $line,
107+
'plugin_id' => $plugin_id,
108+
'metadata' => $metadata,
109+
];
110+
98111
try {
99-
/** @var \Drupal\os2web_audit\Plugin\AuditLogger\AuditLoggerInterface $logger */
100-
$logger = $this->loggerManager->createInstance($plugin_id, $configuration ?? []);
101-
$logger->log($type, $timestamp, $line, $metadata);
102-
}
103-
catch (PluginException $e) {
104-
$this->watchdog->get('os2web_audit')->error($e->getMessage());
112+
$queueStorage = $this->entityTypeManager->getStorage('advancedqueue_queue');
113+
/** @var \Drupal\advancedqueue\Entity\Queue $queue */
114+
$queue = $queueStorage->load(self::OS2WEB_AUDIT_QUEUE_ID);
115+
116+
$job = Job::create(LogMessages::class, $payload);
117+
118+
$queue->enqueueJob($job);
105119
}
106-
catch (AuditException | ConnectionException $e) {
107-
// Change metadata into string.
108-
$data = implode(', ', array_map(function ($key, $value) {
109-
return $key . " => " . $value;
110-
}, array_keys($metadata), $metadata));
111-
112-
// Fallback to send log message info watchdog.
113-
$msg = sprintf("Plugin: %s, Type: %s, Msg: %s, Metadata: %s", $e->getPluginName(), $type, $line, $data);
114-
$this->watchdog->get('os2web_audit')->info($msg);
115-
$this->watchdog->get('os2web_audit_error')->error($e->getMessage());
120+
catch (\Exception $exception) {
121+
$this->watchdog->get(self::OS2WEB_AUDIT_LOGGER_CHANNEL)->error(sprintf('Failed creating job: %s', $exception->getMessage()), $payload);
116122
}
123+
124+
}
125+
126+
/**
127+
* Logs a message using a plugin-specific logger.
128+
*
129+
* @param string $type
130+
* The type of event to log (auth, lookup etc.)
131+
* @param int $timestamp
132+
* The timestamp for the log message.
133+
* @param string $line
134+
* The log message.
135+
* @param string $plugin_id
136+
* The logging plugin id.
137+
* @param array<string, string> $metadata
138+
* Additional metadata for the log message. Default is an empty array.
139+
*
140+
* @throws \Drupal\Component\Plugin\Exception\PluginException
141+
* @throws \Drupal\os2web_audit\Exception\ConnectionException
142+
* @throws \Drupal\os2web_audit\Exception\AuditException
143+
*/
144+
public function log(string $type, int $timestamp, string $line, string $plugin_id, array $metadata = []): void {
145+
146+
$configuration = $this->configFactory->get(PluginSettingsForm::getConfigName())->get($plugin_id);
147+
148+
/** @var \Drupal\os2web_audit\Plugin\AuditLogger\AuditLoggerInterface $logger */
149+
$logger = $this->loggerManager->createInstance($plugin_id, $configuration ?? []);
150+
$logger->log($type, $timestamp, $line, $metadata);
117151
}
118152

119153
}

0 commit comments

Comments
 (0)