この記事でやったこと
CloudWatch のログ監視でメールを飛ばした際、ログメッセージを記載して飛ばす。
以下は「error」を2件検知して、メールを飛ばしたメールの本文。
検知したメッセージは以下
May 20 09:33:21 ip-10-0-1-121 ec2-user[1786]: 2022年 5月 20日 金曜日 09:33:21 JST error
May 20 09:33:25 ip-10-0-1-121 ec2-user[1789]: 2022年 5月 20日 金曜日 09:33:25 JST error
目次
- この記事でやったこと
- 実装背景
- LambdaのPythonスクリプト
- Pythonスクリプトの補足1
- Pythonスクリプトの補足2
- Pythonスクリプトの補足3
- Pythonスクリプトの補足4
- EC2のコマンド参考1
- EC2のコマンド参考2
- Lambdaでもしも無限ループを起こしてしまった場合
実装背景
CloudWatch の通常のログ監視を SNS を使ってメールを通知すると非常に見辛い。
以下の記事を参考にしました。
https://dev.classmethod.jp/articles/cwl-lambda-sns-publish/
実装までの手順なども上記通りに実装しました。
しかしながら、メールを飛ばした際に私の環境では以下の課題がありました。
・連続でログを検知しても、1行しか通知されない
・2byte文字(日本語)が文字化けされて通知されてくる
LambdaのPythonスクリプト
以下のPythonスクリプトをLambdaで動かす事で課題を解決できました。
import logging
import json
import base64
import gzip
import boto3
import os
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
sns_topic_arn = os.environ['SNS_TOPIC_ARN'] # 環境変数よりSNSトピックARN取得
sns_client = boto3.client('sns')
# CloudWatchLogsからのデータはbase64エンコードされているのでデコード
decoded_data = base64.b64decode(event['awslogs']['data'])
# バイナリに圧縮されているため展開
json_data = json.loads(gzip.decompress(decoded_data))
logger.info("EVENT: " + json.dumps(json_data))
# 1行目の内容は直書き
i = 0
message = "検知したメッセージは以下\n\n"
# 検知メッセージの1つ目を取得
message_output = message + json_data['logEvents'][i]['message'] + "\n"
#メッセージ数をカウントしつつ処理する
while True:
try:
i = i + 1
print(json_data['logEvents'][i]['message'])
except:
break
else:
message_output = message_output + json_data['logEvents'][i]['message'] + "\n"
# SNS件名設定
log_group = json_data['logGroup']
log_stream = json_data['logStream']
#ロググループ名、ログストリーム名をsubjectに設定
subject = log_group + ' ' + log_stream
response = sns_client.publish(
TopicArn = sns_topic_arn,
Subject = subject,
Message = message_output
)
Pythonスクリプトの補足1
何を出力しているか不明な場合は、好きなところに print を入れておきましょう。
Lambda のログ画面に print の出力結果が出てきます。
print(json_data['logEvents'][i]['message'])
print(i)
Pythonスクリプトの補足2
json_data['logEvents'][i]['message'] は i=0 だとリストの1行目を読み込みます。
リストの1行目は、検知メッセージの1つ目を取得する事になります。
i=1の場合は、リストの2行目、検知メッセージの2つ目を取得する事になります。
message_output = message + json_data['logEvents'][i]['message'] + "\n"
Pythonスクリプトの補足3
検知メッセージが1つしかない状況で、以下のように検知メッセージの2つ目を
取得しにいこうとすると Python がエラーとなり、Lambda は強制終了してしまうようです。
message_output = message + json_data['logEvents'][1]['message'] + "\n"
Pythonスクリプトの補足4
存在しないリストにアクセスすると強制終了となるので、例外処理を入れています。
あえて print(json_data['logEvents'][i]['message']) を実施してエラーになった場合は break で終了させます。
エラーにならなかった場合は、message_output に追記させています。
それを while True でループさせています。
while True:
try:
i = i + 1
print(json_data['logEvents'][i]['message'])
except:
break
else:
message_output = message_output + json_data['logEvents'][i]['message'] + "\n"
EC2のコマンド参考1
EC2上でログを出すコマンド例をいくつか
$ sudo logger "error"
$ sudo logger "error" ; sleep 60
$ sudo echo `date` "error" | logger ;sleep 60
EC2のコマンド参考2
EC2上でログを垂れ流しで見れる例
「EC2のコマンド参考1」とは別のターミナルを立ち上げておきます
$ sudo tail -f /var/log/messages
Lambdaでもしも無限ループを起こしてしまった場合
https://open-groove.net/lambda/throttling-lambda-function/
こちらの記事を参考にし、同時実行数を0にして無限ループを止めました。
無限ループ止まったかな?というのを確認したあとにデフォルトの1000に元戻しして動かしました。