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

如何在Serilog的ForContext中序列化对象而非字符串化

解决Serilog额外关联数据的结构化存储问题

要实现不在日志文本中显示额外数据,但让自定义Sink能获取到结构化JSON格式的关联数据,可以用以下两种可靠方法:

方法一:带解构参数的ForContext(推荐)

Serilog的ForContext方法支持第三个参数destructureObjects,将其设为true后,会像{@}标记一样对对象进行结构化解构,而不是默认调用ToString()生成字符串。

示例代码

// 定义你的额外数据对象
var additionalDetails = new 
{
    UserId = 456,
    Operation = "Delete",
    Context = new { ClientIp = "10.0.0.5", Timestamp = DateTime.UtcNow }
};

// 初始化Logger(包含你的自定义Sink)
Log.Logger = new LoggerConfiguration()
    .WriteTo.Sink(new YourCustomSink())
    .CreateLogger();

// 传递结构化上下文数据,不影响日志消息文本
Log.ForContext("AdditionalDetails", additionalDetails, destructureObjects: true)
   .Information("用户执行了删除操作");

自定义Sink中解析JSON

在你的Sink里,通过LogEvent.Properties获取结构化属性,并用Serilog内置的JsonValueFormatter序列化为标准JSON:

public class YourCustomSink : ILogEventSink
{
    public void Emit(LogEvent logEvent)
    {
        if (logEvent.Properties.TryGetValue("AdditionalDetails", out var propertyValue))
        {
            var jsonFormatter = new JsonValueFormatter();
            using var stringWriter = new StringWriter();
            
            jsonFormatter.Format(propertyValue, stringWriter);
            string validJson = stringWriter.ToString();
            
            // 将validJson存储到目标位置
        }
    }
}

这种方式完全不会污染日志消息文本,且能保证数据是结构化的有效JSON,和{@additionalDetails}的解析效果一致。

方法二:隐藏的消息模板占位符(备选)

如果必须通过日志方法的参数传递数据,可以在消息模板中添加一个不会被输出模板渲染的占位符,同时用@标记解构对象:

// 消息模板中的{@_hiddenDetails}不会在常规日志输出中显示(需配合输出模板配置)
Log.Information("用户执行了更新操作 {@_hiddenDetails}", additionalDetails);

然后在自定义Sink中直接提取_hiddenDetails属性即可。但这种方法需要确保你的输出模板(比如控制台输出)不包含该属性,否则会显示出来,因此不如方法一灵活干净。

注意事项

  • 不要手动用JsonConvert.SerializeObject把对象转成字符串再传递,这样Sink拿到的只是字符串,丢失了结构化信息,后续无法灵活解析。
  • 避免在ForContext的属性名前加@,那是消息模板中的解构标记,上下文属性名用普通字符串即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 07:40:23