如何在日志上下文中使用 Serilog enrichers

How to use Serilog enrichers with log context

Serilog enrichers - 尤其是 LogEvent - 是否应该知道推送到 LogContext 上的属性?

我有一个 属性 被推送到 Serilog 上下文中:

using (LogContext.PushProperty(LoggingConstants.MyProperty, _myValue))
{
   // ...
}

我还注册了一个自定义增强器,如果它不存在,它会添加一个 属性,将其填充到指定长度:

public class PaddedPropertyEnricher : ILogEventEnricher
{
    private readonly string _propertyName;
    private readonly int _maxLength;

    public PaddedPropertyEnricher(string propertyName, int maxLength)
    {
      _propertyName = propertyName;
      _maxLength = maxLength;
    }

    public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
    {
      var property = propertyFactory.CreateProperty(_propertyName, new string(' ', _maxLength));
      logEvent.AddPropertyIfAbsent(property);
    }
}

和:

Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .Enrich.With(new PaddedPropertyEnricher(LoggingConstants.MyProperty, 3))
                 // etc.

此增强器的目的是让不包含自定义 属性 的日志条目不会导致日志条目中的列错位。

比如打算改造这个:

2021-06-10 12:23:33.676 +12:00 [INF] [] First message without MyProperty
2021-06-10 12:23:33.677 +12:00 [DBG] [123] Second message with MyProperty

进入这个:

2021-06-10 12:23:33.676 +12:00 [INF] [   ] First message without MyProperty
2021-06-10 12:23:33.677 +12:00 [DBG] [123] Second message with MyProperty

但是,这个增强器不起作用。当针对我推送到上下文的 属性 注册丰富器时,从 LogEvent 检索的值始终是 null - 除非 MyProperty 明确地在日志消息本身中.

这是一个已知的限制,还是我滥用了增强剂?

这可能与在Always log context data using serilog遇到的问题有关。

问题是因为我的自定义增强器在 Enrich.FromLogContext() 之前被注册了;例如:

// Bad configuration
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .Enrich.With(new PaddedPropertyEnricher(LoggingConstants.MyProperty, 3))
    .WriteTo.Console(minimumLogLevels.Console, LogFormat)

     // Other sink configuration here

    .Enrich.FromLogContext()
    .CreateLogger();

通过确保 Enrich.FromLogContext() 在任何依赖它的 enricher 之前发生,该问题已得到解决;即:

// Good configuration
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.Console(minimumLogLevels.Console, LogFormat)

     // Other sink configuration here

    .Enrich.FromLogContext()
    .Enrich.With(new PaddedPropertyEnricher(LoggingConstants.MyProperty, 3))
    .CreateLogger();

感谢 Olivier Roger 提示我查看完整的 Log.Logger 链。

am I misusing enrichers?

我相信你就是这种情况。

TL;DR; Enricher 在所有接收器之间共享,因此如果您只想针对一个特定接收器的输出格式,那么它们不是最佳选择。要控制接收器的输出,您应该使用接收器提供的机制,它可以是输出模板,也可以是自定义 IFormatProvider.


您创建的 属性 似乎只是为了影响 格式化输出 specific 接收器的方式。这不是为接收器格式化输出的“Serilog 方式”。

当您通过 Enricher 创建 属性 时,它们对所有接收器可用,并且每个接收器都有责任根据位置决定如何最好地序列化属性他们写信给。

通过创建带填充的 属性,您假设所有接收器都希望 属性 的格式与此完全相同,将来可能不会出现这种情况。

如果您只想在控制台接收器或使用标准 Serilog 输出模板的任何其他接收器中添加一些填充,那么您可以使用填充语法:

.WriteTo.Console(outputTemplate: "{Message}[{MyProperty,3}]")

这将确保在呈现 属性 时填充 3 个空格(如果 属性 不存在,则填充 3 个空格)。

如果您需要对接收器的输出进行更多控制,那么您可以提供自定义 IFormatProvider


您可以在 Formatting-Output 上的文档中查看更多详细信息。