Skip to content

Commit

Permalink
ITKDev: Added exception hanling
Browse files Browse the repository at this point in the history
  • Loading branch information
cableman committed Apr 26, 2024
1 parent d016055 commit 3a51f97
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 50 deletions.
25 changes: 25 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#### Link to ticket

Please add a link to the ticket being addressed by this change.

#### Description

Please include a short description of the suggested change and the reasoning behind the approach you have chosen.

#### Screenshot of the result

If your change affects the user interface you should include a screenshot of the result with the pull request.

#### Checklist

- [ ] My code is covered by test cases.
- [ ] My code passes our test (all our tests).
- [ ] My code passes our static analysis suite.
- [ ] My code passes our continuous integration process.

If your code does not pass all the requirements on the checklist you have to add a comment explaining why this change
should be exempt from the list.

#### Additional comments or questions

If you have any further comments or questions for the reviewer please add them here.
1 change: 1 addition & 0 deletions os2web_audit.info.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ name: OS2web Audit
type: module
description: 'OS2web Audit Module (log events in the system)'
core_version_requirement: ^8 || ^9 || ^10
configure: os2web_audit.plugin_settings_local_tasks
6 changes: 0 additions & 6 deletions os2web_audit.module

This file was deleted.

2 changes: 1 addition & 1 deletion os2web_audit.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ services:

os2web_audit.logger:
class: Drupal\os2web_audit\Service\Logger
arguments: ['@plugin.manager.os2web_audit_logger', '@config.factory']
arguments: ['@plugin.manager.os2web_audit_logger', '@config.factory', '@current_user', '@logger.factory']

os2web_audit.commands:
class: Drupal\os2web_audit\Commands\AuditLogDrushCommands
Expand Down
4 changes: 2 additions & 2 deletions src/Commands/AuditLogDrushCommands.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public function logMessage(string $log_message = ''): void {
if (empty($log_message)) {
throw new \Exception('Log message cannot be empty.');
}
$this->auditLogger->info('test', time(), $log_message, FALSE, ['from' => 'drush']);
$this->auditLogger->error('test', time(), $log_message, TRUE, ['from' => 'drush']);
$this->auditLogger->info('test', $log_message, FALSE, ['from' => 'drush']);
$this->auditLogger->error('test', $log_message, TRUE, ['from' => 'drush']);
}

}
37 changes: 37 additions & 0 deletions src/Exception/AuditException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Drupal\os2web_audit\Exception;

/**
* Class AuditException.
*
* Base exception for auditing provider plugins.
*/
class AuditException extends \Exception {

/**
* The name of the plugin-.
*
* @var string
*/
private string $pluginName = 'Unknown plugin';

public function __construct(string $message = "", int $code = 0, \Throwable $previous = NULL, ?string $pluginName = NULL) {
parent::__construct($message, $code, $previous);

if (isset($pluginName)) {
$this->pluginName = $pluginName;
}
}

/**
* Name of the plugin that started the exception.
*
* @return string
* Name of the plugin if given else "Unknown plugin".
*/
public function getPluginName(): string {
return $this->pluginName;
}

}
11 changes: 11 additions & 0 deletions src/Exception/ConnectionException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Drupal\os2web_audit\Exception;

/**
* Class ConnectionException.
*
* Plugin connection exception in auditing provider plugins.
*/
class ConnectionException extends AuditException {
}
9 changes: 0 additions & 9 deletions src/Form/SettingsForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,6 @@ public function buildForm(array $form, FormStateInterface $form_state): array {
'#default_value' => $config->get('provider'),
];

$form['fallback'] = [
'#type' => 'select',
'#title' => $this->t('Fallback Log provider'),
'#description' => $this->t('Select the logger provider you which to use, if the main provider fails'),
'#options' => $options,
'#default_value' => $config->get('fallback'),
];

return parent::buildForm($form, $form_state);
}

