.NET6 API对接Datadog错误追踪属性解析/导出异常问题
问题:.NET6 API + OpenTelemetry + Datadog 异常属性转换错误
我有一个.NET6 API项目,使用OpenTelemetry(otel)进行埋点,追踪数据发送至Datadog,采用otel/opentelemetry-collector-contrib:latest镜像接收应用遥测数据后转发至Datadog。
配置了ExceptionMiddleware捕获所有异常并创建Span属性,同时参照Datadog官方文档配置Otel处理器,将exception属性转换为Datadog标准的error属性,但出现以下异常情况:
- exception.type被错误转换为error.message
- exception.message被忽略
- error.type丢失
仅exception.stacktrace正常转换为error.stack。即使直接在代码中设置error属性,也会出现完全相同的问题。
ExceptionMiddleware代码
public async Task InvokeAsync(HttpContext context) { string userId = GetUserId(context); try { await this._next(context); } catch (BusinessException bex) { LogWarning(bex); UpdateSpan(bex, userId); await this.HandleBadRequest(bex.Message, bex.StackTrace, context); } catch (Exception ex) { LogError(ex); string errorId = UpdateSpan(ex, userId); await this.HandleInternalServerError($"ErrorId: {errorId}", ex.StackTrace, context); } } private static string UpdateSpan(Exception ex, string userId) { Activity activity = Activity.Current; if (activity == null) return _defaultActivityId; string errorId = activity.Id.ToString(); activity?.SetStatus(ActivityStatusCode.Error, ex.Source); activity?.SetTag("exception.message", ex.Message); activity?.SetTag("exception.stacktrace", ex.StackTrace); activity?.SetTag("exception.type", ex.Source); return errorId; } private void LogWarning(BusinessException bex) { Dictionary<string, object> errorDict = BuildExceptionAtributes(bex); using (_logger.BeginScope(errorDict)) { _logger.LogWarning(bex.Message); }; } private void LogError(Exception ex) { Dictionary<string, object> errorDict = BuildExceptionAtributes(ex); using (_logger.BeginScope(errorDict)) { _logger.LogError(ex.Message); }; } private static Dictionary<string, object> BuildExceptionAtributes(Exception ex) { var errorDict = new Dictionary<string, object>() { // otel ["exception.message"] = ex.Message, ["exception.stacktrace"] = ex.StackTrace, ["exception.type"] = ex.Source, }; return errorDict; }
Otel处理器配置(config.yaml)
# This is a configuration file for the OpenTelemetry Collector intended to be # used in conjunction with the OTLP Exporter example (see ../TestOtlpExporter.cs) # # For more information about the OpenTelemetry Collector see: # https://github.com/open-telemetry/opentelemetry-collector # receivers: otlp: protocols: grpc: http: exporters: logging: verbosity: detailed datadog: api: key: some-key # site: processors: # https://github.com/open-telemetry/opentelemetry-collector/blob/main/processor/batchprocessor/README.md batch: attributes/insert: actions: - key: "error.message" action: "insert" from_attribute: "exception.message" attributes/insert2: actions: - key: "error.stack" action: "insert" from_attribute: "exception.stacktrace" attributes/insert3: actions: - key: "error.type" action: "insert" from_attribute: "exception.type" service: pipelines: traces: receivers: [otlp] processors: [batch, attributes/insert, attributes/insert2, attributes/insert3] exporters: [logging, datadog] metrics: receivers: [otlp] exporters: [logging, datadog] logs: receivers: [otlp] exporters: [logging, datadog]
直接设置error属性的代码
private static string UpdateSpan(Exception ex, string userId) { Activity activity = Activity.Current; if (activity == null) return _defaultActivityId; string errorId = activity.Id.ToString(); activity?.SetStatus(ActivityStatusCode.Error, ex.Source); activity?.SetTag("kinvo.user.id", userId); activity?.SetTag("exception.message", ex.Message); activity?.SetTag("exception.stacktrace", ex.StackTrace); activity?.SetTag("exception.type", ex.Source); // sending directly activity?.SetTag("error.message", ex.Message); activity?.SetTag("error.stack", ex.StackTrace); activity?.SetTag("error.type", ex.Source); return errorId; }
异常输出示例
{ "exception": { "message": "Object reference not set to an instance of an object.", "stacktrace": "at blablabla.GetConnection(Int64 userId, Nullable`1 version) in blablabla.cs:line 36", "type": "blablabla.WebApi" }, "error": { "message": "blablabla.WebApi", "stack": "at blablabla.GetConnection(Int64 userId, Nullable`1 version) in blablabla.cs:line 36", "type": "does-not-show" } }
解决方案
1. 修正Exception.type的赋值错误
当前代码中exception.type使用ex.Source(异常来源程序集名称),而非真正的异常类型。需替换为ex.GetType().FullName获取完全限定的异常类型名:
// UpdateSpan方法中修改 activity?.SetTag("exception.type", ex.GetType().FullName); // BuildExceptionAtributes方法中同步修改 ["exception.type"] = ex.GetType().FullName,
2. 移除手动属性转换,依赖Datadog自动映射
Datadog的Otel Exporter原生支持将OpenTelemetry的exception.*属性自动映射为Datadog标准error.*属性,手动配置的attributes处理器会与自动映射逻辑冲突,导致属性覆盖。简化配置如下:
processors: batch: service: pipelines: traces: receivers: [otlp] processors: [batch] exporters: [logging, datadog] metrics: receivers: [otlp] exporters: [logging, datadog] logs: receivers: [otlp] exporters: [logging, datadog]
3. 修正Span状态描述
将Span状态的描述信息从ex.Source改为ex.Message,确保错误信息准确:
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
内容的提问来源于stack exchange,提问作者Vitor Ribeiro
相关产品推荐
相关产品推荐

