Java の AWS Lambda 関数ログ作成 - AWS Lambda

Java の AWS Lambda 関数ログ作成

Lambda 関数には、CloudWatch Logs ロググループが付属しており、関数のインスタンスごとにログストリームを含みます。ランタイムは、各呼び出しに関する詳細をログストリームに送信し、関数のコードのログやその他の出力を中継します。

関数コードからログを出力するには、java.lang.System のメソッドを使用するか、stdoutstderr に書き込む任意のログ記録モジュールを使用できます。aws-lambda-java-core ライブラリには、コンテキストオブジェクトからアクセスできる LambdaLogger という名前のロガークラスが用意されています。ロガークラスは複数行のログをサポートしています。

以下の例では、コンテキストオブジェクトによって提供される LambdaLogger ロガーを使用しています。

例 Handler.java

// Handler value: example.Handler public class Handler implements RequestHandler<Object, String>{ Gson gson = new GsonBuilder().setPrettyPrinting().create(); @Override public String handleRequest(Object event, Context context) { LambdaLogger logger = context.getLogger(); String response = new String("SUCCESS"); // log execution details logger.log("ENVIRONMENT VARIABLES: " + gson.toJson(System.getenv())); logger.log("CONTEXT: " + gson.toJson(context)); // process event logger.log("EVENT: " + gson.toJson(event)); return response; } }

例 ログの形式

START RequestId: 6bc28136-xmpl-4365-b021-0ce6b2e64ab0 Version: $LATEST ENVIRONMENT VARIABLES: { "_HANDLER": "example.Handler", "AWS_EXECUTION_ENV": "AWS_Lambda_java8", "AWS_LAMBDA_FUNCTION_MEMORY_SIZE": "512", ... } CONTEXT: { "memoryLimit": 512, "awsRequestId": "6bc28136-xmpl-4365-b021-0ce6b2e64ab0", "functionName": "java-console", ... } EVENT: { "records": [ { "messageId": "19dd0b57-xmpl-4ac1-bd88-01bbb068cb78", "receiptHandle": "MessageReceiptHandle", "body": "Hello from SQS!", ... } ] } END RequestId: 6bc28136-xmpl-4365-b021-0ce6b2e64ab0 REPORT RequestId: 6bc28136-xmpl-4365-b021-0ce6b2e64ab0 Duration: 198.50 ms Billed Duration: 200 ms Memory Size: 512 MB Max Memory Used: 90 MB Init Duration: 524.75 ms

Java ランタイムは、呼び出しごとに STARTEND、および REPORT の各行を記録します。レポート行には、次の詳細が示されます。

レポートログ

  • RequestId – 呼び出しの一意のリクエスト ID。

  • 所要時間 – 関数のハンドラーメソッドがイベントの処理に費やした時間。

  • 課金期間 – 呼び出しの課金対象の時間。

  • メモリサイズ – 関数に割り当てられたメモリの量。

  • 使用中の最大メモリ– 関数によって使用されているメモリの量。

  • Init Duration (初期所要時間) – 最初に処理されたリクエストについて、ハンドラーメソッド外で関数をロードしてコードを実行するためにランタイムにかかった時間。

  • XRAY TraceId – トレースされたリクエストの場合、AWS X-Ray のトレース ID

  • SegmentId – トレースされたリクエストの場合、X-Ray のセグメント ID。

  • Sampled (サンプリング済み) – トレースされたリクエストの場合、サンプリング結果。

ログは、Lambda コンソール、CloudWatch Logs コンソール、またはコマンドラインで表示することができます。

AWS マネジメントコンソール でログを表示する

関数設定ページで関数を設定すると、Lambda コンソールによってログ出力が示されます。すべての呼び出しのログを表示するには、CloudWatch Logs コンソールを使用します。

Lambda 関数のログを表示するには

  1. CloudWatch コンソールのログページを開きます。

  2. 関数 (/aws/lambda/function-name) のロググループの名前を選択します。

  3. リストの最初のストリームを選択します。

