如何让 serilog enricher 在启动时使用依赖注入?

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

这里有一个答案: 这说明你可以传入一个实例。

但是要做到这一点,我需要在我的依赖注入代码具有 运行(在 startup.cs

之后移动我的记录器设置

这意味着不会记录启动错误,因为记录器还没有准备好。

有没有办法在我的 Main() 方法中以某种方式将 serilog 配置为 运行,同时还使用 DI 项丰富数据? DI 项虽然是单例,但有进一步的依赖(主要是数据库连接)。

我用谷歌搜索了这个并阅读了一些关于向上下文添加内容的内容,但我一直无法找到一个我可以适应的完整工作示例。

我发现的大多数示例都涉及将代码放入控制器以附加信息,但我希望它对每个日志条目都全局可用。

我的主要以 :

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

进入 .NET Core MVC 代码之前

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

我的 DI 对象基本上是一个 "UserData" class,其中包含用户名、公司 ID 等,这些是在访问时访问数据库以获取基于某些当前身份的值的属性(没有'尚未实施)。它被我的 DI 注册为单例。

我建议使用插入到 ASP .NET Core 管道中的简单中间件,使用所需的依赖项来丰富 Serilog 的 LogContext,让ASP .NET Core 依赖注入为您解决依赖...

例如假设 IUserDataService 是一个服务,你可以用它来获取你需要的数据,丰富日志,中间件看起来像这样:

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是在做充实,在当前执行的日志上下文中添加了一个叫做UserData的属性。

ASP .NET Core 负责解析 IUserDataService 只要您在 Startup.ConfigureServices.

中注册它

当然,要实现这一点,您必须:

1。通过调用 Enrich.FromLogContext() 告诉 Serilog 从日志上下文丰富日志。例如

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。在 Startup.Configure 中将中间件添加到管道中。例如

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

    app.UseMiddleware<UserDataLoggingMiddleware>();

    // ...

    app.UseMvc();
}

对已接受的答案稍作改进是使用 ILogger.BeginScope 而不是静态 Serilog LogContext.PushProperty。它与字典一起变得有点丑陋,但仍然是一个改进,并且与记录器无关。

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);
    }
}

UseSerilog 有几个覆盖,其中有一个带有 lambda 的覆盖,它接受 3 个参数,包括 IServiceProvider.

它可用于动态注入一个对象,这将在 ctor 中使用 IServiceProvider 并使用 GetService 来查找每个 Log() 操作所需的上下文。