我有一个 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.cs
,ConstructNamedProperties()
第 111 行)
这会调用 ConstructProperty()
,它只是忽略了 token 的 Format
属性,这解释了为什么它被忽略,但我想知道是否有一种方法可以我没有想到的工作。
附言 是的,我知道我有几个可以做的选择,但我不想做这些:
- 使用自定义解构器或自己手动将属性提取为匿名类型 - 这实质上会破坏原始对象,这正是我不想要的(我仍然希望将原始值存储为属性)。例如。
Log.Information("{Task}", new {Name = this.Name, Id = this.Id});
- 使用我自己的格式字符串手动调用
ToString()
- 与 (1) 相同,这会破坏原始字符串,并且意味着它不会与所有信息一起存储。例如。Log.Information("{Task}", this.ToString("自定义格式"));
- 在将对象传递到 Serilog 之前,在我的对象上创建一个属性,如
ToStringFormat
- 这似乎是不好的做法,只会增加额外的困惑,更不用说并发问题了。例如。this.Format = "自定义格式"; Log.Information("{Task}", this);
最佳答案
这是由于 Serilog 管道中捕获 和格式化 之间的分离。
为了在渲染到接收器时处理像 :X
这样的格式字符串,需要提供实现 IFormattable
的原始对象。
但是,由于接收器通常异步处理事件,Serilog 无法确定任何给定的记录对象都是线程安全的,因此在使用 ToString()< 进行记录的时间/地点捕获任何未知类型
.
为了解决这个问题,您需要告诉 Serilog 您的 Point
类是一个(本质上不可变的)值类型:
.Destructure.AsScalar(typeof(Point))
配置记录器时。然后,您可以在 Point
上实现 IFormattable
并在模板中使用 {Point:X}
等。
关于c# - 有没有办法使用 Serilog 提供自定义属性格式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69654180/