I have a web api (.NET Core 3.1) which is using Serilog for logging. Serilog is added to the IWebHostBuilder
quite early:
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);
});
}
This means (afaik) that I need to have already configured the logger at this point. So this is the very first thing I do in the main:
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.CreateLogger();
var host = CreateWebHostBuilder(args).Build();
host.Run();
}
But the line .ReadFrom.Configuration(Configuration)
requires the configuration to be set up. This is usually done in the StartUp
(again, afaik) which has not yet been run at this time. Obviously I could move my LoggerConfiguration
to later, but the .UseSerilog
would be called before it was configured.
So how do I configure Serilog with IConfugration
, when I haven't set it up yet?
CodePudding user response:
@RubenBartelink pointed to a very good ressource in comment.
This is also described in the Serilog for ASP.NET Core documentation.
In particular the two-stage initialization part, which states:
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 withCreateBootstrapLogger()
: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
configurationUsing 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.