Java 中的 AWS Lambda 函數記錄 - AWS Lambda

Java 中的 AWS Lambda 函數記錄

AWS Lambda 會代表您自動監控 Lambda 函數,並將函數指標傳送至 Amazon CloudWatch。您的 Lambda 函數隨附有 CloudWatch Logs 日誌群組,且函數的每一執行個體各有一個日誌串流。Lambda 執行時間環境會將每次叫用的詳細資訊傳送至日誌串流,並且轉傳來自函數程式碼的日誌及其他輸出。

此頁面說明如何從 Lambda 函數的程式碼產生日誌輸出,或使用 AWS Command Line Interface、Lambda 主控台或 CloudWatch 主控台來存取日誌。

建立傳回日誌的函數

若要由您的函數程式碼輸出日誌,您可以使用 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。

  • 持續時間 - 函數的處理常式方法處理事件所花費的時間量。

  • 計費持續時間 - 叫用的計費時間量。

  • 記憶體大小 - 分配給函數的記憶體數量。

  • 使用的記憶體上限 - 函數所使用的記憶體數量。

  • 初始化持續時間 - 對於第一個提供的請求,這是執行時間載入函數並在處理常式方法之外執行程式碼所花費的時間量。

  • XRAY TraceId - 對於追蹤的請求,這是 AWS X-Ray 追蹤 ID

  • SegmentId - 對於追蹤的請求,這是 X-Ray 區段 ID。

  • 已取樣 - 對於追蹤的請求,這是取樣結果。

使用 Lambda 主控台

您可以在叫用 Lambda 函數之後,使用 Lambda 主控台來檢視日誌輸出。如需詳細資訊,請參閱 存取 AWS Lambda 的 Amazon CloudWatch 日誌

使用 CloudWatch 主控台

您可以使用 Amazon CloudWatch 主控台來檢視所有 Lambda 函數叫用的日誌。

若要在 CloudWatch 主控台上檢視日誌

  1. 在 CloudWatch 主控台上開啟 Log groups (日誌群組) 頁面。

  2. 選擇您的函數的日誌群組 (/aws/lambda/your-function-name)。

  3. 選擇日誌串流

每個日誌串流都會對應至函式的執行個體。當您更新 Lambda 函數,以及建立額外執行個體以處理多個並行叫用時,便會出現日誌串流。若要尋找特定叫用的日誌,建議使用 AWS X-Ray 來檢測函數。X-Ray 會在追蹤內記錄有關請求和日誌串流的詳細資訊。

若要使用會讓日誌和追蹤與 X-Ray 相互關聯的範例應用程式,請參閱 適用於 AWS Lambda 的錯誤處理器範例應用程式

使用 AWS Command Line Interface​ (AWS CLI​)

AWS CLI 是開放原始碼工具,可讓您在命令列 shell 中使用命令來與 AWS 服務互動。若要完成本節中的步驟,您必須執行下列各項:

您可以透過 AWS CLI,使用 --log-type 命令選項來擷取要叫用的日誌。其回應將包含 LogResult 欄位,內含該次叫用的 base64 編碼日誌 (最大達 4 KB)。

範例 擷取日誌 ID

下列範例顯示如何從名稱為 my-function 的函數的 LogResult 欄位來擷取日誌 ID

aws lambda invoke --function-name my-function out --log-type Tail

您應該會看到下列輸出:

{ "StatusCode": 200, "LogResult": "U1RBUlQgUmVxdWVzdElkOiA4N2QwNDRiOC1mMTU0LTExZTgtOGNkYS0yOTc0YzVlNGZiMjEgVmVyc2lvb...", "ExecutedVersion": "$LATEST" }

範例 解碼日誌

在相同的命令提示中,使用 base64 公用程式來解碼日誌。下列範例顯示如何擷取 my-function 的 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: 80 ms Memory Size: 128 MB Max Memory Used: 73 MB

base64 公用程式可在 Linux、macOS 和 Ubuntu on Windows 上使用。macOS 使用者可能需要使用 base64 -D

範例 get-logs.sh 指令碼

在相同的命令提示中,使用下列指令碼下載最後五個日誌事件。該指令碼使用 sed 以從輸出檔案移除引述,並休眠 15 秒以使日誌可供使用。輸出包括來自 Lambda 的回應以及來自 get-log-events 命令的輸出。

複製下列程式碼範例的內容,並將您的 Lambda 專案目錄儲存為 get-logs.sh

如果您使用 AWS CLI 的第 2 版,則需要 cli-binary-format 選項。您也可以在 AWS CLI config 檔案中設定此選項。

#!/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 stream1 --limit 5

範例 macOS 和 Linux (僅限)

在相同的命令提示中,macOS 和 Linux 使用者可能需要執行下列命令,以確保指令碼可執行。

