有没有办法使用 Serilog 提供自定义 属性 格式?

Is there a way to provide custom property formatting with Serilog?

我有一个 class BackgroundTask,我已将其设置为在事情发生时记录事件,例如任务完成时。例如,对于 Success 的情况,我调用 Log.Verbose("{Task} completed successfully", this); 我的默认 ToString() 包括进度,但对于已完成的任务,我们知道它是 100%,所以我想忽略它。我知道对于数字类型,您可以传入自定义格式说明符字符串(如 "{IntProperty:p5}",这会将我的 int 呈现为小数点后 5 位的百分比),我希望能够这样做,例如作为 Log.Information("{Task:Name}", this),它将 "Name" 传递到我的 ToString() 方法中。

我试过添加很多不同的方法,比如添加 ToString(),(什么都不接受,一个字符串,一个字符串和 IFormatProvider),实现 IFormattable,强制字符串化等似乎没有任何效果。我可以看到 Serilog 正在正确解析我的格式字符串:(PropertyBinder.csConstructNamedProperties() 第 111 行)

这个调用 ConstructProperty() 只是忽略了令牌的 Format 属性,这解释了为什么它被忽略,但我想知道是否有一种方法可以工作没想到

PS 是的,我知道我有几个选项可以,但我不想这样做:

  1. 使用自定义解构器或自己手动将属性提取为匿名类型 - 这实质上会破坏原始对象,这正是我不想要的(我仍然希望将原始值存储为 属性).例如。 Log.Information("{Task}", new {Name = this.Name, Id = this.Id});
  2. 使用我自己的格式字符串手动调用 ToString() - 与 (1) 相同,这会破坏原始字符串,并且意味着它不会与所有信息一起存储。例如。 Log.Information("{Task}", this.ToString("Custom Format"));
  3. 在我的对象上创建一个 属性,例如 ToStringFormat,然后再将其传递到 Serilog - 这似乎是不好的做法,只会增加额外的混乱,更不用说并发问题了。例如。 this.Format = "Custom FOrmat"; Log.Information("{Task}", this);

这是由于 Serilog 管道中 捕获 格式化 之间的拆分。

为了在渲染到接收器时处理像 :X 这样的格式字符串,实现 IFormattable 的原始对象需要可用。

但是,由于接收器通常异步处理事件,Serilog 无法确定任何给定的记录对象都是线程安全的,因此任何未知类型都会在 time/site 的日志记录中使用 [=13] 捕获=].

为了解决这个问题,您需要告诉 Serilog 您的 Point class 是一个(本质上不可变的)值类型:

    .Destructure.AsScalar(typeof(Point))

配置记录器时。然后,您可以在 Point 上实现 IFormattable 并在模板中使用 {Point:X} 等。