如何在Serilog的RenderedCompactJsonFormatter中输出解构对象的转义JSON
Serilog实现结构化日志的理想输出格式方案
需求明确
我们需要用Serilog的RenderedCompactJsonFormatter输出日志时,同时达成两个目标:
- 结构化的
Message属性保持对象解构后的层级结构 @m字段中的对象内容显示为可直接复制的转义JSON字符串
核心实现思路
直接传入对象时,Serilog会自动解构对象到Message属性,但@m会输出可读的对象结构;仅传入JSON字符串则@m是转义JSON,但Message是普通字符串。要兼顾两者,需将对象序列化为JSON字符串作为日志消息,同时把原始对象通过LogContext添加为结构化属性,让Serilog自动解构对象到Message字段。
具体步骤
1. 准备JSON序列化工具(可选)
统一序列化格式,避免不同场景下JSON输出不一致:
public static class JsonUtils { private static readonly JsonSerializerOptions _serializerOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = false, IgnoreNullValues = true }; public static string Serialize(object obj) { return JsonSerializer.Serialize(obj, _serializerOptions); } }
2. 配置Serilog
确保使用RenderedCompactJsonFormatter输出到文件:
var builder = Host.CreateDefaultBuilder(args); builder.UseSerilog((context, config) => { config.WriteTo.File( formatter: new RenderedCompactJsonFormatter(), path: "logs/app-.json", rollingInterval: RollingInterval.Day, retainedFileCountLimit: 7 ); }); builder.ConfigureServices(services => services.AddHostedService<Worker>()); var host = builder.Build(); host.Run();
3. 日志记录的正确写法
通过LogContext添加结构化属性,同时将序列化后的JSON作为日志消息:
var logData = new { OrderId = 456, Customer = new { Name = "Alice", Email = "alice@example.com" }, Status = "Completed" }; // 序列化对象为JSON字符串 var logMessageJson = JsonUtils.Serialize(logData); // 用LogContext添加结构化属性,destructureObjects设为true确保对象被解构 using (LogContext.PushProperty("Message", logData, destructureObjects: true)) { _logger.LogInformation(logMessageJson); }
4. 封装扩展方法简化调用(可选)
为ILogger写扩展方法,避免重复代码:
public static class LoggerStructuredExtensions { public static void LogStructuredJson<T>(this ILogger logger, LogLevel level, T data) { var jsonMessage = JsonUtils.Serialize(data); using (LogContext.PushProperty("Message", data, destructureObjects: true)) { logger.Log(level, jsonMessage); } } // 快捷方法对应不同日志级别 public static void LogStructuredJsonInfo<T>(this ILogger logger, T data) { logger.LogStructuredJson(LogLevel.Information, data); } public static void LogStructuredJsonError<T>(this ILogger logger, T data) { logger.LogStructuredJson(LogLevel.Error, data); } }
调用时只需一行代码:
_logger.LogStructuredJsonInfo(logData);
输出效果验证
最终日志输出会同时满足需求:
@m字段:转义后的完整JSON字符串,可直接复制使用Message字段:解构后的层级化对象结构,便于日志分析工具解析
内容的提问来源于stack exchange,提问作者user1994100
相关产品推荐
相关产品推荐

