【问题标题】:Serilog JSON config LoggingLevelSwitch accessSerilog JSON 配置 LoggingLevelSwitch 访问
【发布时间】:2018-07-22 21:09:04
【问题描述】:

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

"LevelSwitches": {
  "$appLogLevel": "Debug",
  "$netLogLevel": "Information",
  "$sysLogLevel": "Error"
},
"MinimumLevel": {
  "ControlledBy": "$appLogLevel",
  "Override": {
    "Microsoft": "$netLogLevel",
    "System": "$sysLogLevel"
  }
}

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

【问题讨论】:

    标签: serilog asp.net-core-2.1 .net-core-2.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 上将此作为功能请求添加。

    【讨论】:

    【解决方案2】:

    如果你想从代码中访问你的关卡开关,这可能意味着你有办法以某种方式控制它们,所以你可能一开始就不需要它们在配置文件中......

    我确实认为将那部分完全保留在代码中并具有配置部分在代码中,部分在配置文件中更有意义,所以看起来像这样:

    // 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"
        }
      }
    }
    

    不过,为了完整起见,为了访问定义的控制开关,您可以执行以下操作(请注意,这是一种黑客攻击!)。

    编写一个接受LoggingLevelSwitches作为参数并将它们存储为static成员的配置方法(即可以出现在.WriteTo.xxx之后的扩展方法)。该配置方法将引入一个 dummy ILogEventSink,它什么都不做(为了性能,我们甚至可以指定 restrictedToMinimumLevel: LogEventLevel.Fatal,这样它几乎不会被调用)。然后从配置文件中调用该扩展方法(Serilog.Settings.Configuration 知道如何找到扩展方法并将参数传递给它们)和 voilà,您现在可以访问 @ 987654328@ 从您的代码切换!

    这是它的样子:

    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 访问开关。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-26
      • 1970-01-01
      • 2019-08-27
      • 2019-04-26
      相关资源
      最近更新 更多