Repository: davidfowl/TodoApi
File: TodoApi/Extensions/SerilogExtensions.cs (C#)

Code before:
    public static WebApplicationBuilder AddSerilog(
        this WebApplicationBuilder builder,
        string sectionName = "Serilog",
        Action<LoggerConfiguration>? extraConfigure = null)
    {
        builder.Services.AddOptions<SerilogOptions>()
            .BindConfiguration(sectionName)
            .ValidateDataAnnotations();


        builder.Host.UseSerilog((context, serviceProvider, loggerConfiguration) =>
        {
            var loggerOptions = serviceProvider.GetRequiredService<IOptions<SerilogOptions>>().Value;

            // https://github.com/serilog/serilog-settings-configuration
            loggerConfiguration.ReadFrom.Configuration(context.Configuration, sectionName: sectionName);

            extraConfigure?.Invoke(loggerConfiguration);

            var levelString = context.Configuration.GetValue<string>($"{sectionName}:MinimumLevel:Default") ??
                        nameof(LogEventLevel.Information);

            Enum.TryParse<LogEventLevel>(levelString, true, out var logLevel);

            loggerConfiguration
                .Enrich.WithProperty("Application", "TodoApi")
                .Enrich.FromLogContext()
                .Enrich.WithEnvironmentName()
                .Enrich.WithMachineName()
                // https://rehansaeed.com/logging-with-serilog-exceptions/
                .Enrich.WithExceptionDetails();

            if (context.HostingEnvironment.IsDevelopment())
            {
                // https://github.com/serilog/serilog-sinks-async
                loggerConfiguration.WriteTo.Async(writeTo => writeTo.Console(
                    logLevel,
                    loggerOptions.LogTemplate
                ));
            }

            if (!string.IsNullOrEmpty(loggerOptions.ElasticSearchUrl))
            {
                // https://github.com/serilog-contrib/serilog-sinks-elasticsearch
                loggerConfiguration.WriteTo.Async(
                    writeTo =>
                        writeTo.Elasticsearch(ConfigureElasticSink(
                            loggerOptions.ElasticSearchUrl,
                            context.HostingEnvironment.EnvironmentName)));
            }


Review comment (suggestion):
```suggestion
                .Enrich.WithProperty("Application", builder.Environment.ApplicationName)
```

Code after:
        builder.Configuration.GetSection(sectionName).Bind(serilogOptions);

        builder.Host.UseSerilog((context, loggerConfiguration) =>
        {
            // https://github.com/serilog/serilog-settings-configuration
            loggerConfiguration.ReadFrom.Configuration(context.Configuration, sectionName: sectionName);

            loggerConfiguration
                .Enrich.WithProperty("Application", builder.Environment.ApplicationName)
                .Enrich.FromLogContext()
                // https://rehansaeed.com/logging-with-serilog-exceptions/
                .Enrich.WithExceptionDetails();

            if (serilogOptions.UseConsole)
            {
                // https://github.com/serilog/serilog-sinks-async
                loggerConfiguration.WriteTo.Async(writeTo =>
                    writeTo.Console(outputTemplate: serilogOptions.LogTemplate));
            }

            if (!string.IsNullOrEmpty(serilogOptions.ElasticSearchUrl))
            {
                // https://github.com/serilog-contrib/serilog-sinks-elasticsearch
                loggerConfiguration.WriteTo.Elasticsearch(new(new Uri(serilogOptions.ElasticSearchUrl))
                {
                    AutoRegisterTemplate = true,
                    IndexFormat = builder.Environment.ApplicationName
                });
            }

            if (!string.IsNullOrEmpty(serilogOptions.SeqUrl))
            {
                loggerConfiguration.WriteTo.Seq(serilogOptions.SeqUrl);
            }
        });

        return builder;
    }

    private sealed class SerilogOptions
    {
        public bool UseConsole { get; set; } = true;
        public string? SeqUrl { get; set; }
        public string? ElasticSearchUrl { get; set; }

        public string LogTemplate { get; set; } =
            "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Level} - {Message:lj}{NewLine}{Exception}";
    }
}