この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
CloudWatch Logsへの特定文字の書き込みを検知する仕組みとして、メトリクスフィルターを利用しCloudWatch Alarmを設定する構成は多くあります。こちらの構成は、CloudWatch Alarmの状態変化を示す通知となり、検知したログデータそのものを通知に含めることはできません。
▲ メトリクスフィルターを利用したCloudWatch Alarmの通知例
サードパーティのログインテグレーション等を利用せず、CloudWatch Logsのログデータを通知に含めたいといった場合は少々作り込みが必要です。今回はCloudWatch Logsにサブスクリプションフィルターを設定し、検知したログデータを通知するLambda Funcionを作成してみました。
前提
通知先はChatworkとし、通知に利用するSNSトピック、Lambda Functionは以下を利用します。
構成
前提に記載のLambda Function等を利用するため、以下赤枠内が今回のスコープです。CloudWatch Logsのサブスクリプションフィルター設定と、SNSトピックにログデータを送信するLambda Functionの作成を本ブログで行います。
構築
Lambda Function作成
ランタイムはPython 3.8
で、以下Lambda Functionの設定です。
関数コード
cwl-to-sns-publish
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))
# ログデータ取得
message = json_data['logEvents'][0]['message']
# 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 = json.dumps(message)
)
環境変数
本Lambda Functionはサブスクリプションフィルター設定により、Lambda Functionに送信されたCloudWatch LogsのログデータをSNSトピックに送信します。利用するSNSトピックのARNを環境変数に指定しています。
CloudWatch Logsのサブスクリプションフィルター設定により、実行されるLambda Functionは{ "awslogs": {"data":"BASE64ENCODED_GZIP_COMPRESSED_DATA"} }
イベントデータを受け取ります。data
キーはBase64 でエンコードされており、gzip 形式で圧縮されています。ログデータを取得するため、デコード、展開を実施します。
その後、任意の件名(ここでは、ロググループ名 + ログストリーム名)を指定し、ログデータをSNSトピックに送信しています。
なお、後続のLambda Function(前提に記載のChatworkにメッセージを送信するLambda Function)でjson.load
を行っており、そちらのLambda Functionに修正を加えないとタブ等の制御文字が扱えないため、publish
メソッド呼び出しの際にjson.dumps
でエスケープしてログデータを送信しています。通知内容等この辺りをカスタマイズする際は、後続処理も考慮した修正が必要です。(今回は後続のLambda Functionの修正は行っていません。)
CloudWatch Logsサブスクリプションフィルター設定
該当のロググループに、Lambdaサブスクリプションフィルターを設定します。任意のフィルター名で先程作成したLambda Functionを送信先に指定します。
ここでは、フィルタパターンを"ERROR"とし、特定の書き込み(ここではERROR)を含むログデータのみLambda Functionに送信するようにしています。フィルターの詳細については、以下を確認ください。
▲ Lambdaサブスクリプションフィルターの設定
CloudWatch Logsにてサブスクリプションフィルターの設定を行うと、該当のLambda Functionにトリガー等が付与されます。
通知確認
Lambdaサブスクリプションフィルターを設定したロググループに、フィルタパターンに合致する書き込みを行いChatworkへの通知を確認します。put-log-eventsでログの書き込みが可能です。
▲ ERRORを含むロギングを実施
Chatwork通知に、ログデータが含まれていることを確認できました。
さいごに
EventBridgeのイベントデータにログデータがあれば、イベントパターンの定義のみで作り込み不要でログデータを取得できるかな?とも考えましたが、CloudWatch Logsは、CloudTrailを介してイベントとなり、ログデータは含まれていませんでした。
APIコールのイベントとなり、以下のようなイベントデータでした。
CloudTrailを介したCloudWatch Logsのイベント例
{
"version":"0",
"id":"56ae36c7-dfb1-6966-2778-52434364d2a5",
"detail-type":"AWS API Call via CloudTrail",
"source":"aws.logs",
"account":"XXXXXXXXXXXX",
"time":"2021-01-20T00:21:10Z",
"region":"ap-northeast-1",
"resources":[
],
"detail":{
"eventVersion":"1.08",
"userIdentity":{
"type":"AssumedRole",
"principalId":"AA:test-error-write",
"arn":"arn:aws:sts::XXXXXXXXXXXX:assumed-role/LambdaRole/test-error-write",
"accountId":"XXXXXXXXXXXX",
"accessKeyId":"",
"sessionContext":{
"sessionIssuer":{
"type":"Role",
"principalId":"AA",
"arn":"arn:aws:iam::XXXXXXXXXXXX:role/LambdaRole",
"accountId":"XXXXXXXXXXXX",
"userName":"LambdaRole"
},
"webIdFederationData":{
},
"attributes":{
"mfaAuthenticated":"false",
"creationDate":"2021-01-20T00:21:01Z"
}
}
},
"eventTime":"2021-01-20T00:21:10Z",
"eventSource":"logs.amazonaws.com",
"eventName":"CreateLogStream",
"awsRegion":"ap-northeast-1",
"sourceIPAddress":"52.195.16.227",
"userAgent":"awslambda-worker/1.0 rusoto/0.42.0 rust/1.47.0 linux",
"requestParameters":{
"logGroupName":"/aws/lambda/test-error-write",
"logStreamName":"2021/01/20/[$LATEST]e0efc2e860504dd1b7ea0f8a8b2c1539"
},
"responseElements":null,
"requestID":"f603bdb5-8647-47c3-8cf9-b2d318cf947b",
"eventID":"121a21e5-69e4-45bc-94c8-31a2aaeea231",
"readOnly":false,
"eventType":"AwsApiCall",
"apiVersion":"20140328",
"managementEvent":true,
"eventCategory":"Management"
}
}
作り込み不要でログデータの通知を行えるようなアップデートに期待したいと思います。