Recevoir des logs de AWS CloudWatch dans un canal Slack

Logs Cloudwatch dans Slack

Billet ultra-geek aujourd’hui, sorry ;).

L’infra d’illuxiApp est entièrement chez AWS. Nous y sommes depuis quelques années déjà, mais grâce à la gang de chez Unicorne, nous avons complètement rebâti le tout, from scratch. De la bonne façon cette fois :). Et c’est frickin cool!

On utilise CloudWatch évidemment pour centraliser l’ensemble des logs. illuxiApp y envoie aussi ses propres logs. Nous avons des alarmes pour nous aviser de certains éléments, mais on voulait certains logs en temps réel. Par exemple, les erreurs ;).

En nous inspirant de ce post, nous avons mis en place une fonction Lambda qui envoie un message dans un canal Slack chaque fois qu’un événement de log correspond à certains critères.

Voici les étapes.

Création d’un webhook Slack

Dans Slack, dirigez-vous vers Outils et paramètres / Gérer les applications:

Recherchez « webhook » et cliquez sur Webhooks entrants:

Cliquez sur Ajoutez à Slack:

Sélectionnez le canal dans lequel vous souhaitez publier les messages et cliquez sur Ajouter l’intégration:

Personnalisez les paramètres au besoin et copiez l’URl du Webhook:

Création d’une fonction Lambda

Vous êtes maintenant prêt à créer la fonction Lambda qui enverra les logs vers votre nouveau webhook Slack.

Dans le service Lambda de la console AWS, créez une nouvelle Function:

Dans le fichier index.php de la fenêtre de code, coller le code disponible plus bas:

Voici le code en question:

const zlib = require("zlib");
const https = require("https");
const SLACK_ENDPOINT =
  "/services/xxxxxxxxxxxxzzzz"; // À remplacer par l'URL de votre Webhook 
const SLACK_BOT = "Cloudwatch"; // Le nom du bot qui sera affiché

function doRequest(content) {
  
  const obj = JSON.parse(content.message);

  // formatting the message according Slack API
  const payload = {
    username: SLACK_BOT,
    blocks: [
      {
        "type": "rich_text",
        "elements": [
          {
            "type": "rich_text_preformatted",
            "elements": [
              {
                "type": "text",
                "text": JSON.stringify(obj, null, 4)
              },
            ],
            "border": 0
          }
        ]
      }
    ],
  };

  const payloadStr = JSON.stringify(payload);
  const options = {
    hostname: "hooks.slack.com",
    port: 443,
    path: SLACK_ENDPOINT,
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Content-Length": Buffer.byteLength(payloadStr),
    },
  };

  const postReq = https.request(options, function (res) {
    const chunks = [];
    res.setEncoding("utf8");
    res.on("data", function (chunk) {
      return chunks.push(chunk);
    });
    res.on("end", function () {
      if (res.statusCode < 400) {
        console.log("sent!!!");
      } else if (res.statusCode < 500) {
        console.error(
          "Error posting message to Slack API: " +
            res.statusCode +
            " - " +
            res.statusMessage
        );
      } else {
        console.error(
          "Server error when processing message: " +
            res.statusCode +
            " - " +
            res.statusMessage
        );
      }
    });
    return res;
  });
  postReq.write(payloadStr);
  postReq.end();
}

function main(event, context) {
  context.callbackWaitsForEmptyEventLoop = true;
  // always returns the last event
  const payload = Buffer.from(event.awslogs.data, "base64");
  const log = JSON.parse(zlib.gunzipSync(payload).toString("utf8"));
  // the log is an object that contains an array of events called `logEvents` and we need access it bypassing the index 0
  doRequest(log.logEvents[0]);
  const response = {
    statusCode: 200,
    body: JSON.stringify("Event sent to Slack!"),
  };
  return response;
}

exports.handler = main;

Évidemment, vous devrez modifie la valeur de la constante SLACK_ENDPOINT pour y placer l’URL de votre webhook.

Cliquez ensuite sur Deploy pour déployer la fonction.

Création du déclencheur (trigger)

Dans le haut de la page, cliquez sur + Add trigger.

Dans la source, sélectionnez Cloudwatch Logs:

Sélectionnez ensuite le Log Group qui contient les logs que vous voulez envoyer vers Slack, donnez un nom à votre déclencheur et ajoutez un Filter pattern si vous le souhaitez:

Le Filter pattern « ?Error ?error ?ERROR » fera en sorte que seulement les événements de logs qui contiennent Error, error ou ERROR déclencheront votre fonction Lambda.

Cliquez sur Add pour ajouter le déclencheur.

Test

Pour tester, rendez-vous dans le log group en question et sélectionnez le plus récent log stream:

Cliquez ensuite sur Actions / Create log event:

Ajoutez, par exemple, ce log:

{
    "message": "Severity: Notice --> Trying to access array offset on value of type null /file... line...",
    "context": {
        "url": "https://your-url.com",
        "ip": "11.1.11.111"
    },
    "level": 400,
    "level_name": "ERROR",
    "channel": "illuxi-logger-staging",
    "datetime": "2024-09-07T19:40:48.383092+00:00",
    "extra": {}
}

Et voilà!

Commentaires

Laisser un commentaire

D'autres billets qui pourraient vous intéresser

IA remplacera les développeurs, vraiment?

L’IA va tuer les développeurs. Vraiment?

Cette semaine, je lisais un article à propos de la « SaaSpocalypse« , l’idée selon laquelle l’IA générative permettra aux entreprises de ...
Obsidian: Backup et synchronisation sur plusieurs appareils

Obsidian: Backup et synchronisation sur plusieurs appareils

Je prends souvent des notes. Il me semble que j’ai toujours quelque chose à écrire. Une idée, quelque chose à ...
Ce n'est jamais juste un mot de passe

Ce n’est jamais « juste » un mot de passe

Vous souvenez-vous de cette époque où un mot de passe de quatre lettres suffisait? Pas de majuscule. Pas de chiffre. ...
Quand on écoutait des albums pas des chansons

Quand on écoutait encore des albums

Avant Spotify, avant Apple Music, avant YouTube Music, on écoutait des albums. Pas des chansons. On insérait la cassette, le ...

« Qui a tué les Expos » m’a rappelé pourquoi certains souvenirs ne meurent jamais

Je viens de terminer Qui a tué les Expos sur Netflix. Ouf. Je ne m’attendais pas à toutes ces émotions… ...
Extension pour navigateur Obsidian Web Clipper

Comment transformer n’importe quelle page web en note Obsidian

Le Web Clipper d’Obsidian est une extension de navigateur qui permet, en un clic, de capturer le contenu d’une page ...