Search code examples
c#.netasp.net-core.net-6.0serilog

Configure Serilog in .NET 6 console application


I wrote an extension method over IHostBuilder named ConfigureLog to configure Serilog. Serilog must be log on Kibana and this config is in appsettings.json file

public static IHostBuilder ConfigureLog(this IHostBuilder builder)
{
    builder.UseSerilog((hostContext, loggerConfiguration) =>
        {
            var options = hostContext.Configuration.GetSection("LoggingConfiguration")
                .Get<LoggingConfigurationOption>();
            loggerConfiguration = options.GetLoggerConfiguration();
            loggerConfiguration.CreateLogger();
        });

    builder.ConfigureServices((hostContext, services) =>
        {
            services.AddLogging(b =>
            {
                b.AddSerilog();
            });
        });
        
    return builder;
}

My LoggingConfigurationOption class is:

internal class LoggingConfigurationOption
{
    public string ElkUrl { get; set; }
    public string ElkUsername { get; set; }
    public string ElkPassword { get; set; }
    public string ServiceName { get; set; }
    public LogEventLevel? ConsoleLogLevel { get; set; }

    internal LoggerConfiguration GetLoggerConfiguration()
    {
        return new LoggerConfiguration()
            .MinimumLevel.Verbose()
            .MinimumLevel.Override("System", LogEventLevel.Fatal)
            .MinimumLevel.Override("Microsoft", LogEventLevel.Fatal)
            .Enrich.FromLogContext()
            .WriteTo.Console(ConsoleLogLevel??LogEventLevel.Warning)
            .WriteTo.Elasticsearch(
                new ElasticsearchSinkOptions(new Uri(ElkUrl)) {
                    ModifyConnectionSettings = c=>c.BasicAuthentication(ElkUsername,ElkPassword),
                    AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv8,
                    DetectElasticsearchVersion = true,
                    BatchPostingLimit = 1,
                    CustomFormatter = new ExceptionAsObjectJsonFormatter(renderMessage: true),
                    AutoRegisterTemplate = true,
                    MinimumLogEventLevel = LogEventLevel.Information,
                    IndexDecider = IndexDeciderFunc,
                    FailureCallback = e => System.Console.WriteLine("Unable to submit event " + e.MessageTemplate),
                });
    }
    
    private string IndexDeciderFunc(LogEvent logEvent, DateTimeOffset offset)
    {
        var now = DateTimeOffset.UtcNow;
        var year = now.Year;
        var month = now.Month;
        var day = now.Day;

        if (logEvent.MessageTemplate.Text.Equals(HttpRequestTemplate) ||
            logEvent.MessageTemplate.Text.Equals(HttpResponseTemplate))
            return $"http-logs-{year}-{month}-{day}";
        return $"{ServiceName}-logs-{year}-{month}-{day}";
    }
}

This class LoggingConfigurationOption get ELK configuration correctly from appsettings.json file but when I using logger on code, it doesn't work


Solution

  • You seem to have a fundamental misunderstanding of how UseSerilog works and is supposed to be used. loggerConfiguration isn't passed by reference. This:

    loggerConfiguration = options.GetLoggerConfiguration();
    

    only modifies the local variable, so this does effectively nothing. You also don't need to call CreateLogger() here.

    Instead, you're meant to modify the logger configuration you're given, not create a new one. Serilog will then create a logger from that configuration.

    So, your GetLoggerConfiguration() should be rewritten to be something like:

    internal void ConfigureLogger(LoggerConfiguration configuration)
    { 
        // modify the configuation
        configuration
            .MinimumLevel.Verbose()
            .MinimumLevel.Override("System", LogEventLevel.Fatal)
            .MinimumLevel.Override("Microsoft", LogEventLevel.Fatal)
            .Enrich.FromLogContext()
            .WriteTo.Console(ConsoleLogLevel??LogEventLevel.Warning)
            .WriteTo.Elasticsearch(/* omitted for conciseness */);
    }
    

    And then, pass along your provided configuration object:

    builder.UseSerilog((hostContext, loggerConfiguration) =>
        {
            var options = hostContext.Configuration.GetSection("LoggingConfiguration")
                .Get<LoggingConfigurationOption>();
            options.ConfigureLogger(loggerConfiguration);
        });