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

.NET 7控制台应用ILogger/Serilog JSON日志输出异常求助

如何配置.NET 7控制台应用输出合理的JSON格式日志

一、使用原生Microsoft ILogger组件的解决方案

1. 配置JSON日志输出

首先需要启用原生的JSON控制台格式化器,在Program.cs中配置日志:

var builder = Host.CreateDefaultBuilder(args);
builder.ConfigureLogging(logging =>
{
    // 清除默认日志提供器
    logging.ClearProviders();
    // 添加JSON格式的控制台日志
    logging.AddConsole(options =>
    {
        options.FormatterName = "json";
    }).AddJsonConsole(options =>
    {
        // 启用Scope支持
        options.IncludeScopes = true;
        // 容器环境建议使用紧凑的JSON格式
        options.JsonWriterOptions = new System.Text.Json.JsonWriterOptions
        {
            Indented = false,
            SkipValidation = true
        };
        // 配置时间戳格式
        options.FormatterOptions = new JsonConsoleFormatterOptions
        {
            TimestampFormat = "yyyy-MM-ddTHH:mm:ss.fffZ",
            UseUtcTimestamp = true
        };
    });
});

var host = builder.Build();
// 后续业务逻辑
host.Run();

2. 正确传递State和Scope参数

直接传递KeyValuePair<string, object>[]数组时,日志格式化器会调用数组的ToString()方法,而非解析为键值对。改用Dictionary<string, object>或匿名类型即可解决:

修复State传递示例

// 用Dictionary替代数组
var state = new Dictionary<string, object>
{
    {"state-key-1", "state-value-1"},
    {"state-key-2", 2}
};

_logger.Log(LogLevel.Error, new EventId(12, "twelve"), state, null, (state, ex) => 
    "This message came from _logger.Log(...); it has no formatted values.");

修复Scope传递示例

// 用Dictionary替代数组
var sprops = new Dictionary<string, object>
{
    {"scope-key-1", "scope-value-1"},
    {"scope-key-2", 9}
};

using (var scope = _logger.BeginScope(sprops))
{
    var lm = LoggerMessage.Define<string>(LogLevel.Error, new EventId(11, "eleven"), 
        "This message came from LoggerMessage.Define; the formatted value is: {formatted_value}");
    lm(_logger, "my-formatted-value", null);
}

3. 扩展LoggerMessage的结构化字段

如果需要在LoggerMessage.Define中添加额外固定字段,可以扩展泛型参数:

// 定义包含多个固定字段的日志模板
var lm = LoggerMessage.Define<string, int>(LogLevel.Error, new EventId(11, "eleven"), 
    "This message has formatted values: {formatted_value}, {additional_value}");

// 调用时传递对应值
lm(_logger, "my-formatted-value", 100, null);

若需动态添加任意字段,结合BeginScope传递Dictionary即可。

二、使用Serilog的解决方案

1. 安装必要的NuGet包

Install-Package Serilog.AspNetCore
Install-Package Serilog.Sinks.Console
Install-Package Serilog.Formatting.Compact

2. 配置Serilog日志

在Program.cs中替换原生日志配置为Serilog:

using Serilog;

var builder = Host.CreateDefaultBuilder(args);
builder.UseSerilog((context, config) =>
{
    config
        .MinimumLevel.Information()
        .Enrich.FromLogContext() // 启用Scope上下文支持
        // 使用RenderedCompactJsonFormatter确保消息模板和渲染消息正确输出
        .WriteTo.Console(new Serilog.Formatting.Compact.RenderedCompactJsonFormatter());
});

var host = builder.Build();
// 后续业务逻辑
host.Run();

3. 正确使用日志API

修复直接Log方法的消息输出问题

避免传递KeyValuePair数组,改用匿名类型或Dictionary传递状态:

// 方式1:用匿名类型传递额外状态,消息单独指定
var state = new { state_key_1 = "state-value-1", state_key_2 = 2 };
_logger.LogError("This message came from _logger.Log(...); it has no formatted values.", state);

// 方式2:在消息模板中直接定义结构化字段
_logger.LogError("This message has structured values: {state_key_1}, {state_key_2}", 
    "state-value-1", 2);

正确使用Scope传递上下文

using (_logger.BeginScope(new { scope_key_1 = "scope-value-1", scope_key_2 = 9 }))
{
    _logger.LogError("This message is wrapped in a scope");
}

关键注意事项

  • 避免使用KeyValuePair数组:无论是原生ILogger还是Serilog,数组类型的State/Scope都会被当作单个值处理,导致ToString()结果输出,而非解析为键值对。优先使用Dictionary<string, object>或匿名类型。
  • 容器环境优化:JSON日志建议使用紧凑格式(关闭缩进),减少日志体积,同时启用UTC时间戳保证跨环境时间一致性。
  • Serilog格式化器选择:使用RenderedCompactJsonFormatter可以确保RenderedMessage和MessageTemplate字段正确,避免消息被错误放入Properties中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 17:55:03