jaeger tracer初始化必须在main()开头完成,且servicename和reporter配置不可省略;否则tracerprovider为nil,后续所有span调用均静默失效,ui显示空白。

tracer 初始化必须在 main() 最开头,且不能省略 ServiceName 和 reporter 配置
Jaeger trace 数据不上报、UI 里完全空白,90% 是因为 TracerProvider 没初始化成功,或者初始化太晚。OpenTelemetry 的全局 tracer provider 是单例,一旦错过初始化时机,后续所有 Tracer().Start() 都退化为 noop,不报错也不上报。
必须在 main() 函数最开始几行就调用 initTracer(),紧随配置加载(如 viper.ReadInConfig() 或 flag.Parse())之后。别塞进 init()、中间件或 handler 里。
-
ServiceName必须非空且合法:不能含空格、特殊字符,大小写敏感;填错会导致 Collector 静默拒收,UI 中搜不到服务 - 本地开发用 UDP reporter 时,
jaeger.WithAgentHost("localhost")+jaeger.WithAgentPort("6831")是默认组合,但 Docker/K8s 环境下要改成服务名,比如"jaeger-agent:6831"或"host.docker.internal:6831" - 生产环境建议换 HTTP reporter:
jaeger.NewHTTPReporter("http://jaeger-collector:14268/api/traces"),失败会明确报错,比 UDP 静默丢包好排查 - 加一行
log.Printf("tracer provider: %v", otel.GetTracerProvider()),确认输出不是<nil></nil>
Gin 中间件必须用 otelgin.Middleware(),不能手写 Extract/Inject
手动解析 uber-trace-id header 容易出错:大小写错、漏 tracestate、carrier 类型不对,Jaeger 就静默丢弃整个 span。标准中间件自动对齐 W3C Trace Context 规范,且完成 context 绑定。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
正确做法是直接注册 otelgin.Middleware("my-service"),它内部调用 otel.GetTextMapPropagator().Extract() 并把 span 注入到 req.Context(),下游业务代码才能通过 req.Context() 拿到 parent span。
- 别只调
tracer.StartSpan("http-server")就完事——没绑定到 request context,子 span 全部孤立 - 如果用了自定义中间件,必须显式调用
req = req.WithContext(opentracing.ContextWithSpan(req.Context(), span))(旧版 opentracing)或req = req.WithContext(otel.ContextWithSpan(req.Context(), span))(OTel) - 别在
defer span.Finish()前修改w.Header()或写 response body,某些中间件(如 CORS)会提前关闭 writer 导致 panic
下游调用必须用封装过的 client,裸调用必然断链
HTTP、gRPC、DB、MQ 这些跨协议/跨组件调用,Go 不自动继承 context,漏传 req.Context() 就等于主动切断链路。Jaeger UI 里“只有一跳”的问题,绝大多数源于这里。
- HTTP client 必须用
otelhttp.NewClient(http.DefaultClient)实例,禁用裸http.DefaultClient—— 它不会 injecttraceparent头 - gRPC client 必须套
otelgrpc.UnaryClientInterceptor,server 端注册otelgrpc.UnaryServerInterceptor,否则 metadata 里根本没 trace 信息 - MySQL 要用
otelmysql.Wrap(db),PostgreSQL 用otelpostgresql.Wrap(db);裸*sql.DB查询不参与 span 生命周期 - 启动 goroutine 时,传
req.Context(),不是context.Background():写成go doWork(ctx),而不是go doWork(context.Background())
跨进程调用链断裂,大概率是网络或端口没对齐
本地跑通、部署后 spans 全丢,几乎可以确定是网络层问题。UDP 协议不建连、不重试、不报错,填错地址就彻底消失,日志里连 warning 都没有。
- Kubernetes 中 Jaeger Agent 默认监听
0.0.0.0:6831/UDP,但 Pod 网络策略或 Service 配置常阻断 UDP 流量;确认 Service 类型为ClusterIP,且port明确声明protocol: UDP - Docker Compose 环境下,Go 服务里的 reporter 地址不能写
"localhost:6831",得写"jaeger-agent:6831"(对应 docker-compose.yml 中的服务名) - Mac/Windows Docker Desktop 用户,可用
"host.docker.internal:6831"替代"localhost",避免容器内 localhost 指向自身 - 若用
jaeger-all-in-one镜像但配成"localhost:6832",注意 6832 是 Binary 协议端口,需显式设置Reporter.Protocol = "thrift_binary",否则静默丢弃
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










