CloudWatch Logs を文字列検知してログ内容をメールを送信してみた サブスクリプションフィルター版

CloudWatch Logs のログから特定文字列をマッチさせ SNS から通知する方法を紹介します。
2020.06.08

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。
ご機嫌いかがでしょうか。
"No human labor is no human error" が大好きな吉井 亮です。

アプリケーションや OS のログファイルから特定の文字列をマッチし メール等で担当者へ通知する運用はクラウドにおいても必要です。

今回は CloudWatch Logs に出力しているログファイルから サブスクリプションを使用して特定文字列マッチを行い、SNS で通知する方法を試してみます。

2021年4月13日 更新
Lambda 関数の Python コードを修正しました。
以前のコードですと複数回イベントが発生した際に初回イベントしか通知されないと問題がありました。
ループ処理を加えることより複数回のイベントを取得できるように修正しました。
以前のコードをご利用頂いている方は大変お手数ですがコード修正をお願いします。

構成

ログの流れは以下の通りです。

  1. EC2 等から CloudWatch Logs へログファイル出力
  2. サブスクリプションフィルターで特定文字列をマッチさせて Lambda 関数を起動
  3. Lambda 関数で該当メッセージを抽出、SNS 用に整形
  4. 該当文字列が含まれた行を SNS で通知(今回はメール)

実装

それでは実装していきます。

メール通知用 SNS トピックの作成

このトピックはメール通知で使います。 SNS トピック を開きます。 トピックの作成 をクリックします。

[トピックの作成] 画面で任意の名前と表示名を入力し、画面下部の トピックの作成 をクリックします。

次の画面で サブスクリプションの作成 をクリックします。
プロトコルを Eメール に、エンドポイントに ご自身のメールアドレス を入力し、画面下部の サブスクリプションの作成 をクリックします。

しばらくすると AWS Notification - Subscription Confirmation という件名のメールが来ると思います。
本文をよく確認して Confirm を行ってください。

Lambda 関数の作成

Lambda 関数 を開きます。

関数の作成 をクリックします。

一から作成 を選択します。
任意の関数名を入力します。
ランタイムを選択します。今回は Python 3.7 を選択します。

関数の作成をクリックします。

コード

コードは以下を貼り付けます。

import base64
import json
import zlib
import datetime
import os
import boto3
from botocore.exceptions import ClientError

print('Loading function')


def lambda_handler(event, context):
    data = zlib.decompress(base64.b64decode(event['awslogs']['data']), 16+zlib.MAX_WBITS)
    data_json = json.loads(data)
    log_entire_json = json.loads(json.dumps(data_json["logEvents"], ensure_ascii=False))
    log_entire_len = len(log_entire_json)

    print(log_entire_json)

    for i in range(log_entire_len): 
        log_json = json.loads(json.dumps(data_json["logEvents"][i], ensure_ascii=False))

        try:
            sns = boto3.client('sns')
    
            #SNS Publish
            publishResponse = sns.publish(
                TopicArn = os.environ['SNS_TOPIC_ARN'],
                Message = log_json['message'],
                Subject = os.environ['ALARM_SUBJECT']
            )
    
        except Exception as e:
            print(e)

環境変数

環境変数に以下を入力します。

キー
SNS_TOPIC_ARN 前の手順で作成したメール通知用トピック ARN
ALARM_SUBJECT SNS で通知される際の件名

基本設定 -> タイムアウト

関数のタイムアウトを1分に設定します。
※仮に1分にしていますが実際の環境に合わせてチューニングしてください。

実行ロールにポリシーをアタッチ

アクセス権限 タブを開きます。
実行ロールという項目に IAM コンソールへのリンクが表示されています。
そちらをクリックします。

IAM ロールが表示されたら ポリシーをアタッチします をクリックします。

AmazonSNSFullAccess をアタッチします。
※手順を簡易化するために大きめの権限を付与しています。
※実際の運用では適切な権限設定をお願いします。

サブスクリプションフィルタの作成

マネジメントコンソールで CloudWatch Logs を開きます。

対象のロググループをクリックします。

アクションLambda サブスクリプションフィルターを作成 をクリックします。

次の画面で以下を入力します。

項目
Lambda 関数 前の手順で作成した関数
ログの形式 出力されるログに合わせた形式を選択
サブスクリプションフィルターのパターン 検出する文字列・パターンを入力

ストリーミングを開始 をクリックします。

テスト

CloudWatch Logs にテスト用のメッセージを送信してみます。

aws logs put-log-events --log-group-name your-log-group-name --log-stream-name your-log-stream-name --log-events timestamp=`date +%s%3N`,message="This is Error" --sequence-token your-sequence

SNS 経由でメールが届けば成功です。

まとめ

アプリケーションログからエラーを検出して通知するという手段は今でも有効です。
サブスクリプションフィルターだけでは柔軟性に欠けるといった場合は、Lambda 関数のなかでフィルタリングを行うことでカスタマイズが可能です。お試しください。

参考

サブスクリプションを使用したログデータのリアルタイム処理

以上、吉井 亮 がお届けしました。