約1年前の記事 CloudWatchLogsのログ監視 - サブスクリプションフィルタ + Lambdaでメール送信(2) の改良版の話。

 

  1. ログ監視用コードはループさせない
    上記投稿を書いた当時はログイベント全てをメール通知する方式だったが、これだと類似の通知メールが大量に飛んでしまい、抑止した方がいいのでは…という流れになった。通知は最初の一回だけ、に変更した。運用上はこれで要件は満たせるという判断。これが正しいということではなく、現場の方針次第。以下記事のコメント欄で指摘されている対応は、逆にやめることになった。

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

 

  1. Lambda自体のログにトリガー元を記録する
    別途Lambda自体のログについて。Lambda自体のログはエラーが出たか否かと発生時刻くらいしか情報を吐かない。設計上複数のイベントを一つのLambda関数に連携する想定なので、そのままだとLambda関数のInvoke時のトリガーが不明で調査が困難になる(ログ監視でもリソース、メトリクス監視でも)

そのため、Lambdaコード自体のログに、トリガー元のロググループ名を出力させることにした。(アラームならアラーム名)

メッセージ全量を吐かせてもいいがそうするとLambda自体に問題がなくてもLambdaのログにErrorなどの文字が含まれてしまい、ややこしくなる。ログも肥大化する。(大した量ではないとはいえ)このためメッセージ全量については「普段はコメントしておいて、デバッグ時のみコメントを外す」運用とした。

 

追加した内容

デバッグ用に、メッセージ全量出力。必要時だけコメントを外す

log_message= log_json['message']
#print('LogMessage: ', log_message)

 

ロググループ名を出力。これは常時適用

log_group = data_json['logGroup']
print('LogGroup: ',log_group)

 

アラーム用コード例。件名に投入するためアラーム名を取得しているが、printでLambda自体のログにも吐かせる。

alm_list = alarm[0].split(':')
alm_name = alm_list[-1]
print('AlarmName: ', alm_name)

 

コード全体。

修正前

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)

 

修正後

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)

    # ロググループ名取得
    log_group = data_json['logGroup']
    print('LogGroup: ',log_group)
    
    # ログストリーム名取得
    log_stm = data_json['logStream']
    
    # LogEvents取得。ログイベント数に関わらず最初の1ログイベントのみ取得する。(大量通知抑止のため)
    log_json = json.loads(json.dumps(data_json["logEvents"][0], 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)
    
    # ログメッセージ取得
    log_message = log_json['message']
    #print('LogMessage: ', log_message)
    
    # メール件名整形
    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)

 

 

Tokyo bay Tokyo bay


関連がありそうな記事