Go服务集成Jaeger需正确初始化tracer(设ServiceName、匹配reporter端口)、显式透传span上下文(HTTP/gRPC/MQ各层),否则链路断裂;中间件须绑定span到request.Context,跨协议调用需手动注入提取,Tag/Log值禁用指针和nil。

Go 服务集成 Jaeger 能看到跨进程调用链,前提是 tracer 初始化正确、span 上下文在每个协议层都显式透传——漏掉 HTTP header 注入、gRPC metadata 拦截或 MQ 消息头写入,链路就会在第一个非 HTTP 跳断开。
tracer 初始化必须设 ServiceName 且 reporter 端口匹配
不填 ServiceName 或 reporter.LocalAgentHostPort 会导致 cfg.NewTracer() 返回 nil,但不会 panic,后续调用 StartSpan 直接崩溃。本地开发常见组合是:LocalAgentHostPort: "localhost:6831"(UDP,Thrift Compact 协议),对应 jaegertracing/all-in-one 默认监听的端口。
- Mac 上 Docker Desktop 的 UDP 转发偶尔失效,可改用
--network=host启动 agent,让 Go 进程直连localhost:6831 - 若用
jaeger-all-in-one镜像但配成"localhost:6832",span 会静默丢弃——6832 是 Binary 协议,需显式配置Reporter.Protocol = "thrift_binary" - 生产环境别开
LogSpans: true,改用BufferFlushInterval: 1 * time.Second控制批量上报节奏
Gin/Echo 中间件里必须绑定 span 到 request.Context
只调 tracer.StartSpan("http-server") 不够,生成的 span 必须注入到 http.Request.Context(),否则下游业务代码或中间件无法派生子 span。
- 提取上游:用
tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header)),失败时才新建 root span - 构造新 context:用
opentracing.ContextWithSpan(r.Context(), span),再传给r.WithContext() - 别在
defer span.Finish()前修改w.Header()或写 response body,某些中间件(如 CORS)会提前关闭 writer 导致 panic
gRPC 和 Thrift 调用必须手动注入/提取 SpanContext
HTTP header 自动带 uber-trace-id,但 gRPC 的 metadata 和 Thrift 的传输层完全不识别它——不显式操作,跨协议调用链就断在第一跳。
立即学习“go语言免费学习笔记(深入)”;
- gRPC 客户端:调用前用
grpc_md.AppendToOutgoingContext(ctx, "uber-trace-id", traceIDStr),其中traceIDStr来自tracer.Inject(span.Context(), opentracing.HTTPHeaders, ...) - Thrift 客户端:需在
TProtocol层拦截,把span.Context()序列化进自定义 header(如X-B3-TraceId),服务端再用对应方式反向Extract - MQ 场景更易忽略:Producer 发送消息时必须把当前 span context 写入 message headers,Consumer 收到后调用
StartSpan(..., opentracing.ChildOf(extractedCtx))
Tag 和 log 字段值不能为指针或 nil
span.SetTag("user_id", &u.ID) 会显示为地址或 <nil>;span.LogFields(log.String("error", err.Error())) 比 log.Object("err", err) 更稳,因为后者在 err == nil 时直接 panic。
- tag 键名避免空格和特殊字符,Jaeger UI 可能解析失败
- 不要用
opentracing.GlobalTracer(),多个 service 共享 tracer 会导致 tag 混淆;每个服务应初始化独立Tracer实例 - 跨 goroutine 调用时,必须显式传递带 span 的 context,不能依赖闭包捕获原始 request.Context
最常被忽略的是 MQ 和定时任务场景——它们没有天然的“上游请求上下文”,必须在投递消息或触发任务时主动创建并注入 span,否则整条链路从这里开始就是孤立节点。


