chmod -R 755 get-logs.sh

範例 擷取最後五個記錄事件

在相同的命令提示中,執行下列指令碼以取得最後五個日誌事件。

./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: 27 ms \tMemory Size: 128 MB\tMax Memory Used: 75 MB\t\n", "ingestionTime": 1559763018353 } ], "nextForwardToken": "f/34783877304859518393868359594929986069206639495374241795", "nextBackwardToken": "b/34783877303811383369537420289090800615709599058929582080" }

刪除日誌

當您刪除函數時,不會自動刪除日誌群組。若要避免無限期地儲存日誌,請刪除日誌群組,或設定保留期間,系統會在該時間之後自動刪除日誌。

使用 Log4j 2 和 SLF4J 進行進階日誌記錄

注意

AWS Lambda 在其受管執行時間或基礎容器映像中不包含 Log4j2。因此,這些問題不受 CVE-2021-44228、CVE-2021-45046 以及 CVE-2021-45105 中描述的問題的影響。

對於客戶函數包含受影響的 Log4j2 版本的情況,我們已將變更套用至 Lambda Java 受管執行時間基礎容器映像,這有助於緩解 CVE-2021-44228、CVE-2021-45046 和 CVE-2021-45105 中的問題。由於此變更,使用 Log4J2 的客戶可能會看到一個額外的日誌條目,類似於「Transforming org/apache/logging/log4j/core/lookup/JndiLookup (java.net.URLClassLoader@...)」。在 Log4J2 輸出中參考 jndi 映射器的任何日誌字串都將替換為「Patched JndiLookup::lookup()」。

除此變更之外,我們強烈建議其函數包含 Log4j2 的所有客戶將 Log4j2 更新至最新版本。具體而言,在函數中使用 aws-lambda-java-log4j2 程式庫的客戶應更新至 1.5.0 版 (或更高版本),並重新部署其函數。此版本將基礎 Log4j2 公用程式相依性更新為 2.17.0 版 (或更高版本)。更新後的 aws-lambda-java-log4j2 二進位文件可在 Maven 儲存庫中找到,而其原始碼可在 GitHub 中找到。

若要自訂日誌輸出、在單元測試期間支援日誌記錄,以及記錄 AWS 開發套件呼叫,請使用 Apache Log4j 2 搭配 SLF4J。Log4j 是適用於 Java 程式的日誌程式庫,讓您能夠配置日誌級別和使用附加器程式庫。SLF4J 是一個外觀程式庫,可讓您改變使用的程式庫,而無須您的函數程式碼。

若要將請求 ID 新增至函數的日誌中,請使用 aws-lambda-java-log4j2 程式庫中的附加器。下面的範例為 Log4j 2 的組態文件,其中示範如何將一個時間戳記和請求 ID 新增到所有日誌。

範例 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 轉接器包含在 classpath 中。透過在建置配置中更改程式庫,您可以在不更改函數代碼的情況下更改記錄器類型。需要 SLF4J 才能從 SDK for 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 附加器和 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.11.0' implementation 'com.google.code.gson:gson:2.8.6' implementation 'org.apache.logging.log4j:log4j-api:2.17.1' implementation 'org.apache.logging.log4j:log4j-core:2.17.1' runtimeOnly 'org.apache.logging.log4j:log4j-slf4j18-impl:2.17.1' runtimeOnly 'com.amazonaws:aws-lambda-java-log4j2:1.5.1' 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 - 一種 Java 函數,它示範如何使用 Lambda 的 Java 程式庫、記錄、環境變數、層、AWS X-Ray 追蹤、單元測試和 AWS 開發套件。

  • java-basic - 具有單元測試和變數日誌記錄組態的最小 Java 函數。

  • java-events - 使用 aws-lambda-java-events 程式庫最新版本 (3.0.0 及更新版本) 的最小 Java 函數。這些範例不需要 AWS 開發套件做為相依項目。

  • s3-java - 一種 Java 函數,它處理來自 Amazon S3 的通知事件,並使用 Java Class Library (JCL) 以從上傳的映像檔案建立縮圖。

java-basic 範例應用程式會顯示支援日誌記錄測試的最小日誌記錄組態。處理常式程式碼會使用內容物件提供的 LambdaLogger 記錄器。對於測試,應用程式會使用實作具有 Log4j 2 記錄器的 LambdaLogger 介面的自訂 TestLogger 類別。它會使用 SLF4J 作為與 AWS 開發套件兼容的外觀。建置輸出中會排除記錄程式庫,使部署套件不會變太大。

blank-java 範例應用程式會建置在具有 AWS 開發套件日誌記錄和 Lambda Log4j 2 附加器的基本組態上。它會在 Lambda 中使用 Log4j 2 和自訂附加器,在每個資料行中新增呼叫請求 ID。