http handler 必须用 otelhttp.newhandler() 包裹,否则收不到 inbound 请求的 trace context,root span 直接丢失;它自动 extract traceparent、创建 root span、注入 context 到 request 并写回 response header。

HTTP handler 必须用 otelhttp.NewHandler() 包裹
不包裹就收不到 inbound 请求的 trace context,root span 直接丢失。OpenTelemetry 的自动注入只发生在中间件层,http.ServeMux 或 gin.Engine 原生 handler 不会触发 span 创建。
-
otelhttp.NewHandler()会自动 extracttraceparent、创建 root span、注入 context 到 request,并在 response 写回 header - 别在 handler 里手动调用
tracer.Start(ctx, ...)—— 这样生成的是孤立 span,parent 关系断裂 - Gin 用户应优先用
otelgin.Middleware(),它内部已封装otelhttp.NewHandler()行为;但注意它默认不 inject response header,需确认是否启用WithPropagator() - 自定义 mux 时,每个 route 都要显式 wrap:
http.Handle("/api", otelhttp.NewHandler(http.HandlerFunc(handler), "api"))
下游 HTTP 请求必须手动 Inject() trace header
Go SDK 不会自动把当前 span 注入 outbound 请求,req.WithContext(ctx) 只传 context,不传 trace 上下文 —— 这是最常被忽略的断链原因。
- 必须在
http.Client.Do(req)前调用:otel.GetTextMapPropagator().Inject(req.Context(), propagation.HeaderCarrier(req.Header)) - 用
otelhttp.NewClient()替代http.DefaultClient是最省事方案,它自动完成 inject + extract - 若用自定义 client(比如带 retry 的),务必封装
DoWithContext(ctx, req)工具函数,避免漏掉 inject - 跨语言调用时,检查对方 propagator 类型:Go 默认用 W3C
traceparent,但旧 Python/Java 服务可能只认 B3,需配置复合 propagator:propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.B3{})
otel-collector endpoint 配置错误导致上报静默失败
SDK 默认走 localhost:4317 的 OTLP gRPC,但生产环境 collector 往往监听 0.0.0.0:4317 或 TLS 端口,配置错就等于没上报 —— 且无 error 日志,span 直接消失。
- 启动时加日志验证 exporter 是否 ready:
log.Printf("OTLP exporter connected to %s", endpoint) - 生产环境必须显式指定 endpoint:
otlptracehttp.New(context.Background(), otlptracehttp.WithEndpoint("otel-collector:4317")) - 若 collector 启用了 TLS 或认证,需配
otlptracehttp.WithTLSClientConfig()或otlptracehttp.WithHeaders(map[string]string{...}) - 本地调试建议先切到
stdoutexporter,看到 JSON 输出再切回 OTLP,避免盲调
goroutine 中的 span 必须显式结束
异步任务里 start 了 span 却忘了 span.End(),会导致 span 卡在 batch 缓冲区,内存缓慢上涨,几小时后 OOM —— 这不是理论风险,是真实线上事故高发点。
- 所有
tracer.Start()必须配对span.End(),哪怕在 goroutine 里也要 defer:go func() { defer span.End(); ... }() - 别依赖“函数退出自动 close”,Go 没有析构机制,span 对象不会自动释放
- 消息消费(如 Kafka/NATS handler)、定时任务、后台 worker,都属于高危场景,需逐个检查
- 用
trace.SpanFromContext(ctx)拿当前 span 时,确保 ctx 真的带 span —— 若上游没透传或中间件漏配,SpanFromContext返回的是 noop span,End()无效但也不报错,容易误判
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











