Serilog是否支持为自定义对象设置带格式说明符的属性格式化?
方案1:实现Serilog专属的IStructuralFormattable接口(最适配)
Serilog提供了IStructuralFormattable接口专门解决「既保留完整结构化属性存储,又支持自定义日志渲染格式」的场景,实现后你可以直接在模板里使用{Task:Name}这类格式说明符,不会丢失原始对象的任何属性。
代码示例:
using Serilog.Core; using Serilog.Events; public class BackgroundTask : IStructuralFormattable { // 你的原有属性 public string Name { get; set; } public int Id { get; set; } public int Progress { get; set; } // 接口实现 public string ToString(string format, IFormatProvider formatProvider, ILogEventPropertyValueCollector propertyValueCollector) { // 收集所有需要存入日志的原始属性,确保结构化存储完整 propertyValueCollector.Add(new LogEventProperty(nameof(Name), new ScalarValue(Name))); propertyValueCollector.Add(new LogEventProperty(nameof(Id), new ScalarValue(Id))); propertyValueCollector.Add(new LogEventProperty(nameof(Progress), new ScalarValue(Progress))); // 自定义格式匹配逻辑 return format?.ToLower() switch { "name" => Name, "id" => Id.ToString(), _ => Progress == 100 ? $"{Name}(已完成)" : $"{Name} 进度:{Progress}%" }; } // 保留默认ToString方法兼容非Serilog场景 public override string ToString() => ToString(null, null, null); }
这个方案的优势是逻辑内聚在实体类内部,调用端不需要做任何额外配置,直接按Log.Information("{Task:Name} 执行完成", this)的写法就能生效。
方案2:自定义全局格式提供器(零耦合)
如果你不希望领域实体依赖Serilog的类库,可以通过自定义IFormatProvider的方式实现,实体类不需要做任何修改,只需要在Serilog配置阶段注入格式提供器即可。
代码示例:
- 实现自定义格式提供器
public class BackgroundTaskFormatProvider : IFormatProvider, ICustomFormatter { public object GetFormat(Type formatType) { return formatType == typeof(ICustomFormatter) ? this : null; } public string Format(string format, object arg, IFormatProvider formatProvider) { // 只处理BackgroundTask类型的格式化 if (arg is not BackgroundTask task) return FallbackFormat(format, arg, formatProvider); return format?.ToLower() switch { "name" => task.Name, "id" => task.Id.ToString(), _ => task.Progress == 100 ? $"{task.Name}(已完成)" : $"{task.Name} 进度:{task.Progress}%" }; } // 其他类型走默认格式化逻辑 private string FallbackFormat(string format, object arg, IFormatProvider formatProvider) { return arg is IFormattable formattable ? formattable.ToString(format, formatProvider) : arg?.ToString() ?? string.Empty; } }
- 在Serilog配置中注入格式提供器
Log.Logger = new LoggerConfiguration() // 给所有需要用到自定义格式的Sink都注入格式提供器 .WriteTo.Console(formatProvider: new BackgroundTaskFormatProvider()) .WriteTo.File("logs/app.log", formatProvider: new BackgroundTaskFormatProvider()) .CreateLogger();
这个方案的优势是实体类完全不需要依赖Serilog相关组件,无侵入性。
两种方案都完全满足要求:
- 完整保留原始
BackgroundTask对象的所有属性作为结构化日志存储,不会丢失信息 - 支持
{Task:Name}这类格式说明符自定义渲染内容 - 不需要使用你补充说明里提到的几种不期望的实现方式
内容的提问来源于stack exchange,提问作者user7424240
相关产品推荐
相关产品推荐

