CloudWatch Logsの特定文字を検知してログ内容を通知するLambda Function

CloudWatch Logsの特定文字を検知してログ内容を通知するLambda Function

CloudWatch Logsにサブスクリプションフィルター設定して、特定文字を含むログデータをLambda FunctionつかってChatworkに通知しました。
Clock Icon2021.01.20

この記事は公開されてから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の設定です。

関数コード

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)
    )

環境変数

sa20210118-04

本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通知に、ログデータが含まれていることを確認できました。

sa20210118-09

さいごに

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"
   }
}  

作り込み不要でログデータの通知を行えるようなアップデートに期待したいと思います。

この記事をシェアする

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.