Serilog JSON配置LoggingLevelSwitch访问

8
使用JSON配置来配置Serilog,可以如下配置日志级别开关:
"LevelSwitches": {
  "$appLogLevel": "Debug",
  "$netLogLevel": "Information",
  "$sysLogLevel": "Error"
},
"MinimumLevel": {
  "ControlledBy": "$appLogLevel",
  "Override": {
    "Microsoft": "$netLogLevel",
    "System": "$sysLogLevel"
  }
}

开关(在代码中实例化时)的目的是为了在运行时稍后访问并更改最低日志级别。但是,当通过JSON配置进行配置时,我找不到访问这些开关实例的方法。有人知道如何访问它们吗?

2个回答

1
如果你想从代码中访问你的级别开关,这可能意味着你有一种控制它们的方式,所以首先你可能不需要在配置文件中使用它们... 我认为完全将该部分保留在代码中,并且部分保留在配置文件中更有意义,这看起来像是这样:
// in C# code
var appLevelSwitch = new LoggingLevelSwitch(LogEventLevel.Debug);
var netLevelSwitch= new LoggingLevelSwitch(LogEventLevel.Information);
var systemLevelSwitch= new LoggingLevelSwitch(LogEventLevel.Error);

var configuration = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .Build();

Log.Logger = new LoggerConfiguration()
            // load config from config file ...
            .ReadFrom.Configuration(configuration)
            // ... and complete it in C# code
            .MinimumLevel.ControlledBy(appLevelSwitch )
            .MinimumLevel.Override("Microsoft", netLevelSwitch)
            .MinimumLevel.Override("System", systemLevelSwitch)
            .CreateLogger();

并且在您的配置文件中

{
  "Serilog": {
    "Using":  ["Serilog.Sinks.Console"],
    "WriteTo": [
      { "Name": "Console" },
      { "Name": "File", "Args": { "path": "%TEMP%\\Logs\\serilog-configuration-sample.txt" } }
    ],
    "Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"],
    "Destructure": [
      { "Name": "With", "Args": { "policy": "Sample.CustomPolicy, Sample" } },
      { "Name": "ToMaximumDepth", "Args": { "maximumDestructuringDepth": 4 } },
      { "Name": "ToMaximumStringLength", "Args": { "maximumStringLength": 100 } },
      { "Name": "ToMaximumCollectionCount", "Args": { "maximumCollectionCount": 10 } }
    ],
    "Properties": {
        "Application": "Sample"
    }
  }
}

为了完整起见,为了访问定义的控制开关,您可以执行以下操作(请注意,这有点像黑客!)。
编写一个配置方法(即,在.WriteTo.xxx之后出现的扩展方法),该方法接受LoggingLevelSwitches作为参数并将它们存储为静态成员。该配置方法将引入一个虚拟ILogEventSink,它不执行任何操作(出于性能考虑,我们甚至可以指定restrictedToMinimumLevel:LogEventLevel.Fatal,以便几乎永远不会调用它)。然后从配置文件中调用该扩展方法(Serilog.Settings.Configuration知道如何找到扩展方法并将参数传递给它们),然后就可以从代码中访问static开关了!
下面是它的样子:
public static class LevelSwitches
{
    private static LoggingLevelSwitch _switch1;
    private static LoggingLevelSwitch _switch2;
    private static LoggingLevelSwitch _switch3;

    public static LoggingLevelSwitch Switch1 => _switch1 ?? throw  new InvalidOperationException("Switch1 not initialized !");
    public static LoggingLevelSwitch Switch2 => _switch2 ?? throw  new InvalidOperationException("Switch2 not initialized !");
    public static LoggingLevelSwitch Switch3 => _switch3 ?? throw  new InvalidOperationException("Switch3 not initialized !");

    public static LoggerConfiguration CaptureSwitches(
        this LoggerSinkConfiguration sinkConfig,
        LoggingLevelSwitch switch1,
        LoggingLevelSwitch switch2,
        LoggingLevelSwitch switch3)
    {
        _switch1 = switch1;
        _switch2 = switch2;
        _switch3 = switch3;

        return sinkConfig.Sink(
            restrictedToMinimumLevel: LogEventLevel.Fatal,
            logEventSink: new NullSink());
    }
}

public sealed class NullSink : ILogEventSink
{
    public void Emit(LogEvent logEvent)
    {
        // nothing here, that's a useles sink !
    }
}

