Go项目集成Jaeger的核心是初始化Tracer、入口处创建/提取Span、全程透传Span上下文;需注意Reporter端口匹配、LogSpans调试开关、Gin中间件中Context绑定、gRPC/Thrift/MQ手动透传等关键细节。

直接上结论:Go 项目集成 Jaeger 的核心是三件事——初始化 opentracing.Tracer、在入口处提取或创建 Span、把 Span 上下文透传到下游调用。不是配完就完事,漏掉上下文透传,链路就断了。
初始化 Tracer 时必须注意 Reporter 配置
本地开发常用 LocalAgentHostPort: "localhost:6831",但这个端口依赖 Jaeger Agent 进程在运行。如果只跑 jaeger-all-in-one(默认监听 6832 UDP),却仍配成 6831,span 就会静默丢弃,UI 里完全看不到数据。
-
Reporter.LogSpans: true开启后能在控制台看到 span 日志,适合调试阶段验证是否上报成功 - 生产环境建议关闭
LogSpans,改用BufferFlushInterval控制批量上报节奏,避免高频小包冲击网络 - 若跳过 Agent 直连 Collector,需用
jaeger.NewRemoteReporter(sender)+transport.NewHTTPTransport,此时端口是14268(HTTP 接收)而非 UDP 端口
Gin 中间件里 Span 创建和 Context 注入不能少
常见错误是只调用 StartSpan,但没把生成的 Span 绑定到 http.Request.Context(),导致后续业务代码或中间件拿不到当前 span,也无法派生子 span。
- 必须用
opentracing.ContextWithSpan(r.Context(), span)构造新 context,并通过r.WithContext()传递下去 - 提取上游 context 要用
tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header)),失败时才新建 root span - 别在
defer span.Finish()前修改w.Header()或写 response body,否则可能触发 panic(某些中间件提前结束 response writer)
gRPC 和 Thrift 调用必须手动透传 SpanContext
HTTP 请求头能自动携带 uber-trace-id,但 gRPC metadata 和 Thrift 的传输层不认这个。不显式注入,跨协议调用链就断在第一跳。
- gRPC 客户端调用前:用
grpc_md.AppendToOutgoingContext把tracer.Inject(span.Context(), opentracing.HTTPHeaders, ...)得到的 headers 写入 metadata - Thrift 客户端需在
TProtocol层拦截,把span.Context()序列化进自定义 header 字段(如X-B3-TraceId) - 服务端接收后,要用对应方式反向
Extract,再调用StartSpan(..., opentracing.ChildOf(...))
traceID 穿透 MQ 场景容易被忽略
HTTP → gRPC → RabbitMQ → 消费者,这条链里 MQ 是黑洞。Producer 不注入、Consumer 不提取,消费者里的 span 就变成孤立 root span,和上游完全无关。
关键点在于:发送消息时,把当前 span.Context() 用 tracer.Inject 序列化进消息的 headers(如 JSON 字段或 AMQP property),消费者拿到后先 tracer.Extract,再基于它创建 child span。这步手工操作没有框架自动帮你做,漏掉整个异步链路就不可见。











