http handler中用context.background()调tracer.start会生成孤立根span,因未继承r.context()中的trace id和parent span id;必须用r.context()作为参数,或改用otelhttp.newhandler自动注入上下文。

OpenTelemetry 是当前 Golang 分布式链路追踪最稳妥的选择,但直接用 otel.Tracer 打点容易漏上下文、跨 goroutine 断链、HTTP 透传失败——核心问题不在“怎么加”,而在“在哪加”和“怎么传”。
为什么 http.HandleFunc 里调 tracer.Start 会丢 span?
Go 的 HTTP handler 默认在新 goroutine 中执行,而 OpenTelemetry 的 context 是基于 context.Context 传递的,不是全局或 goroutine 绑定的。如果没把入参 r.Context() 显式传给 tracer.Start,新 span 就无法继承 trace ID 和 parent span ID,变成孤立根 span。
- 必须用
r.Context()作为tracer.Start的第一个参数,不能用context.Background() - HTTP 中间件(如
otelhttp.NewHandler)会自动完成 context 注入,比手动调Start更可靠 - 手动打点时,记得用
span.End(),且确保在 defer 中调用,否则 span 不上报
goroutine 内启动子 span 总是断链?
Go 的原生 go func() {}() 不会继承父 context,context.WithValue 也仅限当前 goroutine 生效。子 goroutine 必须显式接收并使用带 span 的 context。
- 错误写法:
go doWork()→doWork里用context.Background()开 span - 正确写法:
go doWork(ctx),并在doWork中用tracer.Start(ctx, "sub-task") - 若需异步但保留 trace 上下文,可用
otel.GetTextMapPropagator().Inject(ctx, carrier)+ HTTP header 或消息队列 metadata 透传
如何让 gRPC 和 HTTP 调用自动串联 trace?
靠 otelgrpc.Interceptor 和 otelhttp.Transport 这类官方插件,它们会在底层自动注入/提取 trace header(如 traceparent),但前提是 client 和 server 都启用,且传播器一致。
- server 端:gRPC 用
otelgrpc.UnaryServerInterceptor(),HTTP 用otelhttp.NewHandler() - client 端:gRPC dial 时加
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor());HTTP client 用&http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)} - 传播器默认是
tracecontext,若对接旧系统(如 Zipkin),需显式设为b3:otel.SetTextMapPropagator(b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader)))
本地开发时 trace 数据没发到 Jaeger/Zipkin?
OpenTelemetry SDK 默认不导出数据,需要显式配置 exporter 并注册 sdktrace.NewTracerProvider。常见遗漏点是忘记调 otel.SetTracerProvider(tp),导致所有 otel.Tracer 返回的是 noop 实现。
- Jaeger exporter 示例:
jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces"))) - 务必在 main 初始化早期调用
otel.SetTracerProvider(tp),否则后续 tracer 全部无效 - 开发时可先用
stdoutexporter.New()看日志输出,确认 span 结构和父子关系是否正确
真正难的不是埋点代码,而是理解 context 生命周期与 goroutine 边界之间的张力——一个没传对的 ctx,就能让整条链路在某个 service 里突然“消失”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











