AWS Lambda Powertools Python入門 第 3 回 ~Logger Utility

2022-05-02
デベロッパーのためのクラウド活用方法

Author : 福井 厚

皆さん、こんにちは。AWSソリューションアーキテクトの福井です。
第 2 回 では、AWS Lambda Powertools Python の Tracer ユーティリティーの使い方についてご紹介しました。

今回は Core Utilities の中の Logger についてご紹介します。なお、本シリーズでは、AWS Lambda Powertools Python 1.25.1 を対象としています。

この連載記事のその他の記事はこちら

選択
  • 選択
  • AWS Lambda Powertools Python 入門 第 1 回
  • 第 2 回 ~Tracer Utility
  • 第 3 回 ~Logger Utility

Logger (Core Utilities)

Logger は JSON で構造化されたアウトプットをログとして提供します。Logger の主な機能は以下の通りです。

  • Lambda コンテキスト、コールドスタートの情報をキャプチャし、JSON で構造化されたログを出力
  • 呼び出された時に Lambda のイベントをロギング (デフォルトでは無効)
  • リクエストのパーセンテージでログのサンプリングを DEBUG ログレベルで有効化可能 (デフォルトは無効)
  • どの時点でも構造化されたログにキーを追加可能

利用方法

AWS Lambda Powertools PythonをLambda関数に組み込む方法については、第 1 回 の記事を参照ください。Logger を利用する際は、以下の 2 つの設定を行います。環境変数でもコンストラクタのパラメータでも設定可能です。

設定 説明 環境変数 コンストラタのパラメータ
ログレベル どの詳細度の Logger に設定するか (INFO がデフォルト) LOG_LEVEL level
サービス すべてのログストリームに渡って存在するServiceキーを設定 POWERTOOLS_SERVICE_NAME service

AWS Serverless Application Model (SAM) の例です。(抜粋)

img_lambda-powertools-python-3_01

Logger を利用するために Logger をインポートしてインスタンス化します。Lambda ハンドラーを含む app.py の先頭でインポートします。

img_lambda-powertools-python-3_02

標準の構造化キー

Logger は構造化ログを生成し、以下のキーを含みます。

キー メモ
level: [str] INFO ログのレベル
location:[str] collect.handler:1 文が実行されたソースコードの場所
message: {Any] Collecting payment 文字列型にキャストされた JSON の値
timestamp: [str] 2021-05-03 10:20:19, 650+0200 ミリ秒まで含む Timestamp、デフォルトではローカルタイムゾーン を利用
service: [str] payment 定義されているサービス名
xray_trace_id: [str] 1-5759e988-bd862e3fe1be46a994272793 トレースが有効な場合に X-Ray のトレース ID を表示
sampling_rate: [float] 0.1 有効化されている場合、サンプリングレートをパーセンテージで表示 : 例) 10%
exception_name: [str] VlueError 例外が発生し、logger.exception が使用された時
exception: [str] Traceback (most recent call last)... 例外が発生し、logger.exception が使用された時

Lambda コンテキスト情報の取得

inject_lambda_context デコレーター (①) によって、構造化された Lambda コンテキスト情報を含むログを出力することができます。

img_lambda-powertools-python-3_03

②のログは CloudWatch Logs 上で以下のような出力になります。

{ 
  "level": "INFO", 
  "location": "lambda_handler:8", 
  "message": "start handler!", 
  "timestamp": "2022-03-25 07:25:54,563+0000", 
  "service": "logger-demo", 
  "cold_start": true, 
  "function_name": "logger-demo-LoggerDemoFunction-xxxxxxxxxxx",
  "function_memory_size": "128",
  "function_arn": "arn:aws:lambda:us-west-2:xxxxxxxxxxxx:function:logger-demo-LoggerDemoFunction-xxxxxxxx", 
  "function_request_id": "9c5a35c7-552d-46f1-a6a5-3a03a5153ff4", 
  "xray_trace_id": "1-623d6e82-6dd0ea50736a04742a5ae904" 
}

同様に、③のログは CloudWatch Logs 上で以下のような出力になります。

img_lambda-powertools-python-3_04

それぞれのログでロケーション情報 (ハンドラー名 : 行番号)、タイムスタンプ、サービス名、コールドスタートが発生したかどうか、Lambda 関数名、Lambda 関数のメモリサイズ、Lambda 関数の ARN、リクエスト ID、X-Ray のトレース ID が含まれる JSON 形式のログが生成されます。

入力イベントのロギング

本番環境以外のデバッグ時は、log_event パラメータ、または POWERTOOLS_LOGGER_LOG_EVENT 環境変数の指定で、入力イベントを保存するように Logger を操作することができます。

img_lambda-powertools-python-3_25

注意:センシティブな情報が記録されることを防ぐため、この機能はデフォルトでは無効化されています。

入力イベントを有効化すると以下のようなログが出力されます。こちらは Amazon API Gateway から Lambda Proxy 統合として呼び出された時の入力イベントの例です。

