2

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

我尝试添加许多不同的方法,例如添加ToString()'s,(不接受任何内容、字符串、字符串和IFormatProvider)、实现IFormattable、强制字符串化等,但似乎没有任何效果。我可以看到 Serilog 正在正确解析我的格式字符串: ( PropertyBinder.csConstructNamedProperties()第 111 行) serilog 代码的调试视图

这个调用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);
4

1 回答 1

0

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

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

但是,由于 sinks 经常异步处理事件,Serilog 不能确定任何给定的记录对象是线程安全的,因此任何未知类型都会在使用ToString().

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

    .Destructure.AsScalar(typeof(Point))

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

于 2021-10-24T22:23:54.583 回答