新项目必须用 opentelemetry go sdk + jaegerexporter,禁用已归档的 jaeger-client-go;否则 span 丢失、context 断开、goroutine 无 parent,jaeger ui 显示空白且无报错。

jaeger-client-go 已归档,新项目必须用 OpenTelemetry Go SDK + jaegerexporter,否则链路必然断裂——不是“跑不起来”,而是“看似有 trace,实际 span 丢失、context 断开、goroutine 中无 parent”。
别再用 jaeger-client-go 初始化 tracer
它自 2024 年起已归档,不再维护。继续用会导致:span.Context() 在 goroutine 中返回空、otel.GetTextMapPropagator().Extract() 失效、采样策略不生效、HTTP/gRPC 自动埋点完全不可用。
- 错误做法:
jaeger.NewTracer()+opentracing.SetGlobalTracer() - 正确路径:用
go.opentelemetry.io/otel和go.opentelemetry.io/otel/exporters/jaeger - 关键检查点:
otel.GetTracerProvider()在main()开头调用后必须非nil,否则所有Tracer().Start()都退化为 noop
TracerProvider 必须在 main() 最早几行初始化
OpenTelemetry 的 TracerProvider 是全局单例,延迟初始化等于全程失效。常见现象是 Jaeger UI 里 trace 完全空白,但日志也没报错——因为没 provider,span 就不创建。
- 必须紧随配置加载之后:
flag.Parse()或viper.ReadInConfig()完成后立刻调用initTracer() -
otel.SetTracerProvider(tp)不能塞在某个 handler 或 init 函数里 - 本地验证:加一行
log.Printf("tracer provider: %v", otel.GetTracerProvider()),确保输出不是<nil></nil>
Gin/gRPC 入口必须用标准中间件,别手写 Extract/Inject
手动处理 traceparent 头极易出错:大小写错、字段漏、carrier 类型不对,Jaeger 就静默丢弃整个 span。标准中间件自动对齐 W3C Trace Context 规范,且绑定 context 到 request。
- Gin:用
otelgin.Middleware(),它调用otel.GetTextMapPropagator().Extract()并注入到req.Context() - gRPC server:注册
otelgrpc.UnaryServerInterceptor,否则上游traceparent根本不被识别 - gRPC client:必须套
otelgrpc.UnaryClientInterceptor,否则下游收不到任何 trace 上下文 - HTTP client:禁用
http.DefaultClient,改用otelhttp.NewClient()实例
跨 goroutine 和 DB 调用必须显式透传带 span 的 context
Go 不自动继承 context,90% 的“链路只有一跳”问题都源于这里:goroutine 启动时传了 context.Background(),或 DB 查询没 wrap,导致子 span 变成孤立 root。
- 异步任务:
go doWork(ctx)中的ctx必须是 handler 里拿到的req.Context(),不是context.Background() - MySQL:用
otelmysql.Wrap(db)包装*sql.DB,否则db.Query()不计入 span - PostgreSQL:同理,用
otelpostgresql.Wrap() - 手动 StartSpan:
span := tracer.Start(ctx, "db.query"),ctx必须来自上游 request,不能是context.Background()
go func() { ... }() 没改,整条链路就断在那个 goroutine 里——UI 看不到错误,也看不到数据,只有你心里知道它断了。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











