Motivation
Data logging is a process of collecting and storing data over some period of time in order to monitor, fast identify and fix application problems.
Lambda logger
Usually we create .dot net lambda project from template, that installed together with AWS Toolkit for Visual Studio. Our lambda function contains trigger and context:
public void FunctionHandler(S3Event @event, ILambdaContext context)
{
context.Logger.LogInformation("This is my log.");
}
Using context we can log record from lambda to CloudWatch. Looks pretty easy and simple. CloudWatch will store data and have feature to search in logs. But Logger
implement ILambdaLogger
interface that is not our lovely ILogger
, that we use all the time in .net 😐.
ILogger
Behind the hood, lambda writes logs to the console, and after that, it appears in CloudWatch. That means we can build a logger with the console provider and continue to use CloudWatch with ILogger
var logger = LoggerFactory.Create(builder => builder.AddConsole())
.CreateLogger<Function>();
logger.LogInformation("This is my log.");
So far so good, but all records in CloudWatch - are just plain text. And it is hard to search through plain text and identify problems. Since 2015 CloudWatch support JSON-formatted logs. So we need update configuration to use JSON format AddJsonConsole()
and can query/filter logs by filed from record.
{
"EventId": 0,
"LogLevel": "Information",
"Category": "AWSLambda1.Function",
"Message": "This is my log.",
"State": {
"Message": "This is my log.",
"{OriginalFormat}": "This is my log."
}
}
Scope and structure logging
Two importan feature of ILogger
- Scope - we can define scope fields(for example TraceId), that will be in all log records while the scope is visible.
- Structure logging - we can log a record with a defined field and it will appear as a JSON field. For example Id. ```
using var _ = logger.BeginScope(new Dictionary
{
["TraceId"] = "my-trace-id"
});
logger.LogInformation("This is my log where Id={Id}", 1);
Log record look like that:
{
...
"Message": "This is my log where Id=1",
"State": {
"Message": "This is my log where Id=1",
"Id": 1,
"{OriginalFormat}": "This is my log where Id={Id}"
},
"Scopes": [
{
...
"TraceId": "my-trace-id"
}
]
}
## Test with xUnit
xUnit is the most popular .net unit tests framework. It provides a mechanism to write output from tests using `ITestOutputHelper`. But what if we want to write output everything from `ILogger`? For example when we have [integration tests](https://dev.to/ohalay/integration-tests-for-aws-serverless-solution-12aj %). For that, we may add a **xUnit** log provider `.AddXUnit(outputHelper)` using [community library](https://github.com/martincostello/xunit-logging).
## Conclusion
1. We use structured JSON logging with `ILogger` abstraction.
2. We use output logs for integration tests.
And finally, the source code in the GitHub repository
Serverless integration tests
A public feed with available products that updates every day
Business problem
- Integration tests for serverless solution
Requirements
- Docker
Implementation
- S3 public buckets with available documents
- Lambda updates document
Help links
Top comments (0)