Search code examples
asp.net-coreserilog

Serilog - MinimumLevel Override required to make the default effective


I read here What does MinimumLevel and Override mean in appsettings.json logging context? that Default means default in Serilog - therefore, the following configuration should work:

appSettings.json

"Serilog": {
    "MinimumLevel": {
        "Default": "Warning"
    }
}

However, with the config above I still get Information level logs like this:

[11:53:56 INF] Now listening on: http://0.0.0.0:5000
[11:53:56 INF] Application started. Press Ctrl+C to shut down.
[11:53:56 INF] Hosting environment: Development
[11:53:56 INF] Content root path: [some_path]

Only effective configuration for me is:

"Serilog": {
    "MinimumLevel": {
        "Default": "Warning",
        "Override": {
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Warning"
        }
    }
}

With the above, no Information -level logs are displayed. It seems contradictory to the mentioned stackoverflow answer and common sense.

This is how I do setup the logger:

Log.Logger = new LoggerConfiguration()
                 .ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)
                 .ReadFrom.Configuration(Configuration)
                 .WriteTo.Console(theme: AnsiConsoleTheme.Code)
                 .CreateLogger();

The project is developed with .NET Core 3.1.

I would like to know whether there is something peculiar with serilog or I am missing some important step in configuration?

For the sake of completness - here is Main:

public static void Main(string[] args)
{
    try
    {
        CreateHostBuilder(args).Build().Run();
    }
    catch (Exception exception)
    {
        Log.Fatal(exception, "Host terminated unexpectedly");
    }
    finally
    {
        Log.CloseAndFlush();
    }
}

and CreateHostBuilder:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseSerilog()
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

The Serilog is initialized with the code mentioned above (Log.Logger = ...) in the very first line of ConfigureServices in Startup.cs.


Solution

  • After a few hours of searches and tries I have noticed that there is no single source that would recommend to initialize Serilog inside any of Startup.cs methods.

    The solution for me is to move the initialization to the Program.cs in the following way:

    public static void Main(string[] args)
    {
        SetupSerilog();
        try
        {
            CreateHostBuilder(args).Build().Run();
        }
        catch (Exception exception)
        {
            Log.Fatal(exception, "Host terminated unexpectedly");
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }
    
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseSerilog()
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
    
    private static void SetupSerilog()
    {
        var appsettingsFilename = "appsettings.json";
    
        var configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile(appsettingsFilename)
            .AddEnvironmentVariables()
            .Build();
    
        Log.Logger = new LoggerConfiguration()
            .ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)
            .WriteTo.Console(theme: AnsiConsoleTheme.Code)
            .ReadFrom.Configuration(configuration)
            .CreateLogger();
    }
    

    Unfortunately, I barely understand why is so and what is the reason that Serilog behaves differently, depending on whether the logger is defined inside the constructor, ConfigureServices or Configure of Startup.cs. My very initial idea was to have an easy access to the IConfiguration. Nevertheless, maybe someone will find this cue above usefull.