表題の件、以下の過去記事に書いたが、この時点では送信される本文ががログメッセージだけとなっていて、通知メールとしては不十分なため本文もカスタマイズしてみた。
CloudWatchLogsのログ監視 - サブスクリプションフィルタ + Lambdaでメール送信

 

各種設定は冒頭の過去記事と同様のため割愛するとして、コードは変更前・後両方載せておく。

変更前:lambda_function.py(1)

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)

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

 

変更後:lambda_function.py(2)

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_group = data_json['logGroup']
        
        # ログストリーム名取得
        log_stm = data_json['logStream']
        
        # LogEvents取得
        log_json = json.loads(json.dumps(data_json["logEvents"][i], ensure_ascii=False))
        
        #UNIX時間→時刻/JST変換
        datetime_utc = log_json['timestamp'] / 1000.0
        datetime_utc = datetime.datetime.fromtimestamp(datetime_utc).strftime('%Y/%m/%d %H:%M:%S')
        datetime_utc = datetime.datetime.strptime(datetime_utc, '%Y/%m/%d %H:%M:%S')
        datetime_jst = datetime_utc + datetime.timedelta(hours = 9)
        
        # メール件名整形
        subject_str = "本番環境 - ログアラート " + log_group
        
        # メッセージ本文整形
        fix_msg = "以下のアラートが発生しました" + "\n"
        log_group_msg = "ロググループ名:" + "\n" + log_group
        log_stm_msg = "ログストリーム名:" + "\n" + log_stm
        time_msg = "発生時刻:" + "\n" + str(datetime_jst)
        log_msg = "メッセージ:" + "\n" + log_json['message']
        msg = fix_msg + "\n\n" + log_group_msg + "\n\n" + log_stm_msg + "\n\n" + time_msg + "\n\n" + log_msg

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

参考
【AWS】CloudWatch Logsからシステムログをメール通知する。

 

改良後のコードでは本文整形の他、メール件名を環境変数決め打ちからコード内でeventの情報から取得、に変更している。ログ種別は多義に渡るしログ監視以外のメール送信もあるから、動的に対応できる方が望ましいということで。本文冒頭に決め打ちメッセージ追加と変数名を変えたくらいで基本は参考元とほぼ同じ。タイムスタンプ変換まで書いてもらって非常にありがたい。

 

最終的に、送信されたメールはこんな感じ。

ログ監視メールサンプル

 

参考サイトさんのおかげですんなりできて助かった。(2度目)ちなみに、当初自力で応用しようとしてハマってた。Logs(サブスクリプションフィルタ)からLambdaに渡されるデータ構造が不明だったからだ。しかし以下公式にちゃんとサンプルと説明があったということを、最後に知った。

CloudWatch Logs サブスクリプションフィルターの使用

 

以下、データサンプルの抜粋

{
    "owner": "111111111111",
    "logGroup": "CloudTrail",
    "logStream": "111111111111_CloudTrail_us-east-1",
    "subscriptionFilters": [
        "Destination"
    ],
    "messageType": "DATA_MESSAGE",
    "logEvents": [
        {
            "id": "31953106606966983378809025079804211143289615424298221568",
            "timestamp": 1432826855000,
            "message": "{\"eventVersion\":\"1.03\",\"userIdentity\":{\"type\":\"Root\"}"
        },
        {
            "id": "31953106606966983378809025079804211143289615424298221569",
            "timestamp": 1432826855000,
            "message": "{\"eventVersion\":\"1.03\",\"userIdentity\":{\"type\":\"Root\"}"
        },
        {
            "id": "31953106606966983378809025079804211143289615424298221570",
            "timestamp": 1432826855000,
            "message": "{\"eventVersion\":\"1.03\",\"userIdentity\":{\"type\":\"Root\"}"
        }
    ]
}

 

そして上記ページにたどり着いたのは以下記事のコメントによる。

cloudwatchlogs -> lambda -> SNSを試してみた

 

先に記載したコードはこのコメントで言及されている「複数イベントへの対処」が行われているバージョンとなる。改めて、先駆者の方々のおかげで非常に助かった。(3度目)

 

SkyBus SkyBus


関連がありそうな記事