Serilog JSON配置LoggingLevelSwitch访问

时间:2018-07-17 20:16:22

标签: serilog asp.net-core-2.1 .net-core-2.1

使用JSON配置配置Serilog,可以这样配置日志级别开关:

new Date(field).toISOString()

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

2 个答案:

答案 0 :(得分: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作为参数并将其存储为LoggingLevelSwitch成员的配置方法(即可以在static之后出现的扩展方法)。该配置方法将引入什么都不做的 dummy ILogEventSink(出于性能考虑,我们甚至可以指定restrictedToMinimumLevel: LogEventLevel.Fatal使其几乎永远不会被调用)。然后从配置文件中调用该扩展方法( Serilog.Settings.Configuration 知道如何找到扩展方法并向其传递参数)和voilà,您现在可以访问{ {1}}从您的代码切换!

这是它的样子:

static

然后在您的json配置文件中:

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 !
    }
}

(您可能需要一个"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.Switch2访问交换机。

答案 1 :(得分: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上。