OpenTelemetry 是 CNCF 毕业项目,.NET 生态已全面转向;OpenTracing 已归档,ApplicationInsights 不兼容 OTLP,跨语言对接困难。

为什么直接用 OpenTelemetry.Sdk 而不是 OpenTracing 或旧版 ApplicationInsights
因为 OpenTelemetry 是当前 CNCF 毕业项目,.NET 生态已全面转向它;OpenTracing 已归档,ApplicationInsights 的 SDK 虽仍可用,但不兼容标准 OTLP 协议,跨语言对接会出问题。微服务场景下,必须统一用 OTLP 导出器才能和 Go/Java 服务共用 Jaeger 或 Tempo。
如何在 ASP.NET Core 服务中注入并配置 TracerProvider
核心是注册 TracerProvider 并启用自动仪表化(auto-instrumentation),同时指定 OTLP 导出目标。别漏掉 AddAspNetCoreInstrumentation() 和 AddHttpClientInstrumentation() —— 否则 HTTP 入口和出站调用不会生成 span。
using OpenTelemetry;
using OpenTelemetry.Exporter;
using OpenTelemetry.Instrumentation.AspNetCore;
using OpenTelemetry.Instrumentation.Http;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOpenTelemetry()
.WithTracing(tracerProviderBuilder =>
{
tracerProviderBuilder
.AddAspNetCoreInstrumentation() // 捕获 Controller 请求
.AddHttpClientInstrumentation() // 捕获 HttpClient 调用
.AddSource("MyService"); // 手动创建 span 时用的名称
if (builder.Environment.IsDevelopment())
{
tracerProviderBuilder.AddConsoleExporter();
}
else
{
tracerProviderBuilder.AddOtlpExporter(opt =>
{
opt.Endpoint = new Uri("http://otel-collector:4317"); // 注意:gRPC 端口是 4317,HTTP 是 4318
});
}
});
var app = builder.Build();
app.UseRouting();
app.UseEndpoints(endpoints => endpoints.MapControllers());
app.Run();
-
OTLP gRPC endpoint必须用http://协议(即使走 gRPC),.NET SDK 不支持https://自签名证书直连,生产环境需配 TLS 或改用 HTTP/JSON 导出器 - 本地调试时优先用
AddConsoleExporter(),避免依赖外部 collector;输出含 trace-id、span-id、parent-id,可快速验证链路是否串联 -
AddSource("MyService")是手动埋点的入口名,后续用TracerProvider.Default.GetTracer("MyService")获取 tracer
怎么让跨服务的 HTTP 调用自动传递 trace context
.NET 的 HttpClient 自动注入 traceparent header,但前提是:你用的是 IHttpClientFactory 创建的 client,且没手动 new HttpClient。手动 new 会绕过 instrumentation,导致下游收不到 context。
- ✅ 正确方式:在
Program.cs注册命名 client:builder.Services.AddHttpClient<IOrderClient, OrderClient>(); - ❌ 错误方式:
var client = new HttpClient();—— 这个 client 完全不参与 OpenTelemetry 链路 - 如果必须用自定义
HttpClient,得手动注入 context:activity?.SetTag("http.url", url);,但无法自动关联 parent-span - 检查 trace 是否断开:看下游服务 log 中
traceparentheader 是否存在,值格式应为00-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx-01
常见断链原因和验证手段
分布式链路“看起来有 trace-id,但 span 不连”是最常被忽略的问题。本质是 context 未正确传播或采样器丢弃了 span。
- 默认采样器是
ParentBasedSampler,但如果上游没传 context,它会 fallback 到NeverSample—— 导致本服务第一个 span 就被丢弃。临时调试可强制用AlwaysOnSampler:.SetSampler(new AlwaysOnSampler()) - 检查
Activity.Current是否为 null:在 Controller action 开头加Console.WriteLine(Activity.Current?.Id),若为 null,说明 instrumentation 未生效或中间件顺序错乱 - ASP.NET Core 中间件顺序很重要:
UseRouting()必须在UseOpenTelemetryPrometheusScrapingEndpoint()之前,否则路由未匹配就提前暴露指标端点 - 跨容器通信时,确保服务间 DNS 可解析(比如
otel-collector是 Docker Compose service 名),别用localhost—— 容器内 localhost 指自己
链路追踪真正难的不是埋点,而是确认每个 hop 的 context 都没被意外截断或覆盖。建议上线前用一个简单请求跑通三跳链路(A → B → C),再逐个服务查 traceparent header 和 span duration 是否合理。










