如何使用appsettings.json覆盖serilog设置

3

我有一个扩展方法用于配置我的日志记录器:

 public static class Extensions
 {
     public static IWebHostBuilder UseLogging(this IWebHostBuilder webHostBuilder) =>
         webHostBuilder.UseSerilog((context, loggerConfiguration) =>
         {
             var logLevel = context.Configuration.GetValue<string>("Serilog:MinimumLevel");
             if (!Enum.TryParse<LogEventLevel>(logLevel, true, out var level))
             {
                 level = LogEventLevel.Information;
             }

             loggerConfiguration.Enrich
                 .FromLogContext()
                 .MinimumLevel.Is(level);

             loggerConfiguration
                 .ReadFrom.Configuration(context.Configuration)
                 .WriteTo.Console(
                     theme: AnsiConsoleTheme.Code,
                     outputTemplate: "[{Timestamp:yy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj} <s:{SourceContext}>{NewLine}{Exception}");
         });
 }

我添加了这一行.ReadFrom.Configuration(context.Configuration),期望从appsettings.json中获取的所有设置都会覆盖当前配置。也就是说,如果我在appsettings.json中指定了另一个输出模板,则它将覆盖现有的输出模板。

    "Serilog": {
        "WriteTo": [
            {
                "Name": "Console",
                "Args": {
                    "outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} <s:{SourceContext}>{NewLine}{Exception}"
                }
            }
        ]
    }

但它不起作用。现在,日志消息重复了,一个是来自代码的格式化,另一个是来自配置的。我如何在运行时覆盖appsettings.json中的某些设置?

[03:59:09 INF] 事件处理器服务正在启动。

[19-04-19 00:59:09 INF] 事件处理器服务正在启动。

我有一个扩展方法,我在服务中使用它,但有时我需要覆盖appsettings.json(或环境变量)中的某些设置,我该如何做到这一点?因为目前的解决方案无法正常工作且会添加第二个记录器。

1个回答

3

你需要检查配置文件是否已经指定了控制台记录器。存储接收器的字段是私有的。因此,你需要使用类似于以下代码来响应配置文件。当你想要覆盖日志记录器上的某些属性时,你也需要使用反射访问这些字段。下面的代码可以用于简单测试应用程序。

    public static class Extensions
    {
        public static IWebHostBuilder UseLogging(this IWebHostBuilder webHostBuilder) =>
            webHostBuilder.UseSerilog((context, loggerConfiguration) =>
            {
                var logLevel = context.Configuration.GetValue<string>("Serilog:MinimumLevel");
                if (!Enum.TryParse<LogEventLevel>(logLevel, true, out var level))
                {
                    level = LogEventLevel.Information;
                }

                loggerConfiguration.Enrich
                    .FromLogContext()
                    .MinimumLevel.Is(level);

                loggerConfiguration
                    .ReadFrom.Configuration(context.Configuration);

                // Get the field that holds the sinks.
                var sinks = loggerConfiguration.GetType()
                    .GetField("_logEventSinks", BindingFlags.Instance | BindingFlags.NonPublic)
                    .GetValue(loggerConfiguration) as List<ILogEventSink>;

                // Get the sink type for reusage.
                var sinkType = typeof(AnsiConsoleTheme).Assembly.GetType("Serilog.Sinks.SystemConsole.ConsoleSink");

                // Find the first sink of the right type.
                var sink = sinks?.FirstOrDefault(s => sinkType == s.GetType());

                // Check if a sink was found.
                if (sink == null)
                {
                    // No sink found add a new one.
                    loggerConfiguration
                        .WriteTo.Console(
                            theme: AnsiConsoleTheme.Code,
                            outputTemplate:
                            "[{Timestamp:yy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj} <s:{SourceContext}>{NewLine}{Exception}");
                }
                else
                {
                    // Otherwise change the theme.
                    sinkType.GetField("_theme", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(sink, AnsiConsoleTheme.Code);
                }
            });
    }

谢谢。但是如何仅覆盖一些属性?我不想在我的appsettings.json中有sink的完整声明。我只需要覆盖一些属性。 - Pr.Dumbledor
我已经添加了改变_theme字段的代码。它只部分地起作用。有些东西没有刷新。删除预定义的sink并用自己的覆盖它,并读取您不想手动覆盖的配置可能更容易一些。 - superreeen
我不知道具体应该重写哪个属性,因为它对于任何服务来说都可能是不同的,而不仅仅是主题或格式化程序。 - Pr.Dumbledor
我认为你运气不好,即使是针对一个具体的汇集器更改一个特定字段也会出现问题。试着明确你的实际目标是什么。也许有其他方法可以达到你的需求。但是我可以告诉你,在处理反射超过几个汇集器的路上,你走不通。 - superreeen

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