AWSでのログ監視メール送信はサブスクリプションフィルタ + Lambda + SNSを使用するのがスタンダード。みんなやっていそうなことだが未経験だったのでやってみた。基本参考にしたのは王道クラメソさんの記事だったが、ちょっとわかりにくいところがあったので他の記事も合わせて参照して若干やり方変えつつ検証した。

今回はマネコン作業メインでやったが、CLIやTerraformなどAPI経由で実装する場合追加作業が発生するため注意が必要。(後述 補足事項に記載)

 

参考

 

以下は今後の参考用

 

処理概要

  1. CWLにログが出力される
  2. CWLのサブスクリプションフィルタでキーワード検知
  3. Lambda関数起動
  4. SNSに連携される
  5. メール通知

 

作業概要

  1. SNSトピック作成〜サブスクライブ
  2. Lambda用IAMロール作成
  3. Lambda関数作成
  4. ロググループ/ログストリーム作成
  5. ロググループにサブスクリプションフィルタ作成 (配信先に3.のLambda関数を指定)
  6. テストログ送信〜メール通知確認

※ログストリーム作成は検証時のみ。通常は自動生成される。

 

今回の検証に使用したアイテム(個人メモ)

アイテム 名称
SNSトピック log-monitor-topic
Lambda用IAMロール send-log-filter-role
Lambda関数 send-log-filter-function
サブスクリプションフィルタ send-log-filter

 

やったこと

  1. SNSトピック作成〜サブスクライブ
    過去記事:AWS EventBridge + SNSからのメール件名をカスタマイズするに書いたので省略。ここではCLIでやってるけどマネコンでも特にハマるところはない。アクセスポリシーはデフォルトにした。

 

  1. Lambda用IAMロール作成
    とりあえず以下のマネージドポリシーをアタッチ。
  • CloudEatchLogsFullAccess
  • AmazonSNSFullAccess

 

  1. Lambda関数作成
    (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)

 

(2) 環境変数をセット(Configration/設定タブ ー> 左ペインのEnvironment variables/環境変数)

Key Value
SNS_TOPIC_ARN arn:aws:sns:ap-northeast-1:my-account-id:log-monitor-topic
CUSTOM_SUBJECT エラーログ送信テスト
  1. ロググループ/ログストリーム作成
    マネコンから普通にできるので省略。

 

  1. サブスクリプションフィルタ作成
    ロググループの画面から作成する。

サブスクリプションフィルタ作成

 

配信先として先に作成したLambda関数を指定する。ログフォーマットは当初OtherにしたがJSONでいいらしい。(まだよくわかっていない…)
サブスクリプションフィルタ作成

 

事前にメッセージを投入しておけばここでテストできる。メッセージが何もない場合はスキップ。
サブスクリプションフィルタ作成

 

最後に[Start streaming]押下で完了。

 

作成後のサブスクリプションフィルタ。作成後に変更したい場合はマネコンからはできないため、CLIから設定する。詳細は後述の補足事項に記載。

作成後のサブスクリプションフィルタ

 

  1. テストログ送信〜メール通知確認
    このテストログ送信方法については、クラメソさんや他の記事ではCLIからput-log-eventsを実行しているが、正直面倒くさい。マネコンからやる方が簡単なのでここではその手順を記載。

対象のログストリーム画面から、[Actions(アクション)] ー> [Create log event(ログイベントの作成)]と遷移し、テストイベントを発行する。

ログイベントの作成

 

メール通知を確認。届いた!

エラー通知メール

 

…と、ここまで普通にできたっぽく書いているが、実際には何かとハマって手こずってしまった。当初メールが届かなくてね。複数の記事を参考にしているが、それぞれ微妙に手順や実装が異なるから、少しどこかをいじるとNGになったりする。

メールが届かないのはSNSが原因と思ったけど(Lambdaでエラー出ていないから)、Lambda自体のログにテストイベントのメッセージが何も出力されていないことに気づいて、Lambdaがイベントを取得できていなくてSNSにメッセージが渡っていないことが原因とわかった。コードも若干入れ替えたり編集したりしたんで。自分は紆余曲折しておかしなことになったが、最初はクラメソさんのコードをそのまま使って手順通りにやればできるはず。応用はその後。

 

補足事項

  1. サブスクリプション作成後の変更
    サブスク作成後はマネコンからは直接変更できないが、CLIで編集可能。aws logs put-subscripution-filterで同じサブスク名を指定すれば設定が上書きされる。
$ aws logs put-subscription-filter ¥
--log-group-name [your log group name] ¥
--filter-name [your subscription filter name] ¥
--filter-pattern [your filter pattern] ¥
--destination-arn [your destination arn] ※今回の場合Lambda関数のARN

 

  1. フィルタパターン編集
    フィルタパターンでOR条件するには、キーワード間はスペース区切りとし、各キーワードの先頭に?をつける。除外フィルタはまた別に必要で複雑になる。Lambda関数内で設定することも可能なので、要件に応じて検討。

フィルタパターンOR条件例
これを作成済みのサブスクに適用したい場合は、上記1.のCLIコマンドの--filter-patternオプションの値として指定すればよい。

"?error ?Error ?ERROR ?fail ?Fail ?FAIL"

 

除外キーワードをセットするのはちょっと面倒で、こんな風になる。以下の場合、メッセージが"error event"の場合は検知され、“error test"の場合は検知されない。

"[( msg=¥"*error*¥" || msg=¥"*Error*¥" ) && ( msg!=¥"*test*¥" && msg!=¥"*Test*¥" && msg!=¥"*TEST*¥" )]"

 

Lambdaコード内で除外キーワード定義することも可能だが、検知キーワードと除外キーワードが別れるのもどうなんかと思うし、迷うところ。

 

  1. APIから実装する際の追加作業
    サブスクリプションフィルタ作成時、マネコンだと自動で付与される権限がCLIでは付与されないため、事前にLambda側で権限を追加する。--statement-idは適当な文字列でいいと思われる。多分。
$ aws lambda add-permission ¥
--function-name [your function name] ¥
--statement-id "your statement id" ¥
--principal "logs.ap-northeast-1.amazonaws.com" ¥
--action "lambda:InvokeFunction" ¥
--source-arn "arn:aws:logs:ap-northeast-1:[your account id]:log-group:*:*" ¥
--source-account [your account id]

 

TerraformやCFnから作成する場合も同じ作業が必要になるはずだから要注意。

参考: add-permission

 

スカイウォーク


関連がありそうな記事