根据上下文源将 Serilog 日志过滤到不同的接收器?

Posted

技术标签:

【中文标题】根据上下文源将 Serilog 日志过滤到不同的接收器?【英文标题】:Filter Serilog logs to different sinks depending on context source? 【发布时间】:2018-03-12 23:37:19 【问题描述】:

我有一个 .NET Core 2.0 应用程序,我在其中成功使用 Serilog 进行日志记录。现在,我想将一些数据库性能统计信息记录到一个单独的接收器(它们不是用于调试,这基本上是应用程序中所有其他日志记录的目的,所以我想将它们分开)并认为这可以完成通过使用Log.ForContext<MyClass>() 创建数据库统计记录器。

我不知道应该如何配置 Serilog 使用我的 appsettings.json 将我的“调试日志”记录到一个接收器并将我的数据库统计日志记录到另一个接收器?我希望可以做类似的事情:

"Serilog": 
  "WriteTo": [
    
      "Name": "RollingFile",
      "pathFormat": "logs/Log-Date.log",
      "Filter": 
        "ByExcluding": "FromSource(MyClass)"
      
    ,
    
      "Name": "RollingFile",
      "pathFormat": "logs/DBStat-Date.log",
      "Filter": 
          "ByIncludingOnly": "FromSource(MyClass)"
      
    
  ]

配置的"Filter" 部分纯粹是我的猜测。是否可以使用我的配置文件管理器或我需要在我的 Startup.cs 文件中的代码中执行此操作?

编辑:我已经使用 C# API 让它工作,但仍想使用 JSON 配置来解决它:

Log.Logger = new LoggerConfiguration()
            .WriteTo.Logger(lc => lc
                .Filter.ByExcluding(Matching.FromSource<MyClass>())
                .WriteTo.LiterateConsole())
            .WriteTo.Logger(lc => lc
                .Filter.ByExcluding(Matching.FromSource<MyClass>())
                .WriteTo.RollingFile("logs/DebugLog-Date.log"))
            .WriteTo.Logger(lc => lc
                .Filter.ByIncludingOnly(Matching.FromSource<MyClass>())
                .WriteTo.RollingFile("logs/DBStats-Date.log", outputTemplate: "MessageNewLine"))
            .CreateLogger();

【问题讨论】:

写出正确答案的时间太短了;这在 JSON 中相当难看,但可能。您将需要Serilog.Filters.Expressions 和几个WriteTo.Logger 块,如github.com/skomis-mm/serilog-settings-configuration/blob/… 所示。过滤器需要在每个子记录器的级别进行配置。 HTH! @NicholasBlumhardt 感谢您的提示。我能让“过滤器”真正做某事的唯一方法是将它放在最外层,即作为“Serilog”的方向子级。在那个级别上,它会影响所有接收器,这违背了本练习的目的。如果我将“过滤器”作为 Logger 接收器或子记录器(“RollingFile”)的子级,则它没有任何效果。还有什么提示吗? 嗯,不确定会发生什么;你有没有直接使用 C# API 工作?可能是很好的第一步。 @NicholasBlumhardt 让它使用 API 工作。查看更新的问题。 @NicholasBlumhardt,关于在 appsettings.json 中使用不同的基于上下文的规则描述不同的接收器,我有同样的问题。示例 github.com/skomis-mm/serilog-settings-configuration/blob/… 不会为不同的子记录器应用不同的过滤器。你能推荐一些其他的例子吗?还是在 Serilog 过滤器 GitHub 上提出问题更好? 【参考方案1】:

我今天完成了这项工作,并认为我会提供一个正确的答案,因为我花了很多帖子、问题和其他页面来解决这个问题。

拥有所有日志很有用,但我也想单独记录我的 API 代码,并省略 Microsoft. 命名空间日志。执行此操作的 JSON 配置如下所示:

  "Serilog": 
    "Using": [ "Serilog.Sinks.File" ],
    "MinimumLevel": "Debug",
    "WriteTo": [
      
        "Name": "File",
        "Args": 
          "path": "/var/logs/system.log",
          ... //other unrelated file config
        
      ,
      
        "Name": "Logger",
        "Args": 
          "configureLogger": 
            "WriteTo": [
              
                "Name": "File",
                "Args": 
                  "path": "/var/logs/api.log",
                  ... //other unrelated file config
                
              
            ],
            "Filter": [
              
                "Name": "ByExcluding",
                "Args": 
                  "expression": "StartsWith(SourceContext, 'Microsoft.')"
                
              
            ]
          
        
      
    ],
    "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
    ... //Destructure and other config
  

***WriteTo 是第一个简单的全局接收器。所有日志事件都写入此。如果在与此相同的级别上添加Filter 部分,它将影响所有已配置的WriteTo 元素。

然后我将另一个WriteTo 配置为Logger(不是File),但Args 看起来不同,并且有一个configureLogger 元素,其作用与顶部的Serilog 相同level,也就是子logger的最顶层。这意味着您可以轻松地将其配置拆分为一个单独的文件,并在配置生成器中额外添加(见底部)。

