Go微服务需通过OpenTelemetry SDK在HTTP/gRPC入口中间件中解析traceparent等header并用Extract+StartSpanWithOptions实现自动注入trace_id/span_id,严禁手动拼接或混用Jaeger与OTel。

Go 微服务怎么自动注入 trace_id 和 span_id
Go 服务本身不带链路追踪能力,必须依赖 OpenTracing 或 OpenTelemetry SDK 主动埋点。但“自动注入”其实只在 HTTP/RPC 入口处可行——靠中间件拦截请求,从 headers(如 traceparent、uber-trace-id)中提取上下文,再创建新 Span 并关联。
常见错误是直接用 tracer.StartSpan() 创建孤立 Span,没做上下文传播,导致链路断裂。正确做法是用 tracer.Extract() + tracer.StartSpanWithOptions() 带 ChildOf() 或 FollowerOf() 引用。
- HTTP 服务:在
http.Handler中解析req.Header.Get("traceparent"),传给ot.Tracer.Extract() - gRPC 服务:实现
grpc.UnaryServerInterceptor,从metadata.MD取traceparent - 避免手动拼接
span.Context().SpanID().String()注入日志——应统一用span.Tracer().StartSpan()创建子 Span,让 SDK 自动处理上下文传递
OpenTelemetry Go SDK 和 Jaeger Client 能不能混用
不能混用。Jaeger 的 github.com/uber/jaeger-client-go 是 OpenTracing 实现,而 OpenTelemetry(go.opentelemetry.io/otel)是下一代标准,两者注册的全局 Tracer 实例互不兼容。混用会导致:
-
otel.Tracer("x").Start(ctx, "y")创建的 Span 不会被 Jaeger reporter 发送 -
jaeger.NewTracer()初始化后,otel.GetTracerProvider()返回空或 panic - 日志中出现
no tracer found in context或failed to start span: tracer is not registered
迁移建议:新项目直接用 OpenTelemetry;老 Jaeger 项目升级时,需统一替换 import 路径、初始化逻辑和 exporter 配置,尤其注意 TracerProvider 必须全局唯一且尽早初始化(如 main() 开头)。
立即学习“go语言免费学习笔记(深入)”;
HTTP 客户端发起调用时如何透传 trace 上下文
关键不是“加 header”,而是用 OpenTelemetry 提供的 propagation.HTTPFormat 将当前 Span 上下文序列化到请求头,否则下游无法继续链路。
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
"net/http"
)
func callDownstream(ctx context.Context, url string) error {
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
// 自动注入 traceparent、tracestate 等
otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(req.Header))
_, err := http.DefaultClient.Do(req)
return err
}
容易忽略的点:
- 必须用
http.NewRequestWithContext()把原始 ctx 传进去,否则Inject()没有上下文可读 - 别自己手写
req.Header.Set("traceparent", ...)—— OpenTelemetry 的 propagator 会按 W3C Trace Context 规范生成完整字符串,含 version、trace-id、span-id、flags - 如果下游是旧版 Zipkin/Jaeger,需额外配置
propagation.Baggage{} + propagation.TraceContext{}或切换为jaeger.Propagator(但仅限全链路都用 Jaeger)
为什么本地调试时 trace 数据总发不到后端 collector
最常见原因是 exporter 配置未生效或网络不通,而非代码逻辑问题。先确认三件事:
- collector 地址是否写错?例如用
localhost:4317但 collector 实际监听0.0.0.0:4318,或 Docker 容器内访问宿主机要用host.docker.internal - exporter 是否启动成功?检查初始化返回值:
exp, err := otlphttp.NewExporter(...),err != nil会静默失败 - 是否漏掉
tp.RegisterSpanProcessor(bsp)?OpenTelemetry 默认不发送数据,必须显式注册BatchSpanProcessor
调试技巧:启用 SDK 日志(设置环境变量 OTEL_GO_LOG_LEVEL=debug),看是否有 ExportSpans() returned error 或 connection refused。更直接的办法是临时改用 stdout exporter 输出原始 Span JSON,确认埋点本身是否正常。










