jaeger.newtracer 返回 nil 时,opentracing.startspan 必然 panic;需显式检查 tracer == nil || err != nil,确保 servicename 非空、采样器非空、reporter 地址正确(如容器中用服务名而非 localhost),并正确传递 context。

jaeger.NewTracer 返回 nil 时,opentracing.StartSpan 必然 panic —— 这不是代码写错了,是 tracer 根本没活下来。
tracer 初始化失败却静默返回 nil,怎么防?
Jaeger 的 jaeger.NewTracer(或 cfg.NewTracer())在 reporter 连不上 Agent/Collector、ServiceName 为空、采样器配置缺失等情况下,可能不报错直接返回 nil。而 opentracing.StartSpan 完全不校验 tracer 非空,一调就崩,堆栈里还找不到明确提示。
- 必须显式检查:
if tracer == nil || err != nil,不能只判err -
ServiceName是强制字段,填空字符串或留零值会导致 UI 显示unknown_service - 采样器不能省:
jaeger.NewConstSampler(true)(开发)或jaeger.NewProbabilisticSampler(0.1)(生产) - 别在
init()函数里初始化 tracer:配置项(如环境变量)可能还没加载完 - 本地调试可加一句:
log.Printf("global tracer: %v", opentracing.GlobalTracer()),确认不是<nil></nil>
UDP reporter 在容器里连不上 agent,为什么完全没报错?
Go 客户端默认用 UDP 发送到 "localhost:6831",但这个地址在 Docker 或 Kubernetes 中几乎总是错的——容器内的 localhost 指向自己,不是 Jaeger Agent。
- Docker Compose 环境:把
LocalAgentHostPort改成"jaeger-agent:6831"(服务名) - Mac/Windows Docker Desktop:用
"host.docker.internal:6831" - Kubernetes:用 Service DNS 名,如
"jaeger-agent.default.svc.cluster.local:6831" - UDP 不建连、不重试、不报错——填错地址,span 就彻底消失,日志里连 warning 都没有
- 调试阶段建议换
jaeger.NewHTTPReporter("http://jaeger-collector:14268/api/traces"),HTTP 失败会明确报错
HTTP 中间件里 span 断链,90% 是 context 没绑对
上游注入了 uber-trace-id,下游也提取了,但 UI 里 trace 还是两截——问题不在 header 传递,而在 Go 的 context.Context 没正确流转。
- 注入后必须调
req = req.WithContext(opentracing.ContextWithSpan(req.Context(), span)),否则 handler 里的r.Context()仍是空的 - 提取时要用
tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header)),别手动读r.Header.Get("uber-trace-id") - 中间件必须包裹整个 handler 链;如果只在 middleware 里
StartSpan,却在业务 handler 里才Finish,panic 时defer不执行,span 就永远卡在 “in progress” - 别依赖
http.DefaultTransport自动透传——它不会碰 trace 上下文,得用othttp.Transport或手动Inject
gRPC / MQ / Thrift 调用链断在第一跳,因为没人手动透传
HTTP 请求头能自动带 uber-trace-id,但 gRPC metadata、AMQP headers、Thrift 协议层都不认这个。框架不帮你做,漏一步,整条异步链路就不可见。
- gRPC 客户端:用
grpc_md.AppendToOutgoingContext(ctx, "uber-trace-id", traceIDStr),或更规范地用otgrpc.OpenTracingUnaryClientInterceptor(tracer) - gRPC 服务端:注册
grpc.UnaryInterceptor(otgrpc.OpenTracingUnaryServerInterceptor(tracer)) - RabbitMQ Producer:把
span.Context()用tracer.Inject()序列化进 AMQPheaders字段(不是 body) - RabbitMQ Consumer:先
tracer.Extract()拿到 parent context,再tracer.StartSpan("consume", opentracing.ChildOf(parentCtx)) - 所有跨协议调用,都绕不开「序列化 → 传输载体 → 反序列化 → StartSpan」这三步,没捷径
req.WithContext() 忘了调,链路就断了。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











