Lambda 関数の実行ログが出力されるログストリーム名を Lambda コンテキストから取得する

今さらながら Lambda 実行ログの出力先ログストリーム名を取得できることを知りました。

コンバンハ、千葉(幸)です。

Lambda 関数の実行ログは/aws/lambda/<Lambda関数名>という CloudWatch Logs ロググループに出力されます。

ロググループ内でログストリームが動的に生成され、その中に出力されていきます。ログストリーム名は以下の命名規則になっています。

YYYY/MM/DD/[<Lambda関数 のバージョン>]<ランダムな識別子>

以下のイメージです。

このログストリーム名を動的に取得したい、ということで Lambda コンテキストを使用してみました。

やりたいこと

以下のスクリプトのように Lambda 関数の実行とログ取得を行いたいです。

#!/bin/bash
aws lambda invoke --function-name my-function --cli-binary-format raw-in-base64-out --payload '{"key": "value"}' out
sed -i'' -e 's/"//g' out
sleep 15
aws logs get-log-events --log-group-name /aws/lambda/my-function --log-stream-name $(cat out) --limit 5

ここでは Lambda 関数の返り値としてログストリーム名が返ってくることが前提になっています。そのために Lambda コンテキストを使用します。

Lambda 関数の作成

今回は設計図hello-world-pythonを用いて Lambda 関数を作成しました。

  • 関数名:hello-world-python
  • ランタイム:Python 3.7
  • IAM ロール:自動作成に任せる

コードの中身は以下です。

import json

print('Loading function')


def lambda_handler(event, context):
    print("Received event: " + json.dumps(event, indent=2))
    print("value1 = " + event['key1'])
    print("value2 = " + event['key2'])
    print("value3 = " + event['key3'])
    return event['key1']  # Echo back the first key value
    #raise Exception('Something went wrong')

渡されたイベントの key1 の値を返すようになっています。

Lambda 関数の実行

上記の状態で AWS CLI から Lambda 関数を実行してみます。

まず環境変数を格納。

FUNCTION_NAME=hello-world-python
PAYLOAD=$(cat <<EOM
{
  "key1": "value1",
  "key2": "value2",
  "key3": "value3"
}
EOM
)

上記を引数に使用して実行します。

$ aws lambda invoke --function-name $FUNCTION_NAME --cli-binary-format raw-in-base64-out --payload "$PAYLOAD" out
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

--cli-binary-format raw-in-base64-outを指定しないとペイロードが base64 エンコードされたテキストとして扱われるため、それを回避するために指定します。

outfile として指定したoutにはreturn event['key1']の内容が格納されています。

$ cat out
"value1"

Lambda コンテキストの使用

コードの内容を以下に書き換えます。return で返す内容をcontext.log_stream_nameに変更しています。

import json

print('Loading function')


def lambda_handler(event, context):
    print("Received event: " + json.dumps(event, indent=2))
    print("value1 = " + event['key1'])
    print("value2 = " + event['key2'])
    print("value3 = " + event['key3'])
    # return event['key1']  # Echo back the first key value
    return context.log_stream_name
    #raise Exception('Something went wrong')

ここではlog_stream_nameを指定しましたが、他にも利用できる Lambda コンテキストオブジェクトがあります。

Lambda コンテキストによるログストリーム名の取得

コードを修正した状態で再度 Lambda 関数を実行します。

$ aws lambda invoke --function-name $FUNCTION_NAME --cli-binary-format raw-in-base64-out --payload "$PAYLOAD" out
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
$ cat out
"2022/03/21/[$LATEST]7d0c88909f86424bbe40b4c0cfcdb08f"

ログストリーム名が返り値として取得できていることが分かります。

冒頭のスクリプトと同等のコマンドを実行します。2 行目の sed の処理は"を除去するためのものです。

aws lambda invoke --function-name $FUNCTION_NAME --cli-binary-format raw-in-base64-out --payload "$PAYLOAD" out
sed -i'' -e 's/"//g' out
sleep 15
aws logs get-log-events --log-group-name /aws/lambda/$FUNCTION_NAME --log-stream-name $(cat out) --limit 5

実行するときちんとログストリーム内のログイベントが取得できました。

$ aws lambda invoke --function-name $FUNCTION_NAME --cli-binary-format raw-in-base64-out --payload "$PAYLOAD" out
sed -i'' -e 's/"//g' out
sleep 15
aws logs get-log-events --log-group-name /aws/lambda/$FUNCTION_NAME --log-stream-name $(cat out) --limit 5
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}
{
    "events": [
        {
            "timestamp": 1647838819665,
            "message": "value1 = value1\n",
            "ingestionTime": 1647838828689
        },
        {
            "timestamp": 1647838819665,
            "message": "value2 = value2\n",
            "ingestionTime": 1647838828689
        },
        {
            "timestamp": 1647838819665,
            "message": "value3 = value3\n",
            "ingestionTime": 1647838828689
        },
        {
            "timestamp": 1647838819666,
            "message": "END RequestId: 37e94215-8d36-4ea8-a5cd-b850f20217a5\n",
            "ingestionTime": 1647838828689
        },
        {
            "timestamp": 1647838819666,
            "message": "REPORT RequestId: 37e94215-8d36-4ea8-a5cd-b850f20217a5\tDuration: 1.51 ms\tBilled Duration: 2 ms\tMemory Size: 128 MB\tMax Memory Used: 36 MB\t\n",
            "ingestionTime": 1647838828689
        }
    ],
    "nextForwardToken": "f/36748033645618918877095797763546468513774900547129573386/s",
    "nextBackwardToken": "b/36748033645596618131897267140404932795502252185623592966/s"
}

ちなみに

Lambda コンソールから実行した場合は以下のように見えます。

「テスト」タブから実行した場合。

hello-world-python_-_Lambda_test

「コード」タブから実行した場合。

hello-world-python_-_Lambda_code

終わりに

Lambda コンテキストによりログ出力先のログストリーム名を取得してみました。今回は Python の例を試したましたが、他の言語のランタイムでも同様のことが実現できます。

こういったこともできる、と覚えておくとどこかで役に立つかもしれません。

以上、 チバユキ (@batchicchi) がお送りしました。