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

.NET Core 8中Serilog结构化日志无法在Azure App Insights自定义维度显示

问题描述
  • 环境:.NET Core 8,已启用Serilog,通过OpenTelemetry将跟踪数据推送至Azure App Insights
  • 日志写法:使用Serilog结构化格式 Log.Information("test {@msg}", msg);
  • 问题:功能正常,但Azure App Insights的Custom Dimensions仅显示消息类型,看不到完整的结构化日志内容;手动序列化模型虽能解决,但违背结构化日志的设计初衷
当前配置代码

OpenTelemetry配置

services.AddOpenTelemetry()
            .ConfigureResource(builder => ConfigureResource(
                builder, 
                cfg.GetSection("Application:Name").Value ?? "", 
                cfg.GetSection("Application:Version").Value ?? ""))
            .WithTracing(x => x
                .AddSource(DiagnosticHeaders.DefaultListenerName)
                .AddAzureMonitorTraceExporter(o =>
                {
                    o.ConnectionString = cfg.GetSection("AzureServiceBus:ApplicationInsightsConnectionString").Value;
                }))
            .WithMetrics(x => x.AddMeter(InstrumentationOptions.MeterName)
                .AddAzureMonitorMetricExporter(o =>
                {
                    o.ConnectionString = cfg.GetSection("AzureServiceBus:ApplicationInsightsConnectionString").Value;
                }))
            .UseAzureMonitor();

Serilog配置

Log.Logger = new LoggerConfiguration()
                    .MinimumLevel.Information()
                    .WriteTo.Console()
                    .Enrich.WithProperty("ApplicationName", cfg["Application:Name"])
                    .WriteTo.ApplicationInsights(
                        cfg["AzureServiceBus:ApplicationInsightsConnectionString"],
                        new TraceTelemetryConverter())
                    .CreateLogger();
解决方案

问题根源在Serilog默认的TraceTelemetryConverter——它只会传递日志文本和基础属性,不会自动解析Serilog的结构化数据。下面是几种可行的解决方法:

方法1:替换Serilog自带的专用转换器

直接用Serilog.Sinks.ApplicationInsights提供的转换器替代默认的TraceTelemetryConverter,它会自动把结构化属性映射到App Insights的Custom Dimensions:

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.Console()
    .Enrich.WithProperty("ApplicationName", cfg["Application:Name"])
    .WriteTo.ApplicationInsights(
        cfg["AzureServiceBus:ApplicationInsightsConnectionString"],
        TelemetryConverter.Events) // 也可以用TelemetryConverter.Traces,按需选择
    .CreateLogger();
  • TelemetryConverter.Events:把日志作为自定义事件发送,结构化属性完整保留
  • TelemetryConverter.Traces:把日志作为跟踪信息发送,同样保留结构化属性

方法2:自定义Telemetry转换器(更灵活)

如果需要自定义属性映射逻辑,自己写一个转换器:

public class StructuredLogTelemetryConverter : TraceTelemetryConverter
{
    protected override void FillTelemetryProperties(LogEvent logEvent, TraceTelemetry telemetry)
    {
        // 保留原有基础属性
        base.FillTelemetryProperties(logEvent, telemetry);
        
        // 把所有结构化属性添加到Custom Dimensions
        foreach (var property in logEvent.Properties)
        {
            if (!telemetry.Properties.ContainsKey(property.Key))
            {
                // 用Serilog的JsonFormatter序列化复杂对象
                var valueFormatter = new JsonValueFormatter();
                var propertyValue = new StringWriter();
                valueFormatter.Format(property.Value, propertyValue);
                telemetry.Properties[property.Key] = propertyValue.ToString();
            }
        }
    }
}

然后在Serilog配置里使用这个自定义转换器:

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.Console()
    .Enrich.WithProperty("ApplicationName", cfg["Application:Name"])
    .WriteTo.ApplicationInsights(
        cfg["AzureServiceBus:ApplicationInsightsConnectionString"],
        new StructuredLogTelemetryConverter())
    .CreateLogger();

方法3:通过OpenTelemetry管道传递结构化日志

如果想让Serilog日志走OpenTelemetry管道,需要添加Serilog的OpenTelemetry接收器,并确保属性被正确传递:

// Serilog配置添加OpenTelemetry接收器
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.Console()
    .Enrich.WithProperty("ApplicationName", cfg["Application:Name"])
    .WriteTo.OpenTelemetry(options =>
    {
        options.ResourceAttributes = new Dictionary<string, object>
        {
            { "service.name", cfg["Application:Name"] },
            { "service.version", cfg["Application:Version"] }
        };
        // 确保包含结构化属性
        options.IncludeFormattedMessage = true;
        options.IncludeProperties = true;
    })
    .CreateLogger();

// 调整OpenTelemetry配置,集成Serilog
services.AddOpenTelemetry()
    .ConfigureResource(builder => ConfigureResource(
        builder, 
        cfg.GetSection("Application:Name").Value ?? "", 
        cfg.GetSection("Application:Version").Value ?? ""))
    .WithTracing(x => x
        .AddSource(DiagnosticHeaders.DefaultListenerName)
        .AddSerilog() // 加入Serilog跟踪集成
        .AddAzureMonitorTraceExporter(o =>
        {
            o.ConnectionString = cfg.GetSection("AzureServiceBus:ApplicationInsightsConnectionString").Value;
        }))
    .WithMetrics(x => x.AddMeter(InstrumentationOptions.MeterName)
        .AddAzureMonitorMetricExporter(o =>
        {
            o.ConnectionString = cfg.GetSection("AzureServiceBus:ApplicationInsightsConnectionString").Value;
        }))
    .UseAzureMonitor();
注意事项
  • 确保安装了最新版的Serilog.Sinks.ApplicationInsights、OpenTelemetry.Extensions.Hosting、Azure.Monitor.OpenTelemetry.Exporter NuGet包
  • 避免同时用Serilog的ApplicationInsights接收器和OpenTelemetry的Azure Monitor exporter,防止日志重复发送
  • 测试时先看Console输出确认结构化日志正常生成,再验证App Insights的Custom Dimensions

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 05:37:18