0
Help us understand the problem. What are the problem?

posted at

updated at

【AWS】【CloudWatch/Lambda】ログ監視のメール通知

この記事でやったこと

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

目次

  1. この記事でやったこと
  2. 実装背景
  3. LambdaのPythonスクリプト
  4. Pythonスクリプトの補足1
  5. Pythonスクリプトの補足2
  6. Pythonスクリプトの補足3
  7. Pythonスクリプトの補足4
  8. EC2のコマンド参考1
  9. EC2のコマンド参考2
  10. Lambdaでもしも無限ループを起こしてしまった場合

実装背景

CloudWatch の通常のログ監視を SNS を使ってメールを通知すると非常に見辛い。
以下の記事を参考にしました。
https://dev.classmethod.jp/articles/cwl-lambda-sns-publish/

実装までの手順なども上記通りに実装しました。
しかしながら、メールを飛ばした際に私の環境では以下の課題がありました。
・連続でログを検知しても、1行しか通知されない
・2byte文字(日本語)が文字化けされて通知されてくる

LambdaのPythonスクリプト

以下のPythonスクリプトをLambdaで動かす事で課題を解決できました。

cwl-to-sns-publish.py
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例
    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上でログを出すコマンド例をいくつか

EC2 の /var/log/messages に error を出す
$ sudo logger "error"
EC2 の /var/log/messages に 1分置きに error を出す
$ sudo logger "error" ; sleep 60
EC2 の /var/log/messages に 1分置きに時刻表示含めた error を出す
$ sudo echo `date` "error" | logger ;sleep 60

EC2のコマンド参考2

EC2上でログを垂れ流しで見れる例
「EC2のコマンド参考1」とは別のターミナルを立ち上げておきます

EC2 の /var/log/messages の表示をリアルタイムで出しておく
$ sudo tail -f /var/log/messages

Lambdaでもしも無限ループを起こしてしまった場合

https://open-groove.net/lambda/throttling-lambda-function/
こちらの記事を参考にし、同時実行数を0にして無限ループを止めました。
無限ループ止まったかな?というのを確認したあとにデフォルトの1000に元戻しして動かしました。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?