应使用 go.opentelemetry.io/otel,避免已归档的 opentracing-go 和 go-opencensus;初始化 TracerProvider 后必须显式调用 Shutdown() 防止 trace 丢失;导出器开发用 Jaeger(UDP 6831),生产用 OTLP;务必设置 service.name;HTTP 自动透传用 otelhttp.NewHandler;数据库需用 otelsql.RegisterDriver 全链路拦截;禁用高基数字段;异步 goroutine 必须传递上游 context。

新项目直接用 go.opentelemetry.io/otel,别碰 opentracing-go 或 go-opencensus——它们已归档或停止维护,上下文传播不可靠,跟现代服务网格(如 Istio)、Jaeger v1.50+、AWS X-Ray 都不兼容。
怎么初始化 TracerProvider 并确保数据不丢
必须显式调用 Shutdown(),否则进程退出时未 flush 的 trace 会丢失。常见漏写位置是 main() 函数末尾或 HTTP server 的 GracefulShutdown 钩子里。
- 导出器选型:本地开发用
jaeger(UDP 端口6831),生产推荐otlphttp或otlpgrpc配合 OpenTelemetry Collector - 采样器默认是
ParentBased(AlwaysSample),高流量服务建议配TraceIDRatioBased(0.01)避免打爆后端 -
resource.WithAttributes(semconv.ServiceNameKey.String("my-service"))必须设,否则 Jaeger UI 里看不到服务名 - 别在
init()里初始化 tracer——它依赖 context 和网络,应放在main()启动阶段
HTTP 请求怎么自动透传 traceparent 而不手写 header
用 otelhttp.NewHandler() 包裹 handler,它自动完成 inbound context extract + span create + outbound inject。自己手动 req.Header.Set("traceparent", ...) 会漏 tracestate,多 vendor 场景下链路断裂。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- net/http 直接用:
http.Handle("/api", otelhttp.NewHandler(http.HandlerFunc(handler), "api")) - Gin/Echo 不支持
otelhttp.NewHandler,得写中间件,且必须显式传propagation.TraceContext{},否则默认不解析traceparent - 若下游是 Java Spring Cloud,得换
propagation.B3{}并确保 header 是X-B3-TraceId,否则链路在跨语言边界断开 - 健康检查路径(如
/healthz)建议跳过埋点,避免污染 trace 数据,可用otelhttp.WithFilter()过滤
数据库查询为什么 span 总是断掉或 duration 不准
只 wrap sql.DB(比如用 otelsql.Open)不够——它覆盖不了 sql.Tx 内部的 Stmt.Exec、预编译语句、Rows.Scan 等底层操作,span 断裂或耗时统计失真。
立即学习“go语言免费学习笔记(深入)”;
- 正确做法是用
otelsql.RegisterDriver注册带 tracing 的 driver,让所有连接层调用被拦截 - PostgreSQL 推荐用
github.com/jackc/pgx/v5/pg驱动,MySQL 用github.com/go-sql-driver/mysql+otelsql注册 - 别给 span 加高基数字段(如用户 email、请求 body),会导致 trace 后端内存暴涨;动态值改用
span.AddEvent() - 错误处理要调
span.RecordError(err)+span.SetStatus(codes.Error, ...),否则 Jaeger UI 不标红
最易忽略的是异步 goroutine 中的 context 传递:切勿在新 goroutine 里用 context.Background() 起 span,必须从上游传入的 ctx 派生,否则父子关系全丢。还有就是 otel.Tracer("name") 可全局复用,但每个 span 的 End() 一定要 defer 或显式调用,漏了就看不到完整链路。

