{
    "level": "INFO",
    "location": "decorate:352",
    "message": {
        "resource": "/hello",
        "path": "/hello/",
        "httpMethod": "GET",
        "headers": {
            "Accept": "*/*",
            "CloudFront-Forwarded-Proto": "https",
            "CloudFront-Is-Desktop-Viewer": "true",
            "CloudFront-Is-Mobile-Viewer": "false",
            "CloudFront-Is-SmartTV-Viewer": "false",
            "CloudFront-Is-Tablet-Viewer": "false",
            "CloudFront-Viewer-Country": "JP",
            "Host": "nyuxxno67c.execute-api.us-west-2.amazonaws.com",
            "User-Agent": "curl/7.77.0",
            "Via": "2.0 6c3f41b7aee179237a7e6f3f127xxxxx.cloudfront.net (CloudFront)",
            "X-Amz-Cf-Id": "a4TjfIdP5gHp0bPZq0pfDdZKwGcirxw8rmBIhIRFNwOMII8_NAeZnQ==",
            "X-Amzn-Trace-Id": "Root=1-623d855d-68ad6ddf4dd9d36145490c33",
            "X-Forwarded-For": "27.xxx.xxx.xxx, 130.xxx.xxx.xxx",
            "X-Forwarded-Port": "443",
            "X-Forwarded-Proto": "https"
        },
        "multiValueHeaders": {
            "Accept": [
                "*/*"
            ],
            "CloudFront-Forwarded-Proto": [
                "https"
            ],
            "CloudFront-Is-Desktop-Viewer": [
                "true"
            ],
            "CloudFront-Is-Mobile-Viewer": [
                "false"
            ],
            "CloudFront-Is-SmartTV-Viewer": [
                "false"
            ],
            "CloudFront-Is-Tablet-Viewer": [
                "false"
            ],
            "CloudFront-Viewer-Country": [
                "JP"
            ],
            "Host": [
                "nyuxxno67c.execute-api.us-west-2.amazonaws.com"
            ],
            "User-Agent": [
                "curl/7.77.0"
            ],
            "Via": [
                "2.0 6c3f41b7aee179237a7e6f3f127xxxxx.cloudfront.net (CloudFront)"
            ],
            "X-Amz-Cf-Id": [
                "a4TjfIdP5gHp0bPZq0pfDdZKwGcirxw8rmBIhIRFNwOMII8_NAeZnQ=="
            ],
            "X-Amzn-Trace-Id": [
                "Root=1-623d855d-68ad6ddf4dd9d36145490c33"
            ],
            "X-Forwarded-For": [
                "27.xxx.xxx.xxx, 130.xxx.xxx.xxx"
            ],
            "X-Forwarded-Port": [
                "443"
            ],
            "X-Forwarded-Proto": [
                "https"
            ]
        },
        "queryStringParameters": null,
        "multiValueQueryStringParameters": null,
        "pathParameters": null,
        "stageVariables": null,
        "requestContext": {
            "resourceId": "3or7ty",
            "resourcePath": "/hello",
            "httpMethod": "GET",
            "extendedRequestId": "PiHGrElyPHcF3aA=",
            "requestTime": "25/Mar/2022:09:03:25 +0000",
            "path": "/Prod/hello/",
            "accountId": "786032344772",
            "protocol": "HTTP/1.1",
            "stage": "Prod",
            "domainPrefix": "nyuxxno67c",
            "requestTimeEpoch": 1648199005720,
            "requestId": "47ed9499-5778-405d-a41d-0f02f76cab3f",
            "identity": {
                "cognitoIdentityPoolId": null,
                "accountId": null,
                "cognitoIdentityId": null,
                "caller": null,
                "sourceIp": "27.0.3.153",
                "principalOrgId": null,
                "accessKey": null,
                "cognitoAuthenticationType": null,
                "cognitoAuthenticationProvider": null,
                "userArn": null,
                "userAgent": "curl/7.77.0",
                "user": null
            },
            "domainName": "xxxxxxxxxx.execute-api.us-west-2.amazonaws.com",
            "apiId": "nyuxxno67c"
        },
        "body": null,
        "isBase64Encoded": false
    },
    "timestamp": "2022-03-25 09:03:26,083+0000",
    "service": "logger-demo",
    "cold_start": true,
    "function_name": "logger-demo-LoggerDemoFunction-xxxxxxxxx",
    "function_memory_size": "128",
    "function_arn": "arn:aws:lambda:us-west-2:xxxxxxxxxxxx:function:logger-demo-LoggerDemoFunction-xxxxxxxxxx",
    "function_request_id": "734462fd-1faf-41a6-838a-2e1da8c1ebb4",
    "xray_trace_id": "1-623d855d-68ad6ddf4dd9d36145490c33"
}

Correlation ID の設定

JMESPath 表現を指定した correlation_id_path パラメータを利用することで、入力イベントの特定の Correlation ID (相関関係 ID) をセットすることができます。設定した Correlation ID は、get_correlation_id メソッドで取得することができます。例えば Amazon API Gateway で公開している API に対してリクエストヘッダにアプリケーション独自のヘッダ情報 (“myapp_status”: “some value” など) を定義している場合、Correlation ID として関連づけるためには以下のように指定します。

