应立即切换到 go.opentelemetry.io/otel,因 opentracing-go 已归档、不维护、上下文传播不可靠,且与现代可观测性生态不兼容;替换只需三步:删旧 import、注册 OTel SDK、改用 otelhttp 封装 handler 和 client。

opentracing-go 已归档,不建议在新项目中使用。它不再维护,上下文传播不可靠,与现代服务网格(如 Istio)、Jaeger v1.50+、AWS X-Ray 及 OpenTelemetry Collector 均不兼容。你现在要做的不是“如何用 opentracing-go”,而是**立刻切换到 go.opentelemetry.io/otel**。
为什么 opentracing-go 会静默失效
调用 opentracing.StartSpanFromContext 不报错,但 span 实际为 nil —— 因为 opentracing-go 本身不提供实现,只定义接口;你必须手动注入具体 tracer(如 Jaeger),而 Go 的 init 执行顺序不确定,极易导致 tracer 尚未 ready,HTTP handler 就已启动。更严重的是:它不强制校验上下文传播完整性,header 注入/提取配对错误(比如发送方用 HTTPHeaders,接收方却用 text_map)只会让链路断成孤岛,日志里连 warning 都没有。
替换 opentracing-go 的三步实操
只需改三处,就能把旧代码迁移到 OpenTelemetry:
- 删掉所有
github.com/opentracing/opentracing-go和厂商包(如github.com/uber/jaeger-client-go)的 import - 在
main()开头注册sdktrace.NewTracerProvider,并立即调用otel.SetTracerProvider(tp)和otel.SetTextMapPropagator(propagation.TraceContext{}) - HTTP handler 改用
otelhttp.NewHandler(yourHandler, "route-name")包裹,出向请求改用&http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)}
旧代码里类似 span, ctx := opentracing.StartSpanFromContext(ctx, "db.query") 的调用,直接换成 span := tracer.Start(ctx, "db.query") 即可,无需改逻辑结构。
HTTP 中间件里最容易漏掉的两个点
即使用了 otelhttp.NewHandler,链路仍可能断裂:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
立即学习“go语言免费学习笔记(深入)”;
- 自定义中间件里起 child span 时,必须从
r.Context()取 ctx,不能用context.Background()—— 后者会丢失 trace_id 和 parent_span_id - Gin/Echo 等框架用户,不能直接
gin.Use(otelhttp.NewHandler(...)),必须用对应生态的封装(如ginotel.Middleware),否则路由参数(如:id)会被抹成/,span name 失真
数据库 span 断裂的真实原因
只 wrap sql.DB(例如用 otelsql.Open)不够。它覆盖不了 sql.Tx 内部的 Stmt.Exec、预编译语句、甚至 Rows.Scan 阶段 —— 这些操作会脱离 span 生命周期,造成 duration 统计失真或 span 直接中断。
正确做法是:
- PostgreSQL:用
github.com/jackc/pgx/v5/pgxpool+otelsql.RegisterDriver("pgx", pgxpool.Driver()) - MySQL:用
github.com/go-sql-driver/mysql+otelsql.RegisterDriver("mysql", &mysql.MySQLDriver{}) - 务必避免给 span 加高基数字段(如用户 email、完整 request body),改用
span.AddEvent()记录动态事件
最常被忽略的是异步 goroutine 场景:哪怕只多开一个 go func() { ... }(),也必须显式传入带 span 的 ctx,且用 trace.ContextWithSpan(ctx, span) 注入,而不是 context.WithValue —— 后者无法被 OpenTelemetry 识别。

















