How do I structure DI for Serilog in AWS Lambda when I need the ILambdaContext

Viewed 2036

I have implemented a ServicesCollection DI pattern into my AWS Lambda Function. I want to add Serilog to the ServicesCollection, but I need the ILambdaContext in order to configure the Serilog logger the way that I want.

I currently configure the logger using the stack name. I would like to get the logger from the ServicesCollection.

I currently have a helper method as below.

    protected virtual async Task<ILogger> GetLogger([NotNull] ILambdaContext context)
    {
        if (context == null) throw new ArgumentNullException(nameof(context));
        if (_logger == null)
        {
            var region = context.InvokedFunctionArn.Split(':')[3];
            AWSLoggerConfig configuration = new AWSLoggerConfig(await this.GetStackName(context))
            {
                Region = region
            };

            _logger = new LoggerConfiguration()
                .WriteTo.AWSSeriLog(configuration)
                .CreateLogger();
        }

        return _logger;
    }

As you can see it requires the ILambdaContext. I would like to add the ILambdaContext to the ServicesCollection before calling ServiceProvider.GetService().

How do I create a factory class (to add to the services collection) for the Serilog Logger?

1 Answers

So, I ended up creating a subclass of LoggerConfiguration as below

public class YadaYadaLoggerConfiguration : LoggerConfiguration
{
    private string _stackName;
    public ILambdaContext Context { get; }
    public IAmazonLambda LambdaClient { get; }

    public YadaYadaLoggerConfiguration(ILambdaContext context, IAmazonLambda lambdaClient)
    {
        Context = context;
        LambdaClient = lambdaClient;
        var region = context.InvokedFunctionArn.Split(':')[3];
        AWSLoggerConfig configuration = new AWSLoggerConfig(this.GetStackName(context).Result)
        {
            Region = region
        };

        this.WriteTo.AWSSeriLog(configuration);
    }

    [Pure]
    protected async Task<string> GetStackName([NotNull] ILambdaContext context)
    {
        if (context == null) throw new ArgumentNullException(nameof(context));
        if (_stackName == null)
        {
            var response = await this.LambdaClient.ListTagsAsync(new ListTagsRequest { Resource = context.InvokedFunctionArn });
            _stackName = response.Tags["aws:cloudformation:stack-name"];
        }

        return _stackName;
    }

}

And then, in my Lambda function I added it to the ServicesCollection with the factory Func<>:

protected virtual void ConfigureServices(IServiceCollection serviceCollection)
    {
        serviceCollection.AddSingleton<IAmazonS3,AmazonS3Client>();
        serviceCollection.AddSingleton<IAmazonSQS, AmazonSQSClient>();
        serviceCollection.AddSingleton<HttpClient>();
        serviceCollection.AddSingleton<ILogger>(z => 
            new YadaYadaLoggerConfiguration(z.GetService<ILambdaContext>(), 
                z.GetService<IAmazonLambda>()).CreateLogger());


    }
Related