You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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配置阶段注入格式提供器即可。
代码示例:

  1. 实现自定义格式提供器
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;
    }
}
  1. 在Serilog配置中注入格式提供器
Log.Logger = new LoggerConfiguration()
    // 给所有需要用到自定义格式的Sink都注入格式提供器
    .WriteTo.Console(formatProvider: new BackgroundTaskFormatProvider())
    .WriteTo.File("logs/app.log", formatProvider: new BackgroundTaskFormatProvider())
    .CreateLogger();

这个方案的优势是实体类完全不需要依赖Serilog相关组件,无侵入性。


两种方案都完全满足要求:

  1. 完整保留原始BackgroundTask对象的所有属性作为结构化日志存储,不会丢失信息
  2. 支持{Task:Name}这类格式说明符自定义渲染内容
  3. 不需要使用你补充说明里提到的几种不期望的实现方式

内容的提问来源于stack exchange,提问作者user7424240

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 04:15:09