各ログストリームは、関数のインスタンスに相当します。新しいストリームは、関数を更新する際や、複数の同時呼び出しを処理するために追加のインスタンスを作成する際に表示されます。特定の呼び出しのログを検索するには、X-Ray で関数を計測し、リクエストとログストリームに関する詳細をトレースに記録することができます。ログとトレースを X-Ray と関連付けるサンプルアプリケーションについては、「AWS Lambda の Error Processor サンプルアプリケーション」を参照してください。

AWS CLI の使用

コマンドラインから呼び出しのログを取得するには、--log-type オプションを使用します。レスポンスには、LogResult フィールドが含まれます。このフィールドには、呼び出しから base64 コードされた最大 4 KB のログが含まれます。

$ aws lambda invoke --function-name my-function out --log-type Tail { "StatusCode": 200, "LogResult": "U1RBUlQgUmVxdWVzdElkOiA4N2QwNDRiOC1mMTU0LTExZTgtOGNkYS0yOTc0YzVlNGZiMjEgVmVyc2lvb...", "ExecutedVersion": "$LATEST" }

このログをデコードするには、base64 ユーティリティを使用します。

$ aws lambda invoke --function-name my-function out --log-type Tail \ --query 'LogResult' --output text | base64 -d START RequestId: 57f231fb-1730-4395-85cb-4f71bd2b87b8 Version: $LATEST "AWS_SESSION_TOKEN": "AgoJb3JpZ2luX2VjELj...", "_X_AMZN_TRACE_ID": "Root=1-5d02e5ca-f5792818b6fe8368e5b51d50;Parent=191db58857df8395;Sampled=0"",ask/lib:/opt/lib", END RequestId: 57f231fb-1730-4395-85cb-4f71bd2b87b8 REPORT RequestId: 57f231fb-1730-4395-85cb-4f71bd2b87b8 Duration: 79.67 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 73 MB

base64 ユーティリティは、Linux、macOS、および Windows の Ubuntu で使用できます。macOS の場合、コマンドは base64 -D です。

コマンドラインから完全なログイベントを取得するには、前の例に示すように、関数の出力にログストリーム名を含めることができます。次の例のスクリプトは、my-function という名前の関数を呼び出し、最後の 5 つのログイベントをダウンロードします。

例 get-logs.sh スクリプト

この例では、my-function がログストリーム ID を返す必要があります。

#!/bin/bash aws lambda invoke --function-name my-function --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

このスクリプトは sed を使用して出力ファイルから引用符を削除し、15 秒間スリープ状態にすることにより、ログを使用できるようにします。この出力には Lambda からのレスポンスと、get-log-events コマンドからの出力が含まれます。

$ ./get-logs.sh { "StatusCode": 200, "ExecutedVersion": "$LATEST" } { "events": [ { "timestamp": 1559763003171, "message": "START RequestId: 4ce9340a-b765-490f-ad8a-02ab3415e2bf Version: $LATEST\n", "ingestionTime": 1559763003309 }, { "timestamp": 1559763003173, "message": "2019-06-05T19:30:03.173Z\t4ce9340a-b765-490f-ad8a-02ab3415e2bf\tINFO\tENVIRONMENT VARIABLES\r{\r \"AWS_LAMBDA_FUNCTION_VERSION\": \"$LATEST\",\r ...", "ingestionTime": 1559763018353 }, { "timestamp": 1559763003173, "message": "2019-06-05T19:30:03.173Z\t4ce9340a-b765-490f-ad8a-02ab3415e2bf\tINFO\tEVENT\r{\r \"key\": \"value\"\r}\n", "ingestionTime": 1559763018353 }, { "timestamp": 1559763003218, "message": "END RequestId: 4ce9340a-b765-490f-ad8a-02ab3415e2bf\n", "ingestionTime": 1559763018353 }, { "timestamp": 1559763003218, "message": "REPORT RequestId: 4ce9340a-b765-490f-ad8a-02ab3415e2bf\tDuration: 26.73 ms\tBilled Duration: 100 ms \tMemory Size: 128 MB\tMax Memory Used: 75 MB\t\n", "ingestionTime": 1559763018353 } ], "nextForwardToken": "f/34783877304859518393868359594929986069206639495374241795", "nextBackwardToken": "b/34783877303811383369537420289090800615709599058929582080" }