从这里开始,此子记录器的工作方式相同:您可以配置多个WriteTos,并且此级别上的Filter 元素将仅影响此子记录器。

只需将更多 "Name": "Logger" 元素添加到*** WriteTo 部分并分别为每个元素设置过滤器。

注意 还需要注意的是,即使您在 config 中执行所有这些操作并且没有在代码中引用 Serilog.Filters.Expressions 包的任何一部分,您仍然必须添加对该包的 NuGet 引用。 没有包参考是不行的

关于拆分配置:

如果我必须添加更多记录器,为了清楚起见,我肯定会将不同的记录器拆分为单独的文件,例如

appsettings.json:

  "Serilog": 
    "Using": [ "Serilog.Sinks.File" ],
    "MinimumLevel": "Error",
    "WriteTo": [
      
        "Name": "File",
        "Args": 
          "path": "/var/logs/system.log",
          ...
        
      ,
      
        "Name": "Logger",
        "Args": 
          "configureLogger":  // leave this empty
        
      
    ],
    "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
    ...

apilogger.json:


  "Serilog:WriteTo:1:Args:configureLogger":    //notice this key
    "WriteTo": [
      
        "Name": "File",
        "Args": 
          "path": "/var/logs/api_separateFile.log",
          ...
        
      
    ],
    "Filter": [
      
        "Name": "ByExcluding",
        "Args": 
          "expression": "StartsWith(SourceContext, 'Microsoft.')"
        
      
    ]
  

然后调整我的 IWebHost 构建器以包含附加配置:

    WebHost.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, config) =>
        
            config.AddJsonFile("apilogger.json", optional: false, reloadOnChange: false);
        )
        .UseStartup<Startup>();

这样更容易理解、阅读和维护。

【讨论】:

这并没有完全解决我的问题,但它是最完整的,几乎是我正在寻找的,我可以用我的 google-fu 找到的响应。谢谢! 第二个必须命名为Logger吗?当我将它命名为 Hangfire 时,我什么也做不了 是的。 Hangfire 是一个特定的接收器,很像 File 或类似 AppInsights 的东西。 Logger 就像配置中用于设置子记录器的保留字,您可以在其中指定更多特定的接收器。或许可以将这里的Logger 视为代码中的ILoggerFactory 这个解决方案的工作方式与原来的问题不同吗?在问题中表示,前 2 个接收器将排除只有第 3 个接收器会记录的内容。答案似乎表明第一个接收器将吞噬所有出现的东西,而第二个接收器将做同样的事情,但不包括以“Microsoft”开头的所有内容。不是吗??还是这也是针对原始问题的? 据我所知,使用 appsettings.json 来过滤***记录器而不是子级记录器是不可能的。 【参考方案2】:

下面仅将实体框架日志写入文件,但我们需要安装 Serilog.Filters.Expressions nuget 包

"Serilog": 
    "MinimumLevel": 
      "Default": "Debug",
      "Override": 
        "Microsoft": "Warning",
        "System": "Warning",
        "Microsoft.EntityFrameworkCore": "Information"
      
    ,
    "WriteTo": [
      
        "Name": "Logger",
        "Args": 
          "configureLogger": 
            "Filter": [
              
                "Name": "ByIncludingOnly",
                "Args": 
                  "expression": "StartsWith(SourceContext, 'Microsoft.EntityFrameworkCore')"
                
              
            ],
            "WriteTo": [
              
                "Name": "File",
                "Args": 
                  "path": "C:\\LOGS\\TestService_EF_.json",
                  "outputTemplate": "Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz [Level:u3] Message:ljNewLineException",
                  "rollingInterval": "Day",
                  "retainedFileCountLimit": 7
                
              
            ]
          
        
      
    ]
  

【讨论】:

【参考方案3】:

我必须做类似的事情,但在代码中,而不是 JSON。如https://github.com/serilog/serilog/wiki/Configuration-Basics 底部所述,使用记录器作为接收器就可以了。

在我的情况下,我想将所有内容记录到文件和控制台中,除了来自特定来源的消息应该只进入文件:

private static bool SourceContextEquals(LogEvent logEvent, Type sourceContext)
    => logEvent.Properties.GetValueOrDefault("SourceContext") is ScalarValue sv && sv.Value?.ToString() == sourceContext.FullName;

private static ILogger CreateLogger() =>
    new LoggerConfiguration()
        .WriteTo.File("mylog.log")
        .WriteTo.Logger(lc =>
            lc.Filter.ByExcluding(le => SourceContextEquals(le, typeof(TypeThatShouldLogOnlyToFile)))
           .WriteTo.Console()
        )
        .CreateLogger();

【讨论】:

以上是关于根据上下文源将 Serilog 日志过滤到不同的接收器?的主要内容,如果未能解决你的问题,请参考以下文章

Serilog - 多个日志文件

使用serilog直接将日志写入elasticsearch是否一个好主意

serilog 没有将日志发送到 logstash

如何更改灰度图像的 cairo 上下文源颜色

哪个 Serilog 接收器用于发送到 Logstash?

使用上下文信息进行日志记录