有没有办法使用 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.cs
、ConstructNamedProperties()
第 111 行)
这个调用 ConstructProperty()
只是忽略了令牌的 Format
属性,这解释了为什么它被忽略,但我想知道是否有一种方法可以工作没想到
PS
是的,我知道我有几个选项可以,但我不想这样做:
- 使用自定义解构器或自己手动将属性提取为匿名类型 - 这实质上会破坏原始对象,这正是我不想要的(我仍然希望将原始值存储为 属性).例如。
Log.Information("{Task}", new {Name = this.Name, Id = this.Id});
- 使用我自己的格式字符串手动调用
ToString()
- 与 (1) 相同,这会破坏原始字符串,并且意味着它不会与所有信息一起存储。例如。 Log.Information("{Task}", this.ToString("Custom Format"));
- 在我的对象上创建一个 属性,例如
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}
等。
我有一个 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.cs
、ConstructNamedProperties()
第 111 行)
这个调用 ConstructProperty()
只是忽略了令牌的 Format
属性,这解释了为什么它被忽略,但我想知道是否有一种方法可以工作没想到
PS 是的,我知道我有几个选项可以,但我不想这样做:
- 使用自定义解构器或自己手动将属性提取为匿名类型 - 这实质上会破坏原始对象,这正是我不想要的(我仍然希望将原始值存储为 属性).例如。
Log.Information("{Task}", new {Name = this.Name, Id = this.Id});
- 使用我自己的格式字符串手动调用
ToString()
- 与 (1) 相同,这会破坏原始字符串,并且意味着它不会与所有信息一起存储。例如。Log.Information("{Task}", this.ToString("Custom Format"));
- 在我的对象上创建一个 属性,例如
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}
等。