ログの削除

関数を削除してもロググループが自動的に削除されることはありません。ログが無期限に保存されないようにするには、ロググループを削除するか、自動削除のための保持期間を設定します。

Log4j 2 および SLF4J による高度なログ記録

ログ出力をカスタマイズし、単体テスト中のログ記録をサポートし、AWS SDK 呼び出しをログに記録するには、SLF4J で Apache Log4j 2 を使用します。Log4j は、ログレベルを設定し、アペンダーライブラリを使用できるようにする Java プログラムのログ記録ライブラリです。SLF4J は、関数コードを変更せずに使用するライブラリを切り替えることができるファサードライブラリです。

関数のログにリクエスト ID を追加するには、aws-lambda-java-log4j2 ライブラリのアペンダーを使用します。以下の例では、すべてのログにタイムスタンプとリクエスト ID を追加する Log4j 2 設定ファイルを示しています。

src/main/resources/log4j2.xml – アペンダー設定

<Configuration status="WARN"> <Appenders> <Lambda name="Lambda"> <PatternLayout> <pattern>%d{yyyy-MM-dd HH:mm:ss} %X{AWSRequestId} %-5p %c{1} - %m%n</pattern> </PatternLayout> </Lambda> </Appenders> <Loggers> <Root level="INFO"> <AppenderRef ref="Lambda"/> </Root> <Logger name="software.amazon.awssdk" level="WARN" /> <Logger name="software.amazon.awssdk.request" level="DEBUG" /> </Loggers> </Configuration>

この設定では、各行の先頭に日付、時刻、リクエスト ID、ログレベル、クラス名が追加されます。

例 アペンダーのログ形式

START RequestId: 6bc28136-xmpl-4365-b021-0ce6b2e64ab0 Version: $LATEST 2020-03-18 08:52:43 6bc28136-xmpl-4365-b021-0ce6b2e64ab0 INFO Handler - ENVIRONMENT VARIABLES: { "_HANDLER": "example.Handler", "AWS_EXECUTION_ENV": "AWS_Lambda_java8", "AWS_LAMBDA_FUNCTION_MEMORY_SIZE": "512", ... } 2020-03-18 08:52:43 6bc28136-xmpl-4365-b021-0ce6b2e64ab0 INFO Handler - CONTEXT: { "memoryLimit": 512, "awsRequestId": "6bc28136-xmpl-4365-b021-0ce6b2e64ab0", "functionName": "java-console", ... }

SLF4J は、Java コードでのログ記録のためのファサードライブラリです。関数コードで、SLF4J ロガーファクトリを使用して、info()warn() などのログレベルのメソッドにより、ロガーを取得します。ビルド設定で、ログ記録ライブラリと SLF4J アダプターをクラスパスに含めます。ビルド設定のライブラリを変更することで、関数コードを変更せずにロガータイプを切り替えることができます。SDK for Java からログをキャプチャするには SLF4J が必要です。

以下の例では、ハンドラークラスは SLF4J を使用してロガーを取得しています。

src/main/java/example/Handler.java – SLF4J を使用したログ記録

