フューチャー Advent Calendar 2021の23日目の記事です。
昨日は、@hayao0727 さんによる
AxumでGoのチュートリアル「Writing Web Application」をやってみた でした。
私自身、今年のフューチャーのアドカレ枠では既に2本投稿しているのですが、ラストに向けてガンバっていきます。
投稿した2本はこちら
はじめに
みなさんは、CloudWatch Logsの便利機能である「サブスクリプションフィルター」を利用したことはありますか?
ざっくりと
- 1つのロググループに、最大2つまで設定可能である
- 予め指定した「フィルターパターン」に一致する文字列が、ログに出力されたら起動する
- Lambda, Kinesis, Kinesis Data Firehose に向けて、フィルターがキャッチした文字列(正確には文字列を含むログ全文)を配信する
という機能です。
「特定のログが出力されたら、追加処理を実行したい」という要件が出てきた場合(例えば、error
の文字列がログ出力されたら、チケットを起票するなど)、コンピュートリソース自体に追加処理を加えずとも、サブスクリプションフィルターを設定するだけで、欲しいログを Lambda や Kinesis に送信できます。
今回は以下のシステム構成で
- main-lambda の出力したログを
- サブスクリプションフィルターで拾い上げて
- sub-lambda で取得する
までの流れを追っていきます。
環境用意
今回の検証環境は
- main-lambda
- sub-lambda
- main-lambda → sub-lambda に向けたサブスクリプションフィルターの設定
という流れで準備していきます。
main-lambdaの作成
1つ目のLambdaを作成して、メッセージ文をログ出力する処理を追加します。
import json
def lambda_handler(event, context):
message = "Hello CloudWatch Logs subscription filters."
print(message)
return {
'statusCode': 200,
'body': json.dumps(message)
}
Lambda のデプロイ後、コンソール画面でテスト実行 or awscliコマンドで実行すると、自動で /aws/lambda/main-lambda
のロググループが作成されます。
CloudWatch のコンソール画面からロググループを手動追加しても問題ありませんが、面倒なので Lambda を手動テスト起動して、ロググループを自動生成します。
sub-lambdaの作成
次は、サブスクリプションフィルターの配信を受ける側の Lambda を作成していきます。
import json
import base64
import gzip
def lambda_handler(event, context):
# base64 デコード
decoded_data = base64.b64decode(event['awslogs']['data'])
# gzip 解凍
decompressed_data = gzip.decompress(decoded_data)
json_data = json.loads(decompressed_data)
message = json_data['logEvents'][0]['message']
print(message)
return {
'statusCode': 200,
'body': json.dumps(message)
}
コード解説
サブスクリプションフィルターでの配信データは、{ "awslogs": {"data": "BASE64ENCODED_GZIP_COMPRESSED_DATA"} }
という形式であり、data キーは
- gzip 形式で圧縮
- Base64 でエンコード
された状態で届きます。
そのため、生データを取得するには、受け手側で「Base64 のデコード、gzip の解凍」処理が必要です。
生データ取得後の処理内容は、サブスクリプションフィルターの設定内容が影響するため、後半のパートで説明します。
サブスクリプションフィルターの設定
CloudWatch のコンソール画面から、/aws/lambda/main-lambda
のログループに移動して、サブスクリプションフィルターの設定を選択します。
いくつかの項目選択画面が表示されるため、以下情報を入力します。
送信先を選択
- Lambda 関数: sub-lambda
ログ形式とフィルターを設定
- ログの形式: AWS Lambda
- サブスクリプションフィルターのパターン: Hello
- サブスクリプションフィルター名: Detect Hello
今回は「サブスクリプションフィルターのパターン」を Hello で設定したので、/aws/lambda/main-lambda
のログに Hello の文字列が出力されたとき、サブスクリプションフィルターが起動し、sub-lambda が起動する、という起動フローになります。
項目入力後、「ストリーミングの開始」を選択すると、フィルターの設定状況が確認できます。
以上により、環境準備は完了です。
実行
現状をおさらいすると、
-
Hello の文字列を含むログが
/aws/lambda/main-lambda
に出力されたとき、サブスクリプションフィルターが起動して、sub-lambda が起動する
という状態です。
つまり、main-lambda が Hello の文字列をログ出力さえすれば、後は後続処理が自動で動く状態になっています。
さっそく main-lambda をテスト起動して、ログを出力させます。
(手動起動後の出力ログ)
想定どおり、Hello の文字列が出力されていますね。
このとき、サブスクリプションフィルター → sub-lambda には、以下フォーマットの jsonデータ が渡されます。
{
"messageType": "DATA_MESSAGE",
"owner": "<aws account>",
"logGroup": "/aws/lambda/main-lambda",
"logStream": "2021/12/22/[$LATEST]8ac0982efa7e40e78d6702702b842a7e",
"subscriptionFilters": [
"Detect Hello"
],
"logEvents": [
{
"id": "<log events id>",
"timestamp": 1640181063036,
"message": "Hello CloudWatch Logs subscription filters.\n"
}
]
}
前半の sub-lambda のコードで、以下のような処理がありました。
message = json_data['logEvents'][0]['message']
print(message)
この参照は、サブスクリプションフィルターが渡す jsonデータ に依拠したもので、sub-lambda が受け取ったメッセージを表示するためのものです。
ログを見ると、サブスクリプションフィルターの送信データが、sub-lambda 側で受信できていることが確認できました。
Hello という特定の文字列だけを拾うのではなく、「Hello 文字列を含めた、ログイベントに出力された全文」が渡っていることも分かります。
以上で、
「サブスクリプションフィルターで、あるLambdaの出力したログを、別のLambdaに渡す」
の動作検証は終了です。
おわりに
本記事では、サブスクリプションフィルターを利用して、Lambda のログを配信し、別の Lambda に渡す処理を見ていきました。
既にこの機能を知っている人からすれば「何を今更」という感じかもしれませんが、AWS 初心者が意外と知らずに苦労していたりするケースが見られたので、ブログ化してみました。
以上、長文にお付き合いいただき、ありがとうございました。
明日は、@kmd252525 さんです!