Expand All @@ -97,7 +89,6 @@ public function submitForm(array &$form, FormStateInterface $form_state): void {

$this->config(self::$configName)
->set('provider', $form_state->getValue('provider'))
->set('fallback', $form_state->getValue('fallback'))
->save();
}

Expand Down
5 changes: 5 additions & 0 deletions src/Plugin/AuditLogger/AuditLoggerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ interface AuditLoggerInterface extends PluginInspectionInterface {
* @param array<string, string> $metadata
* Additional metadata associated with the log entry. Defaults to an empty
* array.
*
* @throws \Drupal\os2web_audit\Exception\ConnectionException
* If unable to connect to the Loki endpoint.
* @throws \Drupal\os2web_audit\Exception\AuditException
* Errors in logging the packet.
*/
public function log(string $type, int $timestamp, string $line, array $metadata = []): void;

Expand Down
5 changes: 4 additions & 1 deletion src/Plugin/AuditLogger/Loki.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
/**
* {@inheritdoc}
*
* @throws \JsonException
* @throws \Drupal\os2web_audit\Exception\ConnectionException
* If unable to connect to the Loki endpoint.
* @throws \Drupal\os2web_audit\Exception\AuditException
* Errors in logging the packet.
*/
public function log(string $type, int $timestamp, string $line, array $metadata = []): void {
$client = new LokiClient([
Expand Down
47 changes: 28 additions & 19 deletions src/Service/Logger.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

namespace Drupal\os2web_audit\Service;

use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\os2web_audit\Exception\AuditException;
use Drupal\os2web_audit\Exception\ConnectionException;
use Drupal\os2web_audit\Form\PluginSettingsForm;
use Drupal\os2web_audit\Form\SettingsForm;
use Drupal\os2web_audit\Plugin\LoggerManager;
Expand All @@ -19,6 +23,7 @@ public function __construct(
private readonly LoggerManager $loggerManager,
private readonly ConfigFactoryInterface $configFactory,
private readonly AccountProxyInterface $currentUser,
private readonly LoggerChannelFactoryInterface $watchdog,
) {
}

Expand All @@ -27,41 +32,33 @@ public function __construct(
*
* @param string $type
* The type of event to log (auth, lookup etc.)
* @param int $timestamp
* The timestamp for the log message.
* @param string $line
* The log message.
* @param bool $logUser
* Log information about the current logged-in user (need to track who has
* lookup information in external services). Default: false.
* @param array<string, string> $metadata
* Additional metadata for the log message. Default is an empty array.
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
*/
public function info(string $type, int $timestamp, string $line, bool $logUser = FALSE, array $metadata = []): void {
$this->log($type, $timestamp, $line, $logUser, $metadata + ['level' => 'info']);
public function info(string $type, string $line, bool $logUser = TRUE, array $metadata = []): void {
$this->log($type, time(), $line, $logUser, $metadata + ['level' => 'info']);
}

/**
* Logs a message at error level.
*
* @param string $type
* The type of event to log (auth, lookup etc.)
* @param int $timestamp
* The timestamp for the log message.
* @param string $line
* The log message.
* @param bool $logUser
* Log information about the current logged-in user (need to track who has
* lookup information in external services). Default: false.
* @param array<string, string> $metadata
* Additional metadata for the log message. Default is an empty array.
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
*/
public function error(string $type, int $timestamp, string $line, bool $logUser = FALSE, array $metadata = []): void {
$this->log($type, $timestamp, $line, $logUser, $metadata + ['level' => 'error']);
public function error(string $type, string $line, bool $logUser = TRUE, array $metadata = []): void {
$this->log($type, time(), $line, $logUser, $metadata + ['level' => 'error']);
}

/**
Expand All @@ -78,24 +75,36 @@ public function error(string $type, int $timestamp, string $line, bool $logUser
* lookup information in external services). Default: false.
* @param array<string, string> $metadata
* Additional metadata for the log message. Default is an empty array.
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
*/
private function log(string $type, int $timestamp, string $line, bool $logUser = FALSE, array $metadata = []): void {
$config = $this->configFactory->get(SettingsForm::$configName);
$plugin_id = $config->get('provider');

// @todo default logger (file)
// @todo Fallback logger on error.
$configuration = $this->configFactory->get(PluginSettingsForm::getConfigName())->get($plugin_id);
$logger = $this->loggerManager->createInstance($plugin_id, $configuration ?? []);

if ($logUser) {
// Add user id to the log message metadata.
$metadata['userId'] = $this->currentUser->id();
}

$logger->log($type, $timestamp, $line, $metadata);
try {
/** @var \Drupal\os2web_audit\Plugin\AuditLogger\AuditLoggerInterface $logger */
$logger = $this->loggerManager->createInstance($plugin_id, $configuration ?? []);
$logger->log($type, $timestamp, $line, $metadata);
}
catch (PluginException $e) {
$this->watchdog->get('os2web_audit')->error($e->getMessage());
}
catch (AuditException | ConnectionException $e) {
// Change metadata into string.
$data = implode(', ', array_map(function ($key, $value) {
return $key . " => " . $value;
}, array_keys($metadata), $metadata));

// Fallback to send log message info watchdog.
$msg = sprintf("Plugin: %s, Type: %s, Msg: %s, Metadata: %s", $e->getPluginName(), $type, $line, $data);
$this->watchdog->get('os2web_audit')->info($msg);
$this->watchdog->get('os2web_audit_error')->error($e->getMessage());
}
}

}
46 changes: 34 additions & 12 deletions src/Service/LokiClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Drupal\os2web_audit\Service;

use Drupal\os2web_audit\Exception\AuditException;
use Drupal\os2web_audit\Exception\ConnectionException;

/**
* Class LokiClient.
*
Expand Down Expand Up @@ -57,7 +60,10 @@ public function __construct(
/**
* {@inheritdoc}
*
* @throws \JsonException
* @throws \Drupal\os2web_audit\Exception\ConnectionException
* If unable to connect to the Loki endpoint.
* @throws \Drupal\os2web_audit\Exception\AuditException
* Errors in logging the packet.
*/
public function send(string $label, int $epoch, string $line, array $metadata = []): void {
$packet = [
Expand Down Expand Up @@ -103,20 +109,32 @@ private function getEntrypoint(string $entrypoint): string {
* @param array<string, mixed> $packet
* The packet to send.
*
* @throws \JsonException
* If unable to encode the packet to JSON.
* @throws \LogicException
* @throws \Drupal\os2web_audit\Exception\ConnectionException
* If unable to connect to the Loki endpoint.
* @throws \Drupal\os2web_audit\Exception\AuditException
* Errors in logging the packet.
*/
private function sendPacket(array $packet): void {
$payload = json_encode($packet, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$url = sprintf('%s/loki/api/v1/push', $this->entrypoint);
try {
$payload = json_encode($packet, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
catch (\JsonException $e) {
throw new AuditException(
message: 'Payload could not be encoded.',
previous: $e,
pluginName: 'Loki',
);
}

if (NULL === $this->connection) {
$url = sprintf('%s/loki/api/v1/push', $this->entrypoint);
$this->connection = curl_init($url);

if (FALSE === $this->connection) {
throw new \LogicException('Unable to connect to ' . $url);
throw new ConnectionException(
message: 'Unable to connect to ' . $url,
pluginName: 'Loki',
);
}
}

Expand Down Expand Up @@ -145,14 +163,18 @@ private function sendPacket(array $packet): void {
$result = curl_exec($this->connection);

if (FALSE === $result) {
throw new \RuntimeException('Error sending packet to Loki');
throw new ConnectionException(
message: 'Error sending packet to Loki',
pluginName: 'Loki',
);
}

if (curl_errno($this->connection)) {
echo 'Curl error: ' . curl_error($this->connection);
}
else {
echo 'Curl result: ' . $result;
throw new AuditException(
message: curl_error($this->connection),
code: curl_errno($this->connection),
pluginName: 'Loki',
);
}
}
}
Expand Down

0 comments on commit 3a51f97

Please sign in to comment.