在配置准备好之前配置 Serilog?

Configuring Serilog before configuration is ready?

我有一个 Web api (.NET Core 3.1),它使用 Serilog 进行日志记录。 Serilog 很早就添加到 IWebHostBuilder:

public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
    return WebHost
        .CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseSerilog((context, configuration) =>
        {
            if (context.HostingEnvironment.IsDevelopment())
            {
                configuration.WriteTo.Console(LogEventLevel.Debug);
                return;
            }

            configuration.WriteTo.ApplicationInsights(TelemetryConverter.Traces, LogEventLevel.Error);
        });
}

这意味着(afaik)此时我需要已经配置了记录器。所以这是我主要做的第一件事:

public static int Main(string[] args)
{
    Log.Logger = new LoggerConfiguration()
        .ReadFrom.Configuration(Configuration)
        .CreateLogger();

    var host = CreateWebHostBuilder(args).Build();
    host.Run();
}

但是.ReadFrom.Configuration(Configuration)行需要设置配置。这通常在 StartUp(再次,afaik)中完成,此时尚未 运行。显然我可以将我的 LoggerConfiguration 移到以后,但是 .UseSerilog 会在配置之前被调用。

那么,当我还没有设置 Serilog 时,如何使用 IConfugration 配置它?

@RubenBartelink 在评论中指出了一个非常好的资源。

这在Serilog for ASP.NET Core documentation中也有描述。

特别是 two-stage initialization part,其中指出:

Two-stage initialization

The example at the top of this page shows how to configure Serilog immediately when the application starts.

This has the benefit of catching and reporting exceptions thrown during set-up of the ASP.NET Core host.

The downside of initializing Serilog first is that services from the ASP.NET Core host, including the appsettings.json configuration and dependency injection, aren't available yet.

To address this, Serilog supports two-stage initialization. An initial "bootstrap" logger is configured immediately when the program starts, and this is replaced by the fully-configured logger once the host has loaded. To use this technique, first replace the initial CreateLogger() call with CreateBootstrapLogger():

using Serilog;
using Serilog.Events;

public class Program
{
    public static int Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
            .Enrich.FromLogContext()
            .WriteTo.Console()
            .CreateBootstrapLogger(); // <-- Change this line!

Then, pass a callback to UseSerilog() that creates the final logger:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseSerilog((context, services, configuration) => configuration
            .ReadFrom.Configuration(context.Configuration)
            .ReadFrom.Services(services)
            .Enrich.FromLogContext()
            .WriteTo.Console())
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

It's important to note that the final logger completely replaces the bootstrap logger: if you want both to log to the console, for instance, you'll need to specify WriteTo.Console() in both places, as the example shows.

Consuming appsettings.json configuration

Using two-stage initialization, insert the ReadFrom.Configuration(context.Configuration) call shown in the example above. The JSON configuration syntax is documented in the Serilog.Settings.Configuration README.