Scrivere i log prodotti da Drupal 8 nello stream di Amazon CloudWatch


L’attività di logging se ben concepita può essere una miniera di informazioni per il debug di errori e per tenere traccia di quello che succede nell’applicazione, per capire cosa è andato storto o anche solo per monitorare l’attività degli utenti che ha indotto all’errore.

Se avete dimestichezza con Symfony avrete sicuramente imparato ad amare la versatilità di Monolog, scritto dallo stesso sviluppatore che ha inventato Composer.
Monolog supporta canali (Channels) di log differenti, ciascuno dei quali associati a degli Handler che sono in grado di scrivere un messaggio di log sulle destinazioni più disparate, da un semplice database passando per Syslog fino a soluzioni più evolute come postare un messaggio su Slack o Logstash.

Utilizzando un handler di Monolog possiamo interfacciarci con Amazon CloudWatch che è un servizio di monitoraggio per le risorse cloud AWS e le applicazioni in esecuzione su AWS. Si può utilizzare Amazon CloudWatch per raccogliere e monitorare parametri e file di log, impostare allarmi e reagire automaticamente ai cambiamenti nelle risorse AWS.

Quindi andiamo per ordine:

  • per poter scrivere i nostri log dentro la piattaforma CloudWatch abbiamo bisogno di installare il wrapper di Monolog per Drupal 8
    composer require drupal/monolog:^1.0
  • sucessivamente dovremo installare l’handler specifico per CloudWatch
    composer require maxbanton/cwh:^1.0
  • nella stessa directory del file settings.php del sito dovremo creare uno specifico file per il servizio (monolog.services.yml per esempio) e poi aggiungere questa riga di codice nel file settings.php stesso:
    $settings['container_yamls'][] = 'sites/default/monolog.services.yml';

Ora abbiamo due possibili strade, la cui scelta dipende solo dalle specifiche esigenze del progetto.

Tutti i channel vengono scritti in un unico stream

Nel file sites/default/monolog.services.yml mettiamo queste impostazioni sostituendo i parametri che iniziano con my_ che sono solamente dei segnaposto.

--- 
parameters: 
  monolog.channel_handlers: 
    default: 
      - cloudwatch_handler

  monolog.processors: 
    - message_placeholder
    - current_user
    - request_uri
    - ip
    - referer

services: 
  cloudwatch_client: 
    class: Aws\CloudWatchLogs\CloudWatchLogsClient
    arguments: 
      - 
        credentials: 
          key: my_amazon_accesskey
          secret: my_amazon_secretkey
        region: my_amazon_region
        version: latest

  cloudwatch_handler: 
    class: Maxbanton\Cwh\Handler\CloudWatch
    arguments: 
      - "@cloudwatch_client"
      - my_group_name
      - my_stream_name
      - 30
      - 10000
      - 
        mytag: tag
      - DEBUG

Ogni channel di Monolog verrà scritto in uno specifico stream

Se vogliamo fare in modo che ogni channel, magari per una migliore leggibilità dei log, venga scritto in un proprio stream dobbiamo prima creare un handler custom che possiamo mettere in un modulo creato appositamente oppure in un altro modulo custom. Occhio solo a mettere il namespace corretto.

<?php

namespace Drupal\my_custom_module\Logger\Handler;

use Aws\CloudWatchLogs\CloudWatchLogsClient;
use Maxbanton\Cwh\Handler\CloudWatch;
use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Logger;

/**
 * Class CloudWatchHandler
 *
 * @package Drupal\my_custom_module\Logger\Handler
 */
class CloudWatchHandler extends AbstractProcessingHandler {

  /**
   * CloudWatch constructor.
   */
  public function __construct() {
    parent::__construct(Logger::DEBUG, TRUE);
  }

  /**
   * Writes the record down to the log of the implementing handler
   *
   * @param  array $record
   *
   * @return void
   */
  protected function write(array $record) {

    $sdkParams = [
      'region' => "my_amazon_region",
      'version' => "latest",
      'credentials' => [
        'key' => "my_amazon_key",
        'secret' => "my_amazon_secret",
      ],
    ];

    $client = new CloudWatchLogsClient($sdkParams);

    $group = "my_group_name";

    if (empty($record['channel'])) {
      $stream = "default";
    }
    else {
      $stream = $record['channel'];
    }

    /** @var  \Maxbanton\Cwh\Handler\CloudWatch $handler */
    $handler = new CloudWatch($client, $group, $stream, 30, 10000, [], Logger::DEBUG);

    $handler->write($record);
  }

}

A questo punto nel nostro file sites/default/monolog.services.yml mettiamo le configurazioni:

--- 
parameters: 
  monolog.channel_handlers: 
    default: 
      - cloudwatch_handler

  monolog.processors: 
    - message_placeholder
    - current_user
    - request_uri
    - ip
    - referer

services: 
  cloudwatch_handler: 
    class: Drupal\my_custom_module\Logger\Handler\CloudWatchHandler   
    #Modificare con il namespace corretto

Come si può notare il servizio è molto spartano, ma può essere ampliato iniettando come argomento magari \Drupal\Core\Config\ConfigFactoryInterface $config_factory per caricare una configurazione oppure \Drupal\Core\Entity\EntityTypeManager $entity_type_manager per interfacciarsi con il repository di un’entità.

Rispondi

This site uses Akismet to reduce spam. Learn how your comment data is processed.