Java 中的 AWS Lambda 函数日志记录 - AWS Lambda
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 AWS 服务入门

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 运行时记录每次调用的 STARTENDREPORT 行。报告行提供了以下详细信息:

报告日志

  • RequestId – 调用的唯一请求 ID。

  • Duration (持续时间) – 函数的处理程序方法处理事件所花费的时间。

  • Billed Duration (计费持续时间) – 针对调用计费的时间量。

  • Memory Size (内存大小) – 分配给函数的内存量。

  • Max Memory Used (最大内存使用量) – 函数使用的内存量。

  • 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 错误处理器示例应用程序

使用 AWS CLI

要从命令行获取调用的日志,请使用 --log-type 选项。响应包含一个 LogResult 字段,该字段包含来自调用的多达 4 KB 的 base64 编码日志。

$ 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 和 Ubuntu on Windows 上可用。对于 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 开发工具包调用,请将 Apache Log4j 2 与 SLF4J 结合使用。Log4j 是 Java 程序的日志库,这些程序使您能够配置日志级别和使用 Appender 库。SLF4J 是一个 Facade 库,可让您更改您使用的库,而不更改函数代码。

要将请求 ID 添加到函数的日志中,请使用 aws-lambda-java-log4j2 库中的 Appender。以下示例显示向所有日志添加时间戳和请求 ID 的 Log4j 2 配置文件。

src/main/resources/log4j2.xml – Appender 配置

<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、日志级别和类名。

例 Appender 的日志格式

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 代码中进行日志记录的 Facade 库。在函数代码中,您可以使用 SLF4J 记录器工厂,通过适用于日志级别(info()warn())的方法来检索记录器。在构建配置中,您可以在类路径中包含日志记录库和 SLF4J 适配器。通过更改构建配置中的库,您可以在不更改函数代码的情况下更改记录器类型。SLF4J 需要从 适用于 Java 的开发工具包 中捕获日志。

在以下示例中,处理程序类使用 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 Appender 和 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:3.1.0' 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 记录器的上下文对象不可用,并且没有请求 ID 供 Lambda Appender 使用。有关测试配置示例,请参阅下一节中的示例应用程序。

日志记录代码示例

本指南的 GitHub 存储库包括演示如何使用各种日志记录配置的示例应用程序。每个示例应用程序都包含用于轻松部署和清理的脚本、一个 AWS SAM 模板和支持资源。

Java 中的示例 Lambda 应用程序

  • blank-java – 一个 Java 函数,用于显示 Lambda 的 Java 库、日志记录、环境变量、层、AWS X-Ray 跟踪、单元测试和 AWS 开发工具包的使用情况。

  • java-basic – 具有单元测试和可变日志记录配置的最小 Java 函数。

  • java-events – 一个最小的 Java 函数,它将 aws-lambda-java-events 库与不需要 AWS 开发工具包作为依赖项的事件类型(例如 Amazon API Gateway)结合使用。

  • java-events-v1sdk – 一个 Java 函数,它将 aws-lambda-java-events 库与需要 AWS 开发工具包作为依赖项的事件类型(Amazon Simple Storage Service、Amazon DynamoDB 和 Amazon Kinesis)结合使用。

  • s3-java – 一个 Java 函数,它处理来自 Amazon S3 的通知事件,并使用 Java 类库 (JCL) 从上传的图像文件创建缩略图。

java-basic 示例应用程序显示支持日志记录测试的最小日志记录配置。处理程序代码使用上下文对象提供的 LambdaLogger 记录器。对于测试,应用程序使用一个自定义 TestLogger 类,此类实现带有 Log4j 2 记录器的 LambdaLogger 接口。它使用 SLF4J 作为 Facade 以与 AWS 开发工具包兼容。从构建输出中排除日志记录库,以使部署程序包保持较小。

blank-java 示例应用程序基于具有 AWS 开发工具包日志记录和 Lambda Log4j 2 Appender 的基本配置而构建。它使用 Lambda 中的 Log4j 2 和自定义 Appender,该 Appender 将调用请求 ID 添加到每行。