CloudWatch Logsで特定の文字列を検知したらLambda(node.js)でメール通知する


概要

CloudWatch Logsに出力しているサーバーとかアプリのログで、特定の文字列を見つけたらLambdaにそのログ行を渡してメール通知させる、というSyslogで例えればswatchみたいなことを実装してみた

方式・構成

  1. CloudWatch Logsのサブスクリプションフィルター機能を利用して特定の文字列にマッチしたらそのログをLambdaに渡す
  2. Lambdaは渡されたログデータを加工してAmazon SNSに渡す
  3. Amazon SNSは指定されたトピックからメールで管理者に通知する

作業の大まかな手順

  1. SNSトピックを設定する
  2. Lambda関数を作成する
  3. Lambda関数のロールにIAM権限を設定する
  4. Lambda関数のコードを作成・テストする
  5. CloudWatch Logsでサブスクリプションフィルターを設定する
  6. CloudWatch Logsにテスト用のログを流して動作を確認する

手順1: SNSトピックを設定する

既存のSNSトピックを使用するか、または新規作成する

新規作成の大まかな手順
1. トピックを作成する
1. トピックにEmailサブスクリプションを作成する
1. Emailサブスクリプションから届いた確認メールにConfirmする
1. 手動でトピックへのメッセージを発行してメールが届くことを確認する

手順2: Lambda関数を作成する

Lambda関数を作成する。ランタイムは今回はNode.js 14.xを使用するが、自分の好みや要件などで自由に選んで良い

手順3: Lambda関数のロールにIAM権限を設定する

SNSFullAccessを付与する

手順4: Lambda関数のコードを作成・テストする

  • 環境変数SNS_TOPIC_ARNに手順1で作成したSNSトピックのARNを設定する
  • 環境変数EMAIL_SUBJECTは送信する電子メールの件名を設定する
  • テストデータはテンプレートの中からAmazon CloudWatch Logsを使用する
  • テストしてみてメールが届けば良い
index.js
const aws  = require('aws-sdk');
const zlib = require('zlib');
const sns  = new aws.SNS();

const arn     = process.env.SNS_TOPIC_ARN;
const subject = process.env.EMAIL_SUBJECT;

exports.handler = async (event, context, callback) => {

    // CloudWatchLogsデータをデコード
    const payload     = Buffer.from(event.awslogs.data, 'base64');
    const unzipeddata = zlib.unzipSync(payload);  
    const logdata     = JSON.parse(unzipeddata.toString('utf8'));        
    const logs        = JSON.stringify(logdata,null,2);

    // メッセージ設定
    const params = {
        Message: logs, 
        Subject: subject,
        TopicArn: arn
    };

    // SNSに送信
    // https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/sns-examples-publishing-messages.html
    var publishTextPromise = sns.publish(params).promise();
    publishTextPromise.then(
        function(data) {
            console.log(`Message ${params.Message} sent to the topic ${params.TopicArn}`);
            console.log("MessageID is " + data.MessageId);
        }).catch(
            function(err) {
            console.error(err, err.stack);
        }
    );

    callback(null,logs);

};

手順5: CloudWatch Logsでサブスクリプションフィルターを設定する

  1. CloudWatchで目的のロググループを選択する
  2. Lambdaサブスクリプションフィルターを作成する。その際に以下を設定する
    • 前項で作成したLambda関数を指定する
    • サブスクリプションフィルターのパターンに、検知する文字列を指定する

手順6: CloudWatch Logsにテスト用のログを流して動作を確認する

  1. ログストリームにテスト用のログイベントを作成してメールが届くことを確かめる
  2. 特定の文字列を含まないログイベントも作成してみて、メールが届かないことも確かめる