.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.ExporterNuGet包 - 避免同时用Serilog的ApplicationInsights接收器和OpenTelemetry的Azure Monitor exporter,防止日志重复发送
- 测试时先看Console输出确认结构化日志正常生成,再验证App Insights的Custom Dimensions
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

