【问题标题】:How do I get a serilog enricher to work with dependency injection while keeping it on startup?如何让 serilog 丰富器与依赖注入一起工作,同时保持启动?
【发布时间】:2019-12-18 23:56:01
【问题描述】:

这里有一个答案:How do I pass a dependency to a Serilog Enricher? 这说明你可以传入一个实例。

但要做到这一点,我需要在我的依赖注入代码运行后移动我的记录器设置(在startup.cs 中)

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

有没有办法以某种方式配置 serilog 以在我的 Main() 方法中运行,但也可以使用 DI 项来丰富数据? DI 项虽然是单例,但有更多的依赖关系(主要是数据库连接)。

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

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

我的 Main 以 :

开头
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”类,其中包含用户名、公司 ID 等,这些属性是在访问时访问数据库以获取基于某些当前身份的值的属性(尚未实现)。它被我的 DI 注册为单例。

【问题讨论】:

  • 查看Inline Initialization - 我认为在这种情况下可以从hostingContext 解决依赖关系。然而,这意味着采取不利的方式让日志记录依赖于 DI 设置(我认为您目前正走在幸福的道路上,在其他任何事情之前设置日志记录 - 也许您应该考虑直接从配置中获取值?)
  • 更新: CreateBootstrapLogger()(在this post 中描述)现在完全支持这种情况

标签: dependency-injection asp.net-core-mvc serilog


【解决方案1】:

我建议使用插入到 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 的属性添加到当前执行的日志上下文中。

只要您在Startup.ConfigureServices 中注册了IUserDataService,ASP .NET Core 就会负责解析它。

当然,要让它发挥作用,您必须:

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

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

【讨论】:

  • 我喜欢这种使用中间件的方法。至少在 Serilog 的 .Enrich.With&lt;TEnricher&gt; 可以处理 DI 之前(如果有的话)。通过提供access to IServiceCollection in UseSerilog,Serilog 似乎走错了方向(IMO​​),迫使您手动调用GetService
  • @MathiasR 是的...日志管道中的 DI 可能是一种先有鸡还是先有蛋的问题,因为在设置 DI 容器之前设置日志记录是很常见的(并且能够由容器设置引起的日志问题,在插件场景中特别有用)但同时更复杂的日志管道也有依赖关系......也许有两个记录器是一种方法......一个(简单)用于引导应用程序,另一个(复杂的)在应用程序启动成功后(成功时将日志从简单转发到复杂)
  • LogContext 是静态对象吗?当一个请求值可以覆盖另一个请求值时,在多线程环境中使用会不会有风险?
  • @MichaelFreidgeim LogContext 是一个静态类,但 it is thread-safe via AsyncLocal/ThreadStatic。即每个线程都有自己的 LogContext,尽管它是通过静态类访问的。请求不共享相同的 LogContext。
【解决方案2】:

对已接受答案的轻微改进是使用 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);
    }
}

【讨论】:

  • 这绝对是主观的,但我不认为这是一种改进,而是一种有利有弊的替代方法......I personally prefer to use Serilog directly within my applications而不是微软的ILogger&lt;T&gt;,所以LogContext.PushProperty是故意。我认为没有理由在您自己的应用程序中与记录器无关。恕我直言,它只对您期望其他人使用的库有意义(这样您就不会对它们强制使用 Serilog)
  • 为了使ILogger&lt;T&gt; 具有通用性,它不可避免地只需要公开许多日志框架中通用的功能,因此,每当您需要利用您使用的框架中的新颖功能时,您将被抽象阻塞。 BeginScope 是一个示例,其中与记录器无关的价格意味着 you have no control over destructuring of objects
  • 使用 AsyncLocal LogContext 可确保将值传递给同一线程中的不同记录器。在您的代码中,不清楚如何创建记录器,并且不同的类可以有不同的 DI 记录器
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-14
  • 2022-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多