🦁

【AWS】CloudWatch Logsからシステムログをメール通知する。

2021/08/01に公開

はじめに

CloudWatch Logsから対象のシステムログをメール通知する方法をアウトプットします。
今回はCloudWatch Logsのサブスクリプションフィルターを使用します。
※参考記事にあるクラスメソッドさんの記事を参考にしております。

構成

ログ送信の流れは以下の通りです。

①EC2から CloudWatch Logs へログファイル出力
②サブスクリプションフィルターで特定文字列をマッチさせて Lambda 関数を起動
③Lambda 関数で該当メッセージを抽出、SNS 用に整形
④該当文字列が含まれた行を SNS で通知(今回はメール)

前提

  • 作業用IAMユーザーにて作業を実施
  • 今回は、AmazonLinuxの/var/log/messagesからのログ抽出とします。
  • CloudWatch logs上に既にOS上のログが出力されている状態になります。(OS上のCloudWatchエージェント設定については割愛とさせて頂きます。)
  • CloudWatch Logsのロググループ名とログストリーム名は、事前に以下のように設定しております。
項目 内容 備考
ロググループ名 amalinux01 EC2のホスト名
ログストリーム名 /var/log/messages 監視したいログファイル名
  • 監視したいEC2には、事前にCloudWatchAgentAdminPolicyをアタッチしたIAMロールを設定済み

今回のゴール

以下のような形でメール通知させる。

全体の流れ

①メール通知用SNSトピック作成
②Lambda関数の作成
③サブスクリプションフィルタの作成
④アラート発報試験

作業手順

①メール通知用SNSトピック作成

1.マネジメントコンソールよりSNSを開く。

2.Amazon SNSの画面より「トピック」を開く。

3.トピック一覧より「トピックの作成」を開く。

4.トピックの作成にて以下のように入力し、「トピックの作成」をクリック。

項目 内容 備考
タイプ スタンダード
名前 Alarm_Test 任意の名前でOK
表示名 空欄 入力は任意

5.トピックが作成されたことを確認。

6.作成したトピックの画面にて「サブスクリプションの作成」をクリック。

7.以下のように選択&入力し、「サブスクリプションの作成」をクリック。

項目 内容 備考
トピックARN 該当トピックのARN そのままでOK
プロトコル Eメール Eメールで通知するため
エンドポイント 通知したいEメールアドレス

8.サブスクリプションが正常に作成できたことを確認。

9.登録したメールアドレスに、以下件名でメールが届くので、メールの認証を実施する。

件名
AWS Notification - Subscription Confirmation

10.再度、作成したトピック内のサブスクリプションを確認し、ステータスが「確認済み」になっていることを確認。

②Lambda関数の作成

1.マネジメントコンソールよりLambdaを開く。

2.Lambdaダッシュボードより「関数の作成」をクリック。

3.関数の作成の画面にて、「一から作成」にチェックを入れる。

4.基本的な情報の項目にて、以下のように選択し、「関数の作成」をクリック。

項目 設定内容 備考
関数名 AlertCode 任意の名前を入力
ランタイム Python3.7
実行ロール 以下ポリシーがアタッチされているIAMロール
・CloudWatchLogsFullAccess
・AmazonSNSFullAccess
大きい権限を与えているため、実務で使用する場合はさらに制限が必要

5.関数が作成されることを確認。

6.下にスクロールし、「コード→lambda_function.py」と選択。以下コードをコピー&ペーストし、「Deploy」をクリック。

コード
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): 
        # ホスト名取得
        hostname = data_json['logGroup']
        
        # ログファイル名取得
        logname = 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)
        
        # 件名整形
        subjectmsg = "【Alert】" + hostname + "_" + logname
        
        # 本文整形
        hostmsg = "■ホスト名:" + "\n" + hostname
        lognamemsg = "■ログファイル名:" + "\n" + logname
        timemsg = "■発生時刻:" + "\n" + str(datetime_jst)
        logmsg = "■ログ内容:" + "\n" + log_json['message']
        msg = hostmsg + "\n\n" + timemsg + "\n\n" + lognamemsg + "\n\n" + logmsg

        try:
            sns = boto3.client('sns')
    
            #SNS Publish
            publishResponse = sns.publish(
                TopicArn = os.environ['SNS_TOPIC_ARN'],
                Message = msg,
                Subject = subjectmsg
            )
    
        except Exception as e:
            print(e)

7.コードのDeployが成功したことを確認。

8.「設定→一般設定→編集」と選択。

9.タイムアウトを1分に設定し、「保存」をクリック。

10.一般設定にてタイムアウトの値が「1分0秒」になっていることを確認。

11.「環境変数→編集」と選択する。

12.以下の環境変数を追加し、「保存」をクリック。

項目 設定内容 備考
キー SNS_TOPIC_ARN SNSのarnを格納する変数
arn:aws:sns:ap-northeast-1:+++++++++++++:Alerm_Test 「①メール通知用SNSトピック作成」にて作成したトピックのarn

13.環境変数が追加できたことを確認。

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

1.マネジメントコンソールより「CloudWatch」を起動する。

2.CloudWatch画面より「ロググループ」をクリックする。

3.今回の監視対象であるロググループをクリックする。

4.ロググループの画面より、「サブスクリプションフィルター」をクリックする。

5.サブスクリプションフィルター一覧の右側にある「作成」から「Lambdaサブスクリプションフィルターを作成」をクリックする。

6.「②Lambda関数の作成」にて作成したLambda関数を選択する。

7.下にスクロールし、以下のように入力する。

項目 設定内容 備考
ログの形式 JSON
サブスクリプションフィルターのパターン error フィルタリングしたい値を入力
サブスクリプションフィルター名 【ERROR】amalinux01(/var/log/messges) 任意の名前を入力

8.パターンのテストを実施し問題ないことを確認後、「ストリーミングを開始」をクリックする。

9.サブスクリプションフィルターが作成できたことを確認。

※Lambdaの「設定→トリガー」の部分でも、サブスクリプションフィルターが追加されたことを確認可能

④アラート発報試験

1.監視対象のEC2へログインし、以下コマンドを実行する。
※今回は、AmazonLinux2を使用しています。

コマンド
logger -p user.err -t ERROR "プログラムでエラーが発生しました"

2./var/log/messagesにログが追記されていることを確認。

コマンド
sudo tail -1 /var/log/messages
コマンド実行例
[ec2-user@ip-10-0-0-204 ~]$ sudo tail -1 /var/log/messages
Aug  1 15:42:41 ip-10-0-0-204 ERROR: プログラムでエラーが発生しました
[ec2-user@ip-10-0-0-204 ~]$

3.少々待つと以下内容のメールが受信される。

※Lambdaの対象関数内の「モニタリング」を確認すると、実行ログを確認することが可能。

参考記事

CloudWatch Logs を文字列検知してログ内容をメールを送信してみた サブスクリプションフィルター版

Discussion