img_lambda-powertools-python-3_06

この結果の CloudWatch Logs の内容は、以下のように correlation_id が追加され、関連付けたヘッダの値が表示されます。

img_lambda-powertools-python-3_07

Logger Utility では、良く利用されるイベントソースについては組み込みの JMESPath 表現を提供しています。inject_lambda_context デコレーターにこれらの JMESPath 表現を利用することができます。

名前 JMESPath 表現 説明
API_GATEWAY_REST "requestContext.requestId" API Gateway REST API request ID
API_GATEWAY_HTTP "requestContext.requestId" API Gateway HTTP API request ID
APPSYNC_RESOLVER 'request.headers."x-amzn-trace-id"' AppSync X-Ray Trace ID
APPLICATION_LOAD_BALANCER 'headers."x-amzn-trace-id"' ALB X-Ray Trace ID
EVENT_BRIDGE "id" EventBridge Event ID
img_lambda-powertools-python-3_08

上記のコードを実行すると、CloudWatch Logs には、correlation_id に、API Gateway REST API request ID が設定されます。

img_lambda-powertools-python-3_09

新しいキーをログに追加する

追加のキーを加えるには以下の 2 つの方法があります。

  • append_keys メソッドを使用して将来のすべてのログメッセージに渡って利用するキーを保存
  • extra パラメータを使用してログメッセージ単位にキーを追加する

append_keys メソッド

独自のキーを Logger に追加するために append_keys(**additional_key_value) メソッドを使用します。今回は、GET メソッドのリクエストパラメータとして https://[api_url]/?order_id=0001 のようなクエリ文字列を指定することを想定して、order_id という独自のキーを追加したいと思います。

img_lambda-powertools-python-3_10

このコードを実行した結果の CloudWatch Logs ログは、以下のように order_id が追加されます。なお、order_id に値が無い場合は、Logger はキーを追加しません。

img_lambda-powertools-python-3_11

extra パラメータ

extra パラメータは、logger.infologger.warning など、すべてのログレベルメソッドで利用可能です。すべての dictionary を受付け、すべての keyword 引数は構造化ログのルート配下のパーツとして追加されます。extra パラメータでログに追加された keyward 引数は、指定したメッセージにのみ有効で、後続のメッセージには引き継がれません。

img_lambda-powertools-python-3_12

上記のコードを実行した場合の CloudWatch Logs ログは、以下のようになります。

img_lambda-powertools-python-3_13

set_correlation_id メソッド

既存の Logger に文字列値をパラメータに set_correlation_id メソッドを使用して correlation_id を設定することができます。

img_lambda-powertools-python-3_14

Data Classes utility を利用して、ドット記述オブジェクトを利用して Logger を設定することもできます。

img_lambda-powertools-python-3_15

このコードを実行すると correlation_id に指定した値が設定されて追加されます。

img_lambda-powertools-python-3_16

追加したキーの削除

remove_keys メソッドを使用して追加したキーを削除することができます。

img_lambda-powertools-python-3_17

Logging 例外

logger.exception メソッドを使用することで、例外に関するコンテキスト情報をロギングすることができます。Logger は、exception_name と exception のキーを含みます。これはトラブルシュートで役立ち、エラーを列挙してくれます。

img_lambda-powertools-python-3_18

上記のコードを実行した場合の CloudWatch Logs ログは、以下のようになります。

img_lambda-powertools-python-3_19

Unit Test の実行

inject_lambda_context デコレーターを利用したハンドラに対して Unit Test を実行する時は、ダミーの Lambda Context を渡す必要があります。そうしないと Logger の実行が失敗します。次の pytest のサンプルは Logger が成功するために必要な、最小限の Lambda Context 情報を渡してます。(Python の dataclasses は Python 3.7 以降でのみ利用可能です。)

img_lambda-powertools-python-3_20

まとめ

今回は、AWS Lambda Powertools Python の Logger について紹介しました。Logger を利用することで構造化されたログを簡単に追加できることがお分かり頂けたと思います。また Lambda Context 情報のログへの書き込み、correlation ID の指定、Logger のキーの追加、削除が簡単に行えることも紹介しました。

皆様の Lambda 関数に AWS Lambda Powertools Python を組み込んでご活用ください。
次回は Metrics を紹介します。どうぞお楽しみに。

この連載記事のその他の記事はこちら

選択
  • 選択
  • AWS Lambda Powertools Python 入門 第 1 回
  • 第 2 回 ~Tracer Utility
  • 第 3 回 ~Logger Utility

builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます


筆者プロフィール

photo_fukui-atsushi_2021

福井 厚
アマゾン ウェブ サービス ジャパン合同会社
シニアソリューションアーキテクト サーバーレススペシャリスト

2015 年からアマゾンウェブサービスジャパンでソリューションアーキテクトとして活動。サーバーレススペシャリストとして日々モダンアプリケーション開発とサーバーレスの活用の技術支援を行なっています。

AWS のベストプラクティスを毎月無料でお試しいただけます

さらに最新記事・デベロッパー向けイベントを検索

下記の項目で絞り込む
絞り込みを解除 ≫
1

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する