必须显式配置采样器(如sdktrace.ParentBased(sdktrace.TraceIDRatioBased(0.01)))和传播器(otel.SetTextMapPropagator),否则默认不生成span且上下文无法透传,导致链路在首跳断裂。

直接用 go.opentelemetry.io/otel 初始化 TracerProvider 并配好采样器和导出器,就能跑通分布式追踪;不配采样器、不设传播器、不包裹 HTTP handler,90% 的链路会断在第一跳。
为什么 otelhttp.NewHandler 包裹后还是没 server span?
根本不是中间件“没生效”,而是 handler 没被真正注册进服务路由。常见错误包括:
- 写了
http.Handle("/api", myHandler),但没换成http.Handle("/api", otelhttp.NewHandler(myHandler, "api")) - 用 Gin 时传了原始
http.HandlerFunc,没走gin.WrapH(otelhttp.NewHandler(...)) -
otelhttp.NewHandler第二个参数(span name)为空字符串,部分导出器(如 Jaeger)会直接丢弃该 span - handler 内部 panic 或提前 return,导致
defer span.End()没执行,span 被静默丢弃
采样器配置错或漏掉,trace 就全空
oteltrace.Tracer 默认行为是 sdktrace.NeverSample(),不是“低采样”,是彻底不生成任何 span。现象就是 Jaeger / Tempo 里完全看不到数据,日志也没报错。
- 必须在
sdktrace.NewTracerProvider()中显式传入sdktrace.WithSampler(...) - 生产环境别用
sdktrace.AlwaysSample()——QPS 上千时 exporter 会积压、重试风暴、拖垮网络 - 必配
sdktrace.ParentBased(sdktrace.TraceIDRatioBased(0.01)):否则上游不采样,下游即使想采也收不到traceparent,整条链路消失 - 采样率写死在代码里不灵活,建议用环境变量读取 + 校验(比如 >1 则设为 0.001),再包一层可变
Sampler配合信号重载
goroutine 和数据库调用里 trace 断开的真相
Go 的 context.Context 不跨 goroutine 自动传递,DB 驱动也不读 context 里的 span —— 这是 Go 分布式追踪最常被忽略的底层事实。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 启动新 goroutine 时,必须显式传入带 span 的 context:
go doWork(ctx),并在函数内用trace.SpanFromContext(ctx)获取 parent - 对
time.AfterFunc、sync.WaitGroup等异步逻辑,同样要传ctx,并用context.WithTimeout控制生命周期 - DB 查询必须用
db.QueryContext(ctx, ...),不能用db.Query(...);GORM 要调WithContext(ctx) -
context.WithValue在跨进程场景无效,HTTP 出站必须靠propagator.Inject(ctx, carrier)写头,不能靠塞值
导出器选 OTLP 还是 Jaeger?关键看阶段
本地开发用 Jaeger agent 最快,但生产直连 OTLP endpoint 是硬性要求——不是“推荐”,是精度、协议、维护性三重倒逼的结果。
- Jaeger 的 thrift 协议已弃用,新版只支持 OTLP;旧版
uber-trace-id头不兼容 W3C 标准,跨语言对接易出错 - Zipkin 时间精度仅毫秒级,OpenTelemetry 默认纳秒级,转换时丢精度,且不支持 baggage
- 开发阶段:用
jaeger.NewExporter(jaeger.WithAgentEndpoint()),配合docker run -p 6831:6831 jaegertracing/all-in-one - 生产阶段:用
otlphttp.NewExporter(otlphttp.WithEndpoint("https://your-otlp-endpoint/v1/traces")),必须配 TLS 和认证(截至 2026 年 4 月)
最常被忽略的是:采样器初始化和传播器设置的顺序。先设 otel.SetTextMapPropagator,再建 TracerProvider,否则 Extract/Inject 用的是默认 noop propagator,header 透传就失效了——链路不是慢,是根本没连上。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










