DEV Community

ohalay
ohalay

Posted on

.Net AWS Lambda logging

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.");
}
Enter fullscreen mode Exit fullscreen mode

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.");
Enter fullscreen mode Exit fullscreen mode

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."
    }
}
Enter fullscreen mode Exit fullscreen mode

Scope and structure logging

Two importan feature of ILogger

  1. Scope - we can define scope fields(for example TraceId), that will be in all log records while the scope is visible.
  2. 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<string, object>
{
  ["TraceId"] = "my-trace-id"
});

logger.LogInformation("This is my log where Id={Id}", 1);
Enter fullscreen mode Exit fullscreen mode

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"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

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. For that, we may add a xUnit log provider .AddXUnit(outputHelper) using community library.

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

Build&Test GitHub repo size GitHub contributors GitHub stars GitHub forks

Serverless integration tests

A public feed with available products that updates every day

Business problem

  • Integration tests for serverless solution

Requirements

  • Docker

Implementation

  1. S3 public buckets with available documents
  2. Lambda updates document

Help links

Top comments (0)