C# Lambda 函数日志记录和监控
AWS Lambda 将自动监控 Lambda 函数并将日志条目发送到 Amazon CloudWatch。您的 Lambda 函数带有一个 CloudWatch Logs 日志组以及函数的每个实例的日志流。Lambda 运行时系统环境会将每次调用的详细信息以及函数代码的其他输出发送到该日志流。有关 CloudWatch Logs 的更多信息,请参阅将 CloudWatch Logs 与 Lambda 结合使用。
Sections
创建返回日志的函数
要从函数代码输出日志,您可以使用上下文对象上的 ILambdaLoggerstdout
或 stderr
的任何日志记录库。
.NET 运行时记录每次调用的 START
、END
和 REPORT
行。报告行提供了以下详细信息:
REPORT 行数据字段
-
RequestId – 调用的唯一请求 ID。
-
Duration(持续时间)– 函数的处理程序方法处理事件所花费的时间。
-
Billed Duration(计费持续时间)– 针对调用计费的时间量。
-
Memory Size(内存大小)– 分配给函数的内存量。
-
Max Memory Used(最大内存使用量)– 函数使用的内存量。如果调用共享执行环境,Lambda 会报告所有调用使用的最大内存。此行为可能会导致报告值高于预期。
-
Init Duration(初始持续时间)– 对于提供的第一个请求,为运行时在处理程序方法外部加载函数和运行代码所花费的时间。
-
XRAY TraceId – 对于追踪的请求,为 AWS X-Ray 追踪 ID。
-
SegmentId – 对于追踪的请求,为 X-Ray 分段 ID。
-
Sampled(采样)– 对于追踪的请求,为采样结果。
将 Lambda 高级日志记录控件与 .NET 结合使用
为了让您更好地控制如何捕获、处理和使用函数日志,您可以为支持的 .NET 运行时系统配置以下日志记录选项:
-
日志格式 - 为函数日志选择纯文本或结构化的 JSON 格式
-
日志级别 - 对于 JSON 格式的日志,选择 Lambda 发送到 CloudWatch 的日志的详细信息级别,例如 ERROR、DEBUG 或 INFO
-
日志组 - 选择您的函数发送日志的目标 CloudWatch 日志组
有关这些日志记录选项的更多信息以及如何通过配置来使用函数的说明,请参阅 为 Lambda 函数配置高级日志记录控件。
要将日志格式和日志级别选项与 .NET Lambda 函数结合使用,请参阅以下各节中的指南。
将结构化的 JSON 日志格式与 .NET 结合使用
如果您为函数的日志格式选择 JSON,Lambda 将使用 ILambdaLogger
-
"timestamp"
- 生成日志消息的时间 -
"level"
- 分配给消息的日志级别 -
"requestId"
- 函数调用的唯一请求 ID -
"traceId"
:_X_AMZN_TRACE_ID
环境变量 -
"message"
- 日志消息的内容
ILambdaLogger
实例可以添加其他键值对,例如在日志记录异常时。您也可以提供自己的其他参数,如由客户提供的日志参数一节所述。
注意
如果您的代码已经使用其他日志记录库来生成 JSON 格式的日志,则请确保将函数的日志格式设置为纯文本。将日志格式设置为 JSON 将导致您的日志输出采用双重编码。
以下示例日志记录命令显示了如何写入级别为 INFO
的日志消息。
例 .NET 日志记录代码
context.Logger.LogInformation("Fetching cart from database");
您也可以使用通用日志方法,该方法将日志级别作为参数,如以下示例所示。
context.Logger.Log(LogLevel.Information, "Fetching cart from database");
这些示例代码片段输出的日志将在 CloudWatch Logs 中被捕获,如下所示:
例 JSON 日志记录
{
"timestamp": "2023-09-07T01:30:06.977Z",
"level": "Information",
"requestId": "8f711428-7e55-46f9-ae88-2a65d4f85fc5",
"traceId": "1-6408af34-50f56f5b5677a7d763973804",
"message": "Fetching cart from database"
}
注意
如果您将函数的日志格式配置为使用纯文本而不是 JSON,则消息中捕获的日志级别将遵循使用四字符的标签的 Microsoft 约定。例如,日志级别 Debug
在消息中表示为 dbug
。
当您将函数配置为使用 JSON 格式的日志时,日志中捕获的日志级别会使用完整的标签,如示例 JSON 日志记录所示。
如果您没有为日志输出分配级别,Lambda 将自动为其分配 INFO 级别。
在 JSON 中记录异常
将结构化 JSON 日志记录与 ILambdaLogger
结合使用时,可以在代码中记录异常,如以下示例所示。
例 异常日志记录的使用
try { connection.ExecuteQuery(query); } catch(Exception e) { context.Logger.LogWarning(e, "Error executing query"); }
此代码输出的日志格式如以下示例 JSON 中所示。请注意,JSON 中的 message
属性是使用 LogWarning
调用中提供的消息参数填充的,而 errorMessage
属性来自异常本身的 Message
属性。
例 JSON 日志记录
{
"timestamp": "2023-09-07T01:30:06.977Z",
"level": "Warning",
"requestId": "8f711428-7e55-46f9-ae88-2a65d4f85fc5",
"traceId": "1-6408af34-50f56f5b5677a7d763973804",
"message": "Error executing query",
"errorType": "System.Data.SqlClient.SqlException",
"errorMessage": "Connection closed",
"stackTrace": ["<call exception.StackTrace>"]
}
如果您的函数的日志记录格式设置为 JSON,则当您的代码抛出未捕获的异常时,Lambda 还会输出 JSON 格式的日志消息。以下示例代码片段和日志消息显示了如何记录未捕获的异常。
例 异常代码
throw new ApplicationException("Invalid data");
例 JSON 日志记录
{
"timestamp": "2023-09-07T01:30:06.977Z",
"level": "Error",
"requestId": "8f711428-7e55-46f9-ae88-2a65d4f85fc5",
"traceId": "1-6408af34-50f56f5b5677a7d763973804",
"message": "Invalid data",
"errorType": "System.ApplicationException",
"errorMessage": "Invalid data",
"stackTrace": ["<call exception.StackTrace>"]
}
由客户提供的日志参数
使用 JSON 格式的日志消息,您可以提供其他日志参数并将其包含在日志 message
中。以下代码片段示例显示了添加两个标记为 retryAttempt
和 uri
的用户所提供参数的命令。在示例中,这些参数的值来自传递给日志记录命令的 retryAttempt
和 uriDestination
参数。
例 带有其他参数的 JSON 日志记录命令
context.Logger.LogInformation("Starting retry {retryAttempt} to make GET request to {uri}", retryAttempt, uriDestination);
此命令输出的日志消息如以下示例 JSON 中所示。
例 JSON 日志记录
{
"timestamp": "2023-09-07T01:30:06.977Z",
"level": "Information",
"requestId": "8f711428-7e55-46f9-ae88-2a65d4f85fc5",
"traceId": "1-6408af34-50f56f5b5677a7d763973804",
"message": "Starting retry 1 to make GET request to http://example.com/",
"retryAttempt": 1,
"uri": "http://example.com/"
}
提示
指定其他参数时,您也可以使用位置属性来代替名称。例如,也可以如下所示编写上一个示例中的日志记录命令:
context.Logger.LogInformation("Starting retry {0} to make GET request to {1}", retryAttempt, uriDestination);
请注意,当您提供其他日志记录参数时,Lambda 会将这些参数捕获为 JSON 日志记录中的顶级属性。这种方法不同于一些流行的 .NET 日志记录库(例如 Serilog
),后者在单独的子对象中捕获其他参数。
如果您为其他参数提供的参数是一个复杂对象,则默认情况下 Lambda 使用 ToString()
方法来提供值。要指示参数应以 JSON 形式序列化,请使用 @
前缀,如以下代码片段所示。在此示例中,User
是一个具有 FirstName
和 LastName
属性的对象。
例 具有以 JSON 形式序列化的对象的 JSON 日志记录命令
context.Logger.LogInformation("User {@user} logged in", User);
此命令输出的日志消息如以下示例 JSON 中所示。
例 JSON 日志记录
{
"timestamp": "2023-09-07T01:30:06.977Z",
"level": "Information",
"requestId": "8f711428-7e55-46f9-ae88-2a65d4f85fc5",
"traceId": "1-6408af34-50f56f5b5677a7d763973804",
"message": "User {@user} logged in",
"user":
{
"FirstName": "John",
"LastName": "Doe"
}
}
如果其他参数的参数是数组或实施 IList
或 IDictionary
,则 Lambda 会将该参数作为数组添加到 JSON 日志消息中,如以下示例 JSON 日志记录所示。在此示例中,{users}
采用一个包含 User
属性的实例的 IList
参数,其格式与上一个示例相同。Lambda 会将此 IList
转换为数组,每个值都使用 ToString
方法创建。
例 具有 IList
参数的 JSON 日志记录
{
"timestamp": "2023-09-07T01:30:06.977Z",
"level": "Information",
"requestId": "8f711428-7e55-46f9-ae88-2a65d4f85fc5",
"traceId": "1-6408af34-50f56f5b5677a7d763973804",
"message": "{users} have joined the group",
"users":
[
"Rosalez, Alejandro",
"Stiles, John"
]
}
您也可以使用日志记录命令中的 @
前缀以 JSON 形式序列化列表。在以下示例 JSON 日志记录中,users
属性以 JSON 形式序列化。
例 具有以 JSON 形式序列化的 IList
参数的 JSON 日志记录
{
"timestamp": "2023-09-07T01:30:06.977Z",
"level": "Information",
"requestId": "8f711428-7e55-46f9-ae88-2a65d4f85fc5",
"traceId": "1-6408af34-50f56f5b5677a7d763973804",
"message": "{@users} have joined the group",
"users":
[
{
"FirstName": "Alejandro",
"LastName": "Rosalez"
},
{
"FirstName": "John",
"LastName": "Stiles"
}
]
}
将日志级别筛选与 .NET 结合使用
通过配置日志级别筛选,您可以选择仅将特定详细信息级别或更低级别的日志发送到 CloudWatch Logs。要了解如何为您的函数配置日志级别筛选,请参阅 日志级别筛选。
为使 AWS Lambda 按日志级别筛选日志消息,您可以使用 JSON 格式的日志或使用 .NET Console
方法来输出日志消息。要创建 JSON 格式的日志,请将函数的日志类型配置为 JSON 并使用 ILambdaLogger
实例。
借助 JSON 格式的日志,Lambda 使用 将结构化的 JSON 日志格式与 .NET 结合使用 中所述的 JSON 对象中的“级别”键值对筛选您的日志输出。
如果您使用 .NET Console
方法向 CloudWatch Logs 写入消息,则 Lambda 会对您的消息应用日志级别,如下所示:
-
Console.WriteLine 方法:Lambda 应用日志级别
INFO
-
Console.Error 方法:Lambda 应用日志级别
ERROR
将函数配置为使用日志级别筛选时,您必须从以下选项中选择希望 Lambda 发送到 CloudWatch Logs 的日志级别。请注意 Lambda 使用的日志级别与 .NET ILambdaLogger
使用的标准 Microsoft 级别的映射。
Lambda 日志级别 | 等效 Microsoft 级别 | 标准使用情况 |
---|---|---|
TRACE(最详细) | 跟踪 | 用于跟踪代码执行路径的最精细信息 |
调试 | Debug | 系统调试的详细信息 |
INFO | 信息 | 记录函数正常运行情况的消息 |
警告 | 警告 | 有关潜在错误的消息,如果不加以解决,这些错误可能会导致意外行为 |
ERROR | 错误 | 有关会阻碍代码按预期执行的问题的消息 |
FATAL(最简略) | 重大 | 有关导致应用程序停止运行的严重错误的消息 |
Lambda 仅将选定详细信息级别及更低级别的日志发送到 CloudWatch。例如,如果您将日志级别配置为 WARN,Lambda 将发送与 WARN、ERROR 和 FATAL 级别相对应的日志。
其他日志记录工具和库
Powertools for AWS Lambda(.NET)
从 Lambda 上下文中捕获关键字段,冷启动并将日志记录输出结构化为 JSON
根据指示记录 Lambda 调用事件(默认情况下禁用)
通过日志采样仅针对一定百分比的调用输出所有日志(默认情况下禁用)
在任何时间点将其他键附加到结构化日志
使用自定义日志格式设置程序(自带格式设置程序),从而在与组织的日志记录 RFC 兼容的结构中输出日志
将 Powertools for AWS Lambda(.NET)和 AWS SAM 用于结构化日志记录
请按照以下步骤使用 AWS SAM,通过集成的 Powertools for AWS Lambda(.NET)hello world
消息。
先决条件
要完成本节中的步骤,您必须满足以下条件:
-
.NET 8
-
AWS SAM CLI 版本 1.75 或更高版本。如果您使用的是旧版本的 AWS SAM CLI,请参阅升级 AWS SAM CLI。
部署示例 AWS SAM 应用程序
-
使用 Hello World TypeScript 模板初始化该应用程序。
sam init --app-template hello-world-powertools-dotnet --name sam-app --package-type Zip --runtime dotnet6 --no-tracing
-
构建应用程序。
cd sam-app && sam build
-
部署应用程序。
sam deploy --guided
-
按照屏幕上的提示操作。要在交互式体验中接受提供的默认选项,请按
Enter
。注意
对于 HelloWorldFunction 可能没有定义授权,确定执行此操作吗?,确保输入
y
。 -
获取已部署应用程序的 URL:
aws cloudformation describe-stacks --stack-name sam-app --query 'Stacks[0].Outputs[?OutputKey==`HelloWorldApi`].OutputValue' --output text
-
调用 API 端点:
curl -X GET
<URL_FROM_PREVIOUS_STEP>
如果成功,您将会看到如下响应:
{"message":"hello world"}
-
要获取该函数的日志,请运行 sam logs。有关更多信息,请参阅《AWS Serverless Application Model 开发人员指南》中的 使用日志。
sam logs --stack-name sam-app
该日志输出类似于以下示例:
2023/02/20/[$LATEST]4eaf8445ba7a4a93b999cb17fbfbecd8 2023-02-20T14:15:27.988000 INIT_START Runtime Version: dotnet:6.v13 Runtime Version ARN: arn:aws:lambda:ap-southeast-2::runtime:699f346a05dae24c58c45790bc4089f252bf17dae3997e79b17d939a288aa1ec 2023/02/20/[$LATEST]4eaf8445ba7a4a93b999cb17fbfbecd8 2023-02-20T14:15:28.229000 START RequestId: bed25b38-d012-42e7-ba28-f272535fb80e Version: $LATEST 2023/02/20/[$LATEST]4eaf8445ba7a4a93b999cb17fbfbecd8 2023-02-20T14:15:29.259000 2023-02-20T14:15:29.201Z bed25b38-d012-42e7-ba28-f272535fb80e info {"_aws":{"Timestamp":1676902528962,"CloudWatchMetrics":[{"Namespace":"sam-app-logging","Metrics":[{"Name":"ColdStart","Unit":"Count"}],"Dimensions":[["FunctionName"],["Service"]]}]},"FunctionName":"sam-app-HelloWorldFunction-haKIoVeose2p","Service":"PowertoolsHelloWorld","ColdStart":1} 2023/02/20/[$LATEST]4eaf8445ba7a4a93b999cb17fbfbecd8 2023-02-20T14:15:30.479000 2023-02-20T14:15:30.479Z bed25b38-d012-42e7-ba28-f272535fb80e info {"ColdStart":true,"XrayTraceId":"1-63f3807f-5dbcb9910c96f50742707542","CorrelationId":"d3d4de7f-4ccc-411a-a549-4d67b2fdc015","FunctionName":"sam-app-HelloWorldFunction-haKIoVeose2p","FunctionVersion":"$LATEST","FunctionMemorySize":256,"FunctionArn":"arn:aws:lambda:ap-southeast-2:123456789012:function:sam-app-HelloWorldFunction-haKIoVeose2p","FunctionRequestId":"bed25b38-d012-42e7-ba28-f272535fb80e","Timestamp":"2023-02-20T14:15:30.4602970Z","Level":"Information","Service":"PowertoolsHelloWorld","Name":"AWS.Lambda.Powertools.Logging.Logger","Message":"Hello world API - HTTP 200"} 2023/02/20/[$LATEST]4eaf8445ba7a4a93b999cb17fbfbecd8 2023-02-20T14:15:30.599000 2023-02-20T14:15:30.599Z bed25b38-d012-42e7-ba28-f272535fb80e info {"_aws":{"Timestamp":1676902528922,"CloudWatchMetrics":[{"Namespace":"sam-app-logging","Metrics":[{"Name":"ApiRequestCount","Unit":"Count"}],"Dimensions":[["Service"]]}]},"Service":"PowertoolsHelloWorld","ApiRequestCount":1} 2023/02/20/[$LATEST]4eaf8445ba7a4a93b999cb17fbfbecd8 2023-02-20T14:15:30.680000 END RequestId: bed25b38-d012-42e7-ba28-f272535fb80e 2023/02/20/[$LATEST]4eaf8445ba7a4a93b999cb17fbfbecd8 2023-02-20T14:15:30.680000 REPORT RequestId: bed25b38-d012-42e7-ba28-f272535fb80e Duration: 2450.99 ms Billed Duration: 2451 ms Memory Size: 256 MB Max Memory Used: 74 MB Init Duration: 240.05 ms XRAY TraceId: 1-63f3807f-5dbcb9910c96f50742707542 SegmentId: 16b362cd5f52cba0
-
这是一个可以通过互联网访问的公有 API 端点。我们建议您在测试后删除该端点。
sam delete
管理日志保留日期
删除函数时,日志组不会自动删除。要避免无限期存储日志,请删除日志组,或配置一个保留期,在该保留期结束后,日志将自动删除。要设置日志保留日期,请将以下内容添加到您的 AWS SAM 模板中:
Resources: HelloWorldFunction: Type: AWS::Serverless::Function Properties: # Omitting other properties LogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/aws/lambda/${HelloWorldFunction}" RetentionInDays: 7
在 Lambda 控制台中查看日志
调用 Lambda 函数后,您可以使用 Lambda 控制台查看日志输出。
如果可以在嵌入式代码编辑器中测试代码,则可以在执行结果中找到日志。使用控制台测试功能调用函数时,可以在详细信息部分找到日志输出。
在 CloudWatch 控制台中查看日志
您可以使用 Amazon CloudWatch 控制台查看所有 Lambda 函数调用的日志。
使用 CloudWatch 控制台查看日志
-
打开 CloudWatch 控制台的 Log groups
(日志组页面)。 -
选择您的函数 (/aws/lambda/
your-function-name
) 的日志组。 -
创建日志流。
每个日志流对应一个函数实例。日志流会在您更新 Lambda 函数以及创建更多实例来处理多个并发调用时显示。要查找特定调用的日志,建议您使用 AWS X-Ray 检测函数。X-Ray 会在追踪中记录有关请求和日志流的详细信息。
使用 AWS Command Line Interface(AWS CLI)查看日志
AWS CLI 是一种开源工具,让您能够在命令行 Shell 中使用命令与 AWS 服务进行交互。要完成本节中的步骤,您必须拥有 AWS CLI 版本 2。
您可以通过 AWS CLI,使用 --log-type
命令选项检索调用的日志。响应包含一个 LogResult
字段,其中包含多达 4KB 来自调用的 base64 编码日志。
例 检索日志 ID
以下示例说明如何从 LogResult
字段中检索名为 my-function
的函数的日志 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 --cli-binary-format raw-in-base64-out | base64 --decode
如果使用 cli-binary-format 版本 2,则 AWS CLI 选项是必需的。要将其设为默认设置,请运行 aws configure set cli-binary-format raw-in-base64-out
。有关更多信息,请参阅版本 2 的 AWS Command Line Interface 用户指南中的 AWS CLI 支持的全局命令行选项。
您应看到以下输出:
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 Windowsbase64 -D
。
例 get-logs.sh 脚本
在同一命令提示符下,使用以下脚本下载最后五个日志事件。此脚本使用 sed
从输出文件中删除引号,并休眠 15 秒以等待日志可用。输出包括来自 Lambda 的响应,以及来自 get-log-events
命令的输出。
复制以下代码示例的内容并将其作为 get-logs.sh
保存在 Lambda 项目目录中。
如果使用 cli-binary-format 版本 2,则 AWS CLI 选项是必需的。要将其设为默认设置,请运行 aws configure set cli-binary-format raw-in-base64-out
。有关更多信息,请参阅版本 2 的 AWS Command Line Interface 用户指南中的 AWS CLI 支持的全局命令行选项。
#!/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-namestream1
--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"
}
删除日志
删除函数时,日志组不会自动删除。要避免无限期存储日志,请删除日志组,或配置一个保留期,在该保留期之后,日志将自动删除。