如何配置.NET OpenTelemetry SDK异常遥测适配OTel Collector与Azure Monitor
问题本质
当使用logger.LogError(ex, "消息")记录无关联Activity的异常时,OpenTelemetry将其作为LogRecord发送到Collector,但Azure Monitor exporter默认将这类LogRecord归入traces表,而非预期的exceptions表。而通过Activity.RecordException记录的异常属于Trace Span的一部分,会被正确映射到exceptions表。
方案一:.NET代码层面调整
通过配置OpenTelemetry日志导出,确保异常日志携带Azure Monitor识别所需的属性,强制将异常日志标记为异常事件。
修改.NET日志配置
在AddOpenTelemetry的日志选项中,显式启用异常包含,并添加增强逻辑确保异常属性被正确序列化:
services.AddLogging(loggingBuilder => { loggingBuilder.AddOpenTelemetry(options => { options.IncludeFormattedMessage = true; options.IncludeScopes = true; options.IncludeException = true; // 显式启用异常包含,默认值为true,显式设置更稳妥 options.SetResourceBuilder(resourceBuilder); // 增强日志记录,添加Azure Monitor识别异常所需的属性 options.EnrichWithException((logRecord, exception) => { logRecord.AddAttribute("ai.event.type", "Exception"); logRecord.AddAttribute("exception.type", exception.GetType().FullName); logRecord.AddAttribute("exception.message", exception.Message); logRecord.AddAttribute("exception.stacktrace", exception.ToString()); }); otelConfig.AssignExporter(exporter => exporter.Otlp, otlpExporter => { options.AddOtlpExporter(otlpOptions => { otlpOptions.Protocol = otelConfig.GetProtocol(); otlpOptions.Endpoint = new Uri(otelConfig.Exporters.Otlp.Endpoint); }); }); }); });
此配置会让.NET生成的LogRecord携带ai.event.type = Exception属性,Azure Monitor exporter识别后会将其归入exceptions表。
方案二:OTel Collector层面调整
如果无法修改.NET代码(如遗留代码过多),可在Collector中添加transform处理器,将包含异常属性的LogRecord转换为Azure Monitor可识别的异常事件。
步骤1:添加transform处理器
在Collector配置的processors块中新增transform处理器:
processors: # 保留原有attributes和batch处理器 attributes: actions: - key: http.status_code action: insert from_attribute: http.response.status_code - key: http.url action: insert from_attribute: url.full - key: http.target action: insert from_attribute: url.path - key: http.host action: insert from_attribute: server.address - key: http.scheme action: insert from_attribute: url.scheme - key: http.method action: insert from_attribute: http.request.method batch: # 新增transform处理器 transform: logs: contexts: - context: log statements: # 识别包含异常属性的日志,标记为异常事件 - set(attributes["ai.event.type"], "Exception") where attributes["exception.type"] != nil # 映射异常属性到Azure Monitor所需字段 - set(attributes["ai.exception.type"], attributes["exception.type"]) where attributes["exception.type"] != nil - set(attributes["ai.exception.message"], attributes["exception.message"]) where attributes["exception.message"] != nil - set(attributes["ai.exception.stacktrace"], attributes["exception.stacktrace"]) where attributes["exception.stacktrace"] != nil
步骤2:更新logs pipeline
在service.pipelines.logs中加入transform处理器,顺序放在attributes之后、batch之前:
service: pipelines: traces: receivers: [otlp] processors: [batch,attributes] exporters: [file,azuremonitor] logs: receivers: [otlp] processors: [attributes,transform,batch] # 加入transform处理器 exporters: [file,azuremonitor]
此配置会在Collector端自动识别携带exception.type的LogRecord,添加Azure Monitor所需的异常标记属性,从而将其归入exceptions表。
验证方法
- 重新部署.NET应用和OTel Collector
- 触发无Activity的异常日志记录
- 查看Azure Monitor的
exceptions表,确认异常是否正确归入
内容的提问来源于stack exchange,提问作者Samuel Sagan