然后在你的 JSON 配置文件中:
"LevelSwitches": {
  "$appLogLevel": "Debug",
  "$netLogLevel": "Information",
  "$sysLogLevel": "Error"
},
"MinimumLevel": {
  "ControlledBy": "$appLogLevel",
  "Override": {
    "Microsoft": "$netLogLevel",
    "System": "$sysLogLevel"
  }
},
"WriteTo":[
  {
    "Name": CaptureSwitches"", 
    "Args": {
      "switch1": "$appLogLevel",
      "switch2": "$netLogLevel",
      "switch3": "$sysLogLevel",
    }
  }
]

你可能需要一个"Using"指令,其中包含LevelSwitches类的程序集名称。

从配置文件配置你的日志记录器。

    var configuration = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json")
        .Build();

    var logger = new LoggerConfiguration()
        .ReadFrom.Configuration(configuration)
        .CreateLogger();

从那时起,您应该能够通过LevelSwitches.Switch1LevelSwitches.Switch2LevelSwitches.Switch3访问开关。

1

我的当前项目需要高度可配置的日志记录功能,以及在运行时调整任何已配置的日志级别的能力。

因此,我实际上已经编写了一个解决方法(但是以更通用的方式),通过在我的Program.cs中手动处理配置文件的“MinimalLevel”部分,像这样:

需要一个静态字典以供以后参考:

public static Dictionary<String, LoggingLevelSwitch> LogLevel = null;

还需要一个代码块来绑定LoggingLevelSwitches:

//Configure logger (optional)
  if (appConfig.GetSection("Serilog").Exists()) {
  //Configure Serilog
    LoggerConfiguration logConfig = new LoggerConfiguration().ReadFrom.Configuration(appConfig);
  //If Serilog config parsed okay acquire LoggingLevelSwitches
    LogLevel = LoadLoggingLevelSwitches(appConfig);
  //Bind LoggingLevelSwitches        
    foreach (String name in LogLevel.Keys) {
      if (String.Equals(name, "Default", StringComparison.InvariantCultureIgnoreCase)) {
        logConfig.MinimumLevel.ControlledBy(LogLevel[name]);
      } else {
        logConfig.MinimumLevel.Override(name, LogLevel[name]);
      }
    }
  //Build logger from config
    Log.Logger = logConfig.CreateLogger();
  }

这个程序利用一种例程来实例化所有这些开关(基于配置文件):

public static Dictionary<String, LoggingLevelSwitch> LoadLoggingLevelSwitches(IConfiguration cfg) {
  Dictionary<String, LoggingLevelSwitch> levels = new Dictionary<String, LoggingLevelSwitch>(StringComparer.InvariantCultureIgnoreCase);
//Set default log level
  if (cfg.GetSection("Serilog:MinimumLevel:Default").Exists()) {
    levels.Add("Default", new LoggingLevelSwitch((LogEventLevel)Enum.Parse(typeof(LogEventLevel), cfg.GetValue<String>("Serilog:MinimumLevel:Default"))));
  }
//Set log level(s) overrides
  if (cfg.GetSection("Serilog:MinimumLevel:Override").Exists()) {
    foreach (IConfigurationSection levelOverride in cfg.GetSection("Serilog:MinimumLevel:Override").GetChildren()) {
      levels.Add(levelOverride.Key, new LoggingLevelSwitch((LogEventLevel)Enum.Parse(typeof(LogEventLevel), levelOverride.Value)));
    }
  }      
  return levels;
}

我有一个单独的类来处理运行时日志级别的更改,通过这些开关进行操作,但这是获取我需要的任何和所有东西最简单的方法,然而...

在编写了所有代码之后,发现可以直接从配置文件中添加开关,使用"LevelSwitches"部分,我意识到我可能重复了这项工作。因为显然Serilog需要实例化并绑定其在配置文件中定义的开关...只是似乎没有一个简单易用的方法来访问它们,以便稍后使用。这是违反直觉的,因为LoggingLevelSwitch的整个目的是在运行时引用它。

看起来如果允许通过配置文件创建开关,我们应该能够轻松地访问它们。也许我应该在Serilog GitHub上将此添加为功能请求。


1
实际上,通过配置声明开关是为了支持(目前)Seq汇聚器,它可以接受现有的级别开关并远程控制它(请参见https://github.com/serilog/serilog-sinks-seq#dynamic-log-level-control)。不过,您可以提出一个功能请求,这可能会引起有趣的讨论 :) - tsimbalar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接