C# OpenTelemetry集成方法 C#如何实现分布式追踪

2次阅读

opentelemetry sdk 必须在 webapplicationbuilder 阶段初始化,显式设置 service.name,正确配置 otlp 协议(grpc 或 http/json),手动创建 span 时需通过 propagator 提取父上下文,httpclient 需启用 enablehttpheadersinjection 才能传递 trace 上下文。

C# OpenTelemetry集成方法 C#如何实现分布式追踪

OpenTelemetry SDK 初始化必须在应用启动早期完成

如果在 Program.cs 中延迟注册或依赖服务已构建后再配置 OpenTelemetry,会导致部分 HTTP 请求、数据库调用等自动检测(auto-instrumentation)丢失 span。ASP.NET Core 6+ 推荐在 WebApplicationBuilder 阶段就添加 tracing 服务。

实操建议:

  • 使用 AddOpenTelemetryTracing 扩展方法,而非手动 new TracerProvider,避免生命周期管理错误
  • 必须调用 SetResourceBuilder 显式设置 service.name,否则后端(如 Jaeger、OTLP Collector)无法按服务归类数据
  • 启用 ActivitySource.Create 的自定义追踪前,确认未被 AspNetCoreInstrumentationHttpClientInstrumentation 覆盖

示例关键片段:

builder.Services.AddOpenTelemetryTracing(tracerProviderBuilder => {     tracerProviderBuilder         .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("my-api"))         .AddAspNetCoreInstrumentation()         .AddHttpClientInstrumentation()         .AddOtlpExporter(opt => opt.Endpoint = new Uri("http://localhost:4317")); });

OTLP 协议导出需注意 gRPC vs HTTP/JSON 传输差异

默认 OtlpExporter 使用 gRPC(http://localhost:4317),但若后端只支持 HTTP/JSON(如某些旧版 Collector 配置),会静默失败——没有异常,但 spans 不出现。

常见错误现象:

  • 日志里反复出现 ExportProcessor.OnExportAsync: Export failed,但无
  • wireshark 抓包发现客户端尝试 gRPC 连接,而服务端只监听 :4318

解决方式:

  • 改用 HTTP/JSON 导出:设置 Protocol = OtlpExportProtocol.HttpProtobuf,endpoint 改为 http://localhost:4318/v1/traces
  • 确保 Collector 配置中同时启用了 otlp/<code> 接收器,并区分 <code>grpc_endpointhttp_endpoint
  • 开发期可临时启用 ConsoleExporter 验证 trace 是否生成成功,绕过网络环节

手动创建 Span 时必须显式链接父上下文

在非 HTTP 入口(如后台队列消费、TimerCallback、gRPC ServerStreaming)中,Activity.Current 通常为 NULL,直接 StartActivity 会产生孤立 span,断开调用链。

正确做法是通过 Propagators.Extract 从消息头(如 rabbitmq headers、kafka headers)还原上下文:

  • 使用 B3PropagatorTraceContextPropagator,取决于上下游系统约定的传播格式
  • 提取后调用 ActivitySource.StartActivity(..., parentContext: extractedContext)
  • 避免用字符串拼接 traceId/spanId 手动构造 ActivityContext,易出格式错误(如缺失 trace-flags)

示例(从字典提取 B3 头):

var b3Propagator = new B3Propagator(); var extracted = b3Propagator.Extract(B3Propagator.ParseHeaders, headers, (h, k) => h.GetValueOrDefault(k)); using var activity = MyActivitySource.StartActivity("process-order", ActivityKind.Consumer, extracted.Context);

HttpClient 默认不传递 trace 上下文到外部服务

即使启用了 AddHttpClientInstrumentation(),.NET 默认仍不会将当前 trace context 注入 outgoing request headers,除非显式启用传播。

原因在于:.NET 的 HttpClient instrumentation 默认关闭了 Enrich 和 header 注入逻辑,仅采集本地耗时。

修复方式:

  • AddHttpClientInstrumentation 中启用 EnableHttpHeadersInjection = true
  • 确保目标服务也支持对应传播协议(如 W3C TraceContext),否则收到的 traceparent 可能被忽略
  • 若调用的是遗留 Java 服务,可能需降级为 B3 格式,在 HttpClientInstrumentationOptions 中指定 Propagator = new B3Propagator()

这个点最容易被忽略——你看到自己的服务有 trace,但下游没关联,大概率卡在这里。

text=ZqhQzanResources