表題の件、以下の過去記事に書いたが、この時点では送信される本文ががログメッセージだけとなっていて、通知メールとしては不十分なため本文もカスタマイズしてみた。
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度目)