import org.slf4j.Logger; import org.slf4j.LoggerFactory; // Handler value: example.Handler public class Handler implements RequestHandler<SQSEvent, String>{ private static final Logger logger = LoggerFactory.getLogger(Handler.class); Gson gson = new GsonBuilder().setPrettyPrinting().create(); LambdaAsyncClient lambdaClient = LambdaAsyncClient.create(); @Override public String handleRequest(SQSEvent event, Context context) { String response = new String(); // call Lambda API logger.info("Getting account settings"); CompletableFuture<GetAccountSettingsResponse> accountSettings = lambdaClient.getAccountSettings(GetAccountSettingsRequest.builder().build()); // log execution details logger.info("ENVIRONMENT VARIABLES: {}", gson.toJson(System.getenv())); ...

ビルド設定では、Lambda アペンダーおよび SLF4J アダプターのランタイム依存関係と、Log4J 2 の実装依存関係を使用します。

build.gradle – 依存関係のログ記録

dependencies { implementation platform('software.amazon.awssdk:bom:2.10.73') implementation platform('com.amazonaws:aws-xray-recorder-sdk-bom:2.4.0') implementation 'software.amazon.awssdk:lambda' implementation 'com.amazonaws:aws-xray-recorder-sdk-core' implementation 'com.amazonaws:aws-xray-recorder-sdk-aws-sdk-core' implementation 'com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2' implementation 'com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor' implementation 'com.amazonaws:aws-lambda-java-core:1.2.1' implementation 'com.amazonaws:aws-lambda-java-events:2.2.9' implementation 'com.google.code.gson:gson:2.8.6' implementation 'org.apache.logging.log4j:log4j-api:2.13.0' implementation 'org.apache.logging.log4j:log4j-core:2.13.0' runtimeOnly 'org.apache.logging.log4j:log4j-slf4j18-impl:2.13.0' runtimeOnly 'com.amazonaws:aws-lambda-java-log4j2:1.2.0' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.0' }

テスト用にローカルでコードを実行する場合、Lambda ロガーへのアクセスにコンテキストオブジェクトは使用できず、Lambda アペンダーが使用するリクエスト ID はありません。テスト設定の例については、次のセクションのサンプルアプリケーションを参照してください。

サンプルログ記録コード

このガイドの GitHub リポジトリには、さまざまなログ記録設定の使用方法を示すサンプルアプリケーションが含まれています。各サンプルアプリケーションには、簡易のデプロイとクリーンアップ用のスクリプト、AWS SAM テンプレート、サポートリソースが含まれています。

Java のサンプル Lambda アプリケーション

  • blank-java – Lambda の Java ライブラリ、ロギング、環境変数、レイヤー、AWS X-Ray トレース、単体テスト、および AWS SDK の使用を示す Java 関数。

  • java-basic – 単体テストと変数ログ記録設定を使用する、最小限の Java 関数。

  • java-events – Amazon API Gateway など、依存関係として AWS SDK を必要としないイベントタイプで、aws-lambda-java-events ライブラリを使用する最小限の Java 関数。

  • java-events-v1sdk – Amazon Simple Storage Service や Amazon DynamoDB、Amazon Kinesis など、依存関係として AWS SDK を必要とするイベントタイプで、aws-lambda-java-events ライブラリを使用する Java 関数。

  • s3-java – Amazon S3 からの通知イベントを処理し、Java Class Library (JCL) によりアップロード済みイメージファイルからサムネイルを作成する、Java 関数。

java-basic サンプルアプリケーションは、ログ記録テストをサポートする最小限のログ記録設定を示しています。ハンドラーコードは、コンテキストオブジェクトによって提供される LambdaLogger ロガーを使用します。テストでは、アプリケーションは、Log4j 2 ロガーとの LambdaLogger インターフェイスを実装するカスタム TestLogger クラスを使用します。また、AWS SDK との互換性のためのファサードとして SLF4J を使用します。ログ記録ライブラリは、デプロイパッケージを小さく保つために、ビルド出力から除外しています。

blank-java サンプルアプリケーションは、AWS SDK ログ記録と Lambda Log4j 2 アペンダーを使用した基本設定に基づいてビルドされています。また、Lambda の Log4j 2 と共に、呼び出しリクエスト ID を各行に追加するカスタムアペンダーを使用しています。