How do I get a serilog enricher to work with dependency injection while keeping it on startup?

孤街醉人 提交于 2021-02-07 06:09:21

问题


There is an answer here: How do I pass a dependency to a Serilog Enricher? which explains you can pass an instance in.

However to do that I would need to move my logger setup after my dependency injection code has ran (in the startup.cs)

This means that startup errors won't be logged because the logger won't be ready yet.

Is there a way to somehow configure serilog to run in my Main() method, but also enrich data with a DI item? The DI item has further dependencies (mainly on database connection) although it is a singleton.

I've googled this and read something about adding things to a context, but I've been unable to find a complete working example that I can adapt.

Most of the examples I've found involve putting code into the controller to attach information, but I want this to be globally available for every single log entry.

My Main starts with :

Log.Logger = new LoggerConfiguration()
    .Enrich.FromLogContext()
    .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri(elasticUri))
    {
        AutoRegisterTemplate = true,
    })
    .CreateLogger();

Before going into the .NET Core MVC code

CreateWebHostBuilder(args).Build().Run();

My DI object is basically a "UserData" class that contains username, companyid, etc. which are properties that hit the database when accessed to get the values based on some current identity (hasn't been implemented yet). It's registered as a singleton by my DI.


回答1:


I would suggest using a simple middleware that you insert in the ASP .NET Core pipeline, to enrich Serilog's LogContext with the data you want, using the dependencies that you need, letting the ASP .NET Core dependency injection resolve the dependencies for you...

e.g. Assuming IUserDataService is a service that you can use to get the data you need, to enrich the log, the middleware would look something like this:

public class UserDataLoggingMiddleware
{
    private readonly RequestDelegate _next;

    public UserDataLoggingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context, IUserDataService userDataService)
    {
        var userData = await userDataService.GetAsync();

        // Add user data to logging context
        using (LogContext.PushProperty("UserData", userData))
        {
            await _next.Invoke(context);
        }
    }
}

LogContext.PushProperty above is doing the enrichment, adding a property called UserData to the log context of the current execution.

ASP .NET Core takes care of resolving IUserDataService as long as you registered it in your Startup.ConfigureServices.

Of course, for this to work, you'll have to:

1. Tell Serilog to enrich the log from the Log context, by calling Enrich.FromLogContext(). e.g.

Log.Logger = new LoggerConfiguration()
    .ReadFrom.Configuration(Configuration)
    .Enrich.FromLogContext() // <<======================
    .WriteTo.Console(
        outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} " +
                        "{Properties:j}{NewLine}{Exception}")
    .CreateLogger();

2. Add your middleware to the pipeline, in your Startup.Configure. e.g.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ...

    app.UseMiddleware<UserDataLoggingMiddleware>();

    // ...

    app.UseMvc();
}



回答2:


A slight improvement to the accepted answer would be to use ILogger.BeginScope instead of the static Serilog LogContext.PushProperty. It becomes a bit uglier with the dictionary, but still an improvement, as well as logger-agnostic.

public async Task Invoke(HttpContext context, IUserDataService userDataService, ILogger<UserDataLoggingMiddleware> logger)
{
    var userData = await userDataService.GetAsync();

    // Add user data to logging context
    using (logger.BeginScope(new Dictionary<string, object> { ["UserData"] = userData }))
    {
        await _next.Invoke(context);
    }
}


来源:https://stackoverflow.com/questions/57460579/how-do-i-get-a-serilog-enricher-to-work-with-dependency-injection-while-keeping

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!