目次
ハイブリッドクラウドコンサルティング部の榊原です。今回はEC2(Windows)からCloudWatchに送られたログをLambdaを介して、SNSで送る方法について述べます。
前提
- OSはWindows 2022
- EC2にCloudWatch Agentはインストール済
- ロググループへのログ出力が正常に行えている
- SNSトピックは既に作成済み
実装内容
CloudWatch AgentがインストールされたEC2(Windows)からCloudWatchロググループにログが送られています。サブスクリプションフィルターで必要なログをLambdaに渡し、そこからさらに必要な値のみ抜き出します。そして最後にSNSからユーザに向けてメールで送信するという内容です。
手順
ログの出力内容の確認
記事作成時点で出力されているログは以下の通りです。
<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'><System><Provider Name='System'/><EventID Qualifiers='0'>101</EventID><Version>0</Version><Level>2</Level><Task>0</Task><Opcode>0</Opcode><Keywords>0x80000000000000</Keywords><TimeCreated SystemTime='2023-08-03T07:22:02.6362567Z'/><EventRecordID>66948</EventRecordID><Correlation/><Execution ProcessID='0' ThreadID='0'/><Channel>System</Channel><Computer>XXXXX</Computer><Security UserID='XXXXX'/></System><EventData><Data>テストです</Data></EventData><RenderingInfo Culture='en-US'><Message></Message><Level>Error</Level><Task></Task><Opcode>Info</Opcode><Channel></Channel><Provider></Provider><Keywords><Keyword>Classic</Keyword></Keywords></RenderingInfo></Event>
Errorとあるため、これがついたログのみサブスクリプションフィルターに通しましょう。
Lambda作成
ランタイムはPython3.9、タイムアウトは10秒としました。Lambdaにアタッチしているロールには「AWSLambdaBasicExecutionRole」と「AmazonSNSFullAccess」をアタッチしています。下記コードを入力します。SNSトピックのARNはご自身のものに書き換えてください。ちなみに僕は知らなかったのですが、サブスクリプションフィルターを介して宛先サービスに送信されるログは、base64 でエンコードされ、gzip 形式で圧縮されるのでご注意ください。(AWS公式URLより)
import boto3
import json
import base64
import gzip
import re
def lambda_handler(event, context):
# CloudWatchのログデータはBase64エンコードが施されているのでデコード
log_data_base64 = event['awslogs']['data']
log_data_bytes = base64.b64decode(log_data_base64)
# gzip圧縮されたログデータを解凍し、文字列にデコード
log_data_str = gzip.decompress(log_data_bytes).decode('utf-8')
# JSON文字列を辞書に変換
log_data_dict = json.loads(log_data_str)
# owner(アカウントID)とlogStream(インスタンスID)の抽出
accountID = log_data_dict['owner']
instanceID = log_data_dict['logStream']
# 正規表現を使ってErrorメッセージを抽出
error_messages = re.findall(r"<Message>(.*?)<\/Message>", log_data_str)
if not error_messages:
print("No Error found in log events.")
return {
'statusCode': 200,
'body': 'No Error found in log events.'
}
# SNSクライアントを初期化
sns_client = boto3.client('sns')
# メール送信のためのSNSトピックARNを指定
sns_topic_arn = 'arn:aws:sns:XXXXX'
# メッセージを文字列に変換
error_message_str = "\n".join(error_messages)
message_str = json.dumps({"Log Message": error_message_str})
# メッセージをまとめる
message = "messagesログにアラート文字列が出力されました。\nログ内容を確認しご対応ください。"
log_message = message_str
email_message= f"{message}\n\nアカウントID:{accountID}\n\nインスタンスID: {instanceID}\n\n{log_message}"
# メール送信
try:
subject = "Test Data Extracted from CloudWatch Logs"
response = sns_client.publish(
TopicArn=sns_topic_arn,
Message=email_message,
Subject=subject
)
except Exception as e:
print(e)
Lambdaのeventに渡されたデータはデコードを施し、JSONにすると下記のようになります。
{
"messageType": "DATA_MESSAGE",
"owner": "XXXXX",
"logGroup": "System",
"logStream": "i-XXXXX",
"subscriptionFilters": [
"test_filter_onlyError"
],
"logEvents": [
{
"id": "XXXXX",
"timestamp": 1691043334468,
"message": "<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'><System><Provider Name='System'/><EventID Qualifiers='0'>101</EventID><Version>0</Version><Level>2</Level><Task>0</Task><Opcode>0</Opcode><Keywords>0x80000000000000</Keywords><TimeCreated SystemTime='2023-08-03T06:15:34.4680516Z'/><EventRecordID>66907</EventRecordID><Correlation/><Execution ProcessID='0' ThreadID='0'/><Channel>System</Channel><Computer>XXXXX</Computer><Security UserID='XXXXX'/></System><EventData><Data>テストです</Data></EventData><RenderingInfo Culture='en-US'><Message></Message><Level>Error</Level><Task></Task><Opcode>Info</Opcode><Channel></Channel><Provider></Provider><Keywords><Keyword>Classic</Keyword></Keywords></RenderingInfo></Event>"
}
]
}
サブスクリプションフィルターの準備
対象のロググループから「Lambdaサブスクリプションフィルターを作成」を選択します。
Lambda関数は上で作成したものを選択し、ログの形式は「その他」、サブスクリプションフィルターのパターンは抜き出したい文字列を入力するので今回は「Error」とします。サブスクリプションフィルター名は自由です。テストするログデータは、対象のインスタンスIDを選択します。全て入力したら、「ストリーミングを開始」を選択します。
確認
準備が整ったので、実際にErrorを検出して通知が来るか試してみましょう。
インスタンスにRDP接続し、下記コマンドを入力します。
仮のログデータが作成されます。SUCCESSと表示されればOKです。
C:\Users\Administrator>eventcreate /l SYSTEM /t ERROR /id 123 /d "テストです"
SUCCESS: An event of type 'ERROR' was created with 'SYSTEM' as the log.
まもなく僕のアドレスに以下のようなメールが届きました。
正しく動作していそうです。今回Log Messageには何も入れていないので空白のままですが、実際には例えば下記の赤マーク部分のメッセージが出てきます。(文字が小さいので拡大してご確認ください汗)
最後に
今回はXML形式のログデータをCloudWatchサブスクリプションフィルターとLambda、SNSを使って通知する仕組みをご紹介しました。この記事がどなたかのお役に立てば幸いです。ここまでお読みいただきありがとうございました。
- カテゴリー
- タグ