From 1345bc2d0424ce2b9e16db5768ef4535805b6b36 Mon Sep 17 00:00:00 2001 From: Jesper Kristensen Date: Tue, 23 Apr 2024 15:58:29 +0200 Subject: [PATCH] ITKDev: Fixed sending data into loki --- .../os2forms_audit.services.yml | 2 +- .../src/Commands/AuditLogDrushCommands.php | 2 +- .../AuditLogger/AuditLoggerInterface.php | 4 +- .../src/Plugin/AuditLogger/File.php | 3 +- .../src/Plugin/AuditLogger/Loki.php | 27 ++++---- .../src/Plugin/AuditLogger/Watchdog.php | 8 ++- .../src/Plugin/Derivative/LocalTask.php | 2 +- modules/os2forms_audit/src/Service/Logger.php | 6 +- .../os2forms_audit/src/Service/LokiClient.php | 66 +++++++++---------- .../src/Service/LokiClientInterface.php | 40 +++++++++++ 10 files changed, 101 insertions(+), 59 deletions(-) create mode 100644 modules/os2forms_audit/src/Service/LokiClientInterface.php diff --git a/modules/os2forms_audit/os2forms_audit.services.yml b/modules/os2forms_audit/os2forms_audit.services.yml index b6b589e9..a1e94294 100644 --- a/modules/os2forms_audit/os2forms_audit.services.yml +++ b/modules/os2forms_audit/os2forms_audit.services.yml @@ -8,7 +8,7 @@ services: arguments: ['@plugin.manager.os2forms_audit_logger', '@config.factory'] os2forms_audit.commands: - class: Drupal\os2forms_audit\Commands\Os2FormsAuditDrushCommands + class: Drupal\os2forms_audit\Commands\AuditLogDrushCommands arguments: ['@os2forms_audit.logger'] tags: - { name: drush.command } diff --git a/modules/os2forms_audit/src/Commands/AuditLogDrushCommands.php b/modules/os2forms_audit/src/Commands/AuditLogDrushCommands.php index 133569ab..3a76bb9d 100644 --- a/modules/os2forms_audit/src/Commands/AuditLogDrushCommands.php +++ b/modules/os2forms_audit/src/Commands/AuditLogDrushCommands.php @@ -35,7 +35,7 @@ public function __construct( * @throws \Drupal\Component\Plugin\Exception\PluginException */ public function logMessage(string $log_message = ''): void { - $this->auditLogger->log(time(), $log_message, []); + $this->auditLogger->log('test', time(), $log_message, ['from' => 'drush']); } } diff --git a/modules/os2forms_audit/src/Plugin/AuditLogger/AuditLoggerInterface.php b/modules/os2forms_audit/src/Plugin/AuditLogger/AuditLoggerInterface.php index 579d0e56..ff3daedf 100644 --- a/modules/os2forms_audit/src/Plugin/AuditLogger/AuditLoggerInterface.php +++ b/modules/os2forms_audit/src/Plugin/AuditLogger/AuditLoggerInterface.php @@ -12,6 +12,8 @@ interface AuditLoggerInterface extends PluginInspectionInterface { /** * Logs a message with optional metadata. * + * @param string $type + * The type of event to log (auth, lookup etc.) * @param int $timestamp * The timestamp of the log entry. * @param string $line @@ -20,6 +22,6 @@ interface AuditLoggerInterface extends PluginInspectionInterface { * Additional metadata associated with the log entry. Defaults to an empty * array. */ - public function log(int $timestamp, string $line, array $metadata = []): void; + public function log(string $type, int $timestamp, string $line, array $metadata = []): void; } diff --git a/modules/os2forms_audit/src/Plugin/AuditLogger/File.php b/modules/os2forms_audit/src/Plugin/AuditLogger/File.php index c0227ffb..63cf96fe 100644 --- a/modules/os2forms_audit/src/Plugin/AuditLogger/File.php +++ b/modules/os2forms_audit/src/Plugin/AuditLogger/File.php @@ -26,12 +26,13 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition /** * {@inheritdoc} */ - public function log(int $timestamp, string $line, array $metadata = []): void { + public function log(string $type, int $timestamp, string $line, array $metadata = []): void { // Code to write the entity to a file. // This is just a placeholder and won't write the data. file_put_contents( $this->configuration['file'], json_encode([ + 'type' => $type, 'epoc' => $timestamp, 'line' => $line, 'metadata' => $metadata, diff --git a/modules/os2forms_audit/src/Plugin/AuditLogger/Loki.php b/modules/os2forms_audit/src/Plugin/AuditLogger/Loki.php index 986a98e8..6db8c908 100644 --- a/modules/os2forms_audit/src/Plugin/AuditLogger/Loki.php +++ b/modules/os2forms_audit/src/Plugin/AuditLogger/Loki.php @@ -6,6 +6,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\PluginBase; use Drupal\Core\Plugin\PluginFormInterface; +use Drupal\os2forms_audit\Service\LokiClient; /** * Stores entities in the database. @@ -18,6 +19,9 @@ */ class Loki extends PluginBase implements AuditLoggerInterface, PluginFormInterface, ConfigurableInterface { + /** + * {@inheritdoc} + */ public function __construct(array $configuration, $plugin_id, $plugin_definition) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->setConfiguration($configuration); @@ -25,9 +29,16 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition /** * {@inheritdoc} + * + * @throws \JsonException */ - public function log(int $timestamp, string $line, array $metadata = []): void { - // @todo use loki client to send message to loki + public function log(string $type, int $timestamp, string $line, array $metadata = []): void { + $client = new LokiClient([ + 'entrypoint' => $this->configuration['entrypoint'], + 'auth' => $this->configuration['auth'], + ]); + // Convert timestamp to nanoseconds. + $client->send($type, $timestamp * 1000000000, $line, $metadata); } /** @@ -70,13 +81,11 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta 'username' => [ '#type' => 'textfield', '#title' => $this->t('Username'), - '#required' => TRUE, '#default_value' => $this->configuration['auth']['username'], ], 'password' => [ '#type' => 'password', '#title' => $this->t('Password'), - '#required' => TRUE, '#default_value' => $this->configuration['auth']['password'], ], ]; @@ -101,16 +110,6 @@ public function validateConfigurationForm(array &$form, FormStateInterface $form $form_state->setErrorByName('entrypoint', $this->t('Invalid URL.')); } - // Validate auth username. - if (empty($values['auth']['username'])) { - $form_state->setErrorByName('auth][username', $this->t('Username is required.')); - } - - // Validate auth password. - if (empty($values['auth']['password'])) { - $form_state->setErrorByName('auth][password', $this->t('Password is required.')); - } - $curlOptions = array_filter(explode(',', $values['curl_options'])); foreach ($curlOptions as $option) { [$key] = explode(' =>', $option); diff --git a/modules/os2forms_audit/src/Plugin/AuditLogger/Watchdog.php b/modules/os2forms_audit/src/Plugin/AuditLogger/Watchdog.php index eb3b9f3e..f99e3a53 100644 --- a/modules/os2forms_audit/src/Plugin/AuditLogger/Watchdog.php +++ b/modules/os2forms_audit/src/Plugin/AuditLogger/Watchdog.php @@ -18,13 +18,17 @@ class Watchdog extends PluginBase implements AuditLoggerInterface { /** * {@inheritdoc} */ - public function log(int $timestamp, string $line, array $metadata = []): void { + public function log(string $type, int $timestamp, string $line, array $metadata = []): void { $data = ''; array_walk($metadata, function ($val, $key) use (&$data) { $data .= " $key=\"$val\""; }); - \Drupal::logger('os2forms_audit')->info($line . ' (%data)', ['data' => $data]); + \Drupal::logger('os2forms_audit')->info('%type: %line (%data)', [ + 'type' => $type, + 'line' => $line, + 'data' => $data, + ]); } } diff --git a/modules/os2forms_audit/src/Plugin/Derivative/LocalTask.php b/modules/os2forms_audit/src/Plugin/Derivative/LocalTask.php index b97ea5e0..855edd49 100644 --- a/modules/os2forms_audit/src/Plugin/Derivative/LocalTask.php +++ b/modules/os2forms_audit/src/Plugin/Derivative/LocalTask.php @@ -13,7 +13,7 @@ class LocalTask extends DeriverBase implements ContainerDeriverInterface { public function __construct( - private readonly LoggerManager $loggerManager + private readonly LoggerManager $loggerManager, ) { } diff --git a/modules/os2forms_audit/src/Service/Logger.php b/modules/os2forms_audit/src/Service/Logger.php index c4d9e940..97e2685a 100644 --- a/modules/os2forms_audit/src/Service/Logger.php +++ b/modules/os2forms_audit/src/Service/Logger.php @@ -23,6 +23,8 @@ public function __construct( /** * Logs a message using a plugin-specific logger. * + * @param string $type + * The type of event to log (auth, lookup etc.) * @param int $timestamp * The timestamp for the log message. * @param string $line @@ -32,14 +34,14 @@ public function __construct( * * @throws \Drupal\Component\Plugin\Exception\PluginException */ - public function log(int $timestamp, string $line, array $metadata = []): void { + public function log(string $type, int $timestamp, string $line, array $metadata = []): void { $config = $this->configFactory->get(SettingsForm::$configName); $plugin_id = $config->get('provider'); $configuration = $this->configFactory->get(PluginSettingsForm::getConfigName())->get($plugin_id); $logger = $this->loggerManager->createInstance($plugin_id, $configuration ?? []); - $logger->log($timestamp, $line, $metadata); + $logger->log($type, $timestamp, $line, $metadata); } } diff --git a/modules/os2forms_audit/src/Service/LokiClient.php b/modules/os2forms_audit/src/Service/LokiClient.php index fa6b5da5..5fa56482 100644 --- a/modules/os2forms_audit/src/Service/LokiClient.php +++ b/modules/os2forms_audit/src/Service/LokiClient.php @@ -7,7 +7,7 @@ * * This is based/inspired by https://github.com/itspire/monolog-loki. */ -class LokiClient { +class LokiClient implements LokiClientInterface { /** * Location of the loki entry point. @@ -49,52 +49,34 @@ public function __construct( $this->entrypoint = $this->getEntrypoint($apiConfig['entrypoint']); $this->customCurlOptions = $apiConfig['curl_options'] ?? []; - if (isset($apiConfig['auth']['basic'])) { - $this->basicAuth = (2 === count($apiConfig['auth']['basic'])) ? $apiConfig['auth']['basic'] : []; + if (isset($apiConfig['auth']) && !empty($apiConfig['auth']['username']) && !empty($apiConfig['auth']['password'])) { + $this->basicAuth = $apiConfig['auth']; } } /** - * Send a log message to Loki ingestion endpoint. - * - * Message format sendt to loki in the following json format. - * { - * "Streams": [ - * { - * "stream": { - * "label": "value" - * }, - * "values": [ - * [ "", "", ] - * ] - * } - * ] - * } - * - * @param string $label - * Loki global label to use. - * @param int $epoch - * Unix epoch in nanoseconds. - * @param string $line - * The log line to send. - * @param array $metadata - * Structured metadata. - * - * @see https://grafana.com/docs/loki/latest/reference/api/#ingest-logs + * {@inheritdoc} * * @throws \JsonException */ public function send(string $label, int $epoch, string $line, array $metadata = []): void { - $this->sendPacket([ - 'streams' => [ + $packet = [ + 'streams' => [[ 'stream' => [ - 'label' => $label, + 'app' => 'os2forms', + 'type' => $label, ], 'values' => [ - [$epoch, $line, $metadata], + [(string) $epoch, $line], ], - ], - ]); + ]], + ]; + + if (!empty($metadata)) { + $packet['streams'][0]['stream'] += $metadata; + } + + $this->sendPacket($packet); } /** @@ -159,7 +141,19 @@ private function sendPacket(array $packet): void { } curl_setopt_array($this->connection, $curlOptions); - curl_exec($this->connection); + $result = curl_exec($this->connection); + + if (FALSE === $result){ + throw new \RuntimeException('Error sending packet to Loki'); + } + + if (curl_errno($this->connection)) { + // If an error occurred, output it + echo 'Curl error: ' . curl_error($this->connection); + } else { + // No error occurred, print out the result of the request + echo 'Curl result: ' . $result; + } } } diff --git a/modules/os2forms_audit/src/Service/LokiClientInterface.php b/modules/os2forms_audit/src/Service/LokiClientInterface.php new file mode 100644 index 00000000..eb2b269e --- /dev/null +++ b/modules/os2forms_audit/src/Service/LokiClientInterface.php @@ -0,0 +1,40 @@ +", ""] + * ] + * } + * ] + * } + * + * @param string $label + * Loki global label to use. + * @param int $epoch + * Unix epoch in nanoseconds. + * @param string $line + * The log line to send. + * @param array $metadata + * Extra labels/metadata to filter on. + * + * @see https://grafana.com/docs/loki/latest/reference/api/#ingest-logs + */ + public function send(string $label, int $epoch, string $line, array $metadata = []): void; + +}