必须在main()开头注册tracerprovider并调用otel.settracerprovider和otel.settextmappropagator,否则otel.tracer()返回noop tracer导致trace_id缺失、jaeger无数据、span全为零值——这是go集成opentelemetry最常见静默失效原因。

otel.Tracer() 调用后没 trace_id?不是代码写错了,是根本没生效——90% 的 Go 项目卡在这一步:没注册 TracerProvider。
不注册 TracerProvider 就调 otel.Tracer(),一定返回 noop
现象:程序能跑、无 panic、日志干净,但 Jaeger / APM 里空空如也,span.SpanContext().TraceID() 全是零值。
原因:otel.Tracer("xxx") 底层依赖全局 otel.GetTracerProvider(),而默认实现是 noop——它不报错,也不发数据,纯静默丢弃。
- 必须在
main()最开头执行:先sdktrace.NewTracerProvider(...),紧接着otel.SetTracerProvider(tp) - 别放
init():Go 的 init 执行顺序不可控,HTTP server 可能比 provider 先启动 - 别在 handler 或 goroutine 里 new:会导致多个 provider 冲突、采样逻辑错乱、exporter 连接泄漏
- 注册后还得配传播器:
otel.SetTextMapPropagator(propagation.TraceContext{}),否则跨服务 context 传不下去
HTTP 入口必须用 otelhttp.NewHandler,不能手写 traceparent 解析
手动从 req.Header.Get("traceparent") 提取再 tracer.Start(),看着能 work,实则埋下三处断裂点:
- 漏关键语义字段:
http.status_code、net.peer.ip、http.route全不记录 - span duration 不准:WriteHeader 后不会自动结束 span,耗时被拉长或截断
- goroutine 断链:handler 里起
go func() { ... },子 goroutine 拿不到 parent context,trace_id 归零
正确姿势:
- 对标准
http.Handler:直接http.ListenAndServe(":8080", otelhttp.NewHandler(mux, "my-service")) - 对 Gin:用
ginotel.Middleware,否则 span name 会是GET /而非真实路由 - 对 Echo:用
echootel.Middleware,同理
数据库、Redis、gRPC 埋点必须用 instrumented client
在 db.Query() 前后手动 span.Start()/End() 是典型误区——它只覆盖业务代码执行时间,完全不包含连接池等待、驱动解析、网络往返等真实瓶颈。
- SQL:用
go.opentelemetry.io/contrib/instrumentation/database/sql,注册时调sql.OpenDB(otelsql.InjectDriver("mysql", driver)),不是sql.Open() - MySQL 驱动限定为
github.com/go-sql-driver/mysql;modernc.org/sqlite等纯 Go 驱动不兼容 hook 点 - Redis:用
go.opentelemetry.io/contrib/instrumentation/github.com/go-redis/redis/v9,原生redis.Client不埋点 - gRPC:用
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc,client/server 都要 wrap
导出配置必须匹配实际部署环境
本地能连 Jaeger,上生产却看不到 trace?大概率是 endpoint 或鉴权方式不匹配。
- 腾讯云 APM:内网走 VPC 直连,endpoint 是
otlp://apm.tencentcloudapi.com:4317;外网需加Authenticationheader - 本地调试 Jaeger:用
go.opentelemetry.io/otel/exporters/jaeger,endpoint 设为http://localhost:14268/api/traces - OTLP gRPC:确认 collector 是否监听
:4317,防火墙是否放行,TLS 是否开启(若启用了,exporter 要配otlptracegrpc.WithTLSCredentials()) - 采样器别全开:生产环境慎用
trace.AlwaysSample(),推荐trace.ParentBased(trace.TraceIDRatioBased(0.01))控制上报量
最易被忽略的细节:Tracer 名称不能写死 "default",HTTP handler 和 DB client 必须用不同名称(如 "user-api" 和 "redis-client"),否则 span 属性互相覆盖、采样策略失效、后端存储爆炸式膨胀。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











