是否可以使用 if 语句优雅地配置 Serilog?

Is it possible to elegantly configure Serilog with if-statements?

我的 Serilog 配置代码如下所示:

Log.Logger = new LoggerConfiguration()
    .Enrich.WithExceptionDetails()
    .Enrich.FromLogContext()
    .MinimumLevel.Warning()
//  .MinimumLevel.Override("Microsoft", LogEventLevel.Verbose)
//  .MinimumLevel.Override("System", LogEventLevel.Verbose)
//  .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Verbose)
    .WriteTo.Console( outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate )
//  .WriteTo.File()
    .CreateLogger();

我想在运行时更改此配置,不幸的是,因为 Serilog 使用 API 的 "fluent" 样式,所以它有点混乱。例如,如果我想在运行时启用或禁用控制台和文件日志记录:

Boolean enableConsoleLogging = ...
Boolean enableFileLogging = ...

LoggerConfiguration builder = new LoggerConfiguration()
    .Enrich.WithExceptionDetails()
    .Enrich.FromLogContext()
    .MinimumLevel.Warning();

if( enableConsoleLogging )
{
    builder = builder
        .WriteTo.Console( outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate )
}

if( enableFileLogging )
{
    builder = builder
        .WriteTo.File( ... )
}
Log.Logger = builder.CreateLogger();

...这不是很优雅。

我知道我可以添加自己的 If 扩展方法(但我并不热衷于扩展现有的 API 设计,即使它看起来更漂亮):

Log.Logger = new LoggerConfiguration()
    .Enrich.WithExceptionDetails()
    .Enrich.FromLogContext()
    .MinimumLevel.Warning()
    .If( enableConsoleLogging, b => b.WriteTo.Console( outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate ) )
    .If( enableFileLogging, b => b.WriteTo.File( ... ) )
    .CreateLogger();


public static LoggerConfiguration If( this LoggerConfiguration cfg, Boolean test, Func<LoggerConfiguration,LoggerConfiguration> action )
{
    if( test ) return action( cfg );
    else       return cfg;
}

有哪些替代方案可以在运行时切换不同的 Serilog 选项?是否有任何方法可以与其他 "Fluent" API 一起使用?

我认为要使其优雅并仍然在代码中实现,您必须扩展 API 并创建自己的扩展方法来封装条件检查并使用正确的接收器和参数更新构建器.

类似

Log.Logger = new LoggerConfiguration()
    .Enrich.WithExceptionDetails()
    .Enrich.FromLogContext()
    .MinimumLevel.Warning()
    .WriteToConsoleIfEnabled()  // <---
    .WriteToFileIfEnabled()     // <---
    .CreateLogger();

另一方面,您是否考虑过使用 Serilog.Settings.AppSettings or Serilog.Settings.Configuration?代码中的配置变得更加清晰,您可以 add/remove 根据需要在配置文件中下沉...

Log.Logger = new LoggerConfiguration()
  .ReadFrom.AppSettings()
  .CreateLogger()

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="serilog:minimum-level" value="Verbose" />

    <add key="serilog:using:Console" value="Serilog.Sinks.Console" />
    <add key="serilog:write-to:Console" />

    <add key="serilog:using:RollingFile" value="Serilog.Sinks.RollingFile" />
    <add key="serilog:write-to:RollingFile.pathFormat" value="C:\myapp-{Date}.txt" />
    <add key="serilog:write-to:RollingFile.retainedFileCountLimit" value="10" />

    <!-- //etc... -->
  </appSettings>
</configuration>

Serilog 2.9.0 引入了条件接收器。使用 .WriteTo.Conditional 指定定义是否写入接收器的条件。

例如

bool enableConsoleLogging = ...
bool enableFileLogging = ...

var builder = new LoggerConfiguration()
    .Enrich.WithExceptionDetails()
    .Enrich.FromLogContext()
    .MinimumLevel.Warning()
    .WriteTo.Conditional(evt => enableConsoleLogging, wt => wt.Console())
    .WriteTo.Conditional(evt => enableFileLogging, wt => wt.File(...));

Log.Logger = builder.CreateLogger();
// ...