TraceID在HTTP入口就断了,说明OTel未正确识别链路起点,需检查是否用otelhttp.NewHandler包裹最终handler、有无绕过handler的直连路由、otelhttp.NewHandler是否在启动时注册而非热重载替换。

TraceID 在 HTTP 入口就断了,怎么查?
TraceID 断在最开始,说明请求压根没被 OTel 正确识别为链路起点。不是日志没打,而是 Span 根本没创建。
常见错误现象:Jaeger 里只看到孤立的 root span,上下游无关联;curl -H 'traceparent: ...' -v http://svc/ 后查不到父子关系。
- 检查是否用
otelhttp.NewHandler包裹了最终 handler —— 如果用了http.ServeMux或自定义路由(如gorilla/mux),必须显式 wrap,不能只在中间件里手动取X-Trace-ID - 确认没有绕过 handler:比如健康检查接口直走
http.HandleFunc,或静态文件路由未被 OTel 中间件覆盖 -
otelhttp.NewHandler必须在 server 启动时注册,若 runtime 动态替换 handler(如热重载),旧实例的 tracer 上下文会丢失
下游 HTTP 调用不显示父子关系,是哪步错了?
调用发出去了,但链路上下游“失联”,大概率是 traceparent 头重复注入或压根没注入。
典型表现:Jaeger 中 client span 显示为 root,server 端却有另一个独立 trace;或者 traceparent header 出现两次,值不同。
- 只用
otelhttp.NewClient,别再手动调propagator.Inject—— 前者已内置 inject,后者会覆盖或追加,导致解析失败 - 如果必须用原生
http.Client(比如要定制Transport),确保ctx里已有 active span(即当前 handler 已StartSpan),否则propagator.Inject注入的是空 traceparent - 检查
req.Header是否被其他中间件(如 auth、logging)清空或覆盖 ——traceparent是大小写敏感的 W3C 标准字段,nginx或ALB可能强制转小写,需在入口 middleware 中重写为Traceparent
gRPC 调用链在服务端断开,拦截器注册对了吗?
HTTP 链路正常,gRPC 却变成两个独立 trace,问题几乎一定出在 server 拦截器注册时机或顺序上。
错误现象:grpc client 发起调用后,server 端生成新 trace,traceparent metadata 存在但未被 OTel 提取。
- 必须在
grpc.Server初始化时,通过grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor())或grpc.StatsHandler(otelgrpc.NewServerHandler())注册 —— 不能晚于 server 启动 - 如果同时用了自定义拦截器(如鉴权、限流),OTel 拦截器必须放在最外层;否则鉴权逻辑可能提前返回或 panic,导致 metadata 来不及被提取
-
otelgrpc.UnaryServerInterceptor依赖metadata.FromIncomingContext,若上游 client 用的是grpc.Dial但没传grpc.WithUnaryInterceptor,traceparent就不会进 metadata
跨机房调用链里 Span 耗时不准,光看 Jaeger 数据会误判
同一对服务间 Span 时间波动剧烈(比如 80ms–350ms),但实际业务逻辑很稳定 —— 这说明网络抖动、TLS 重协商、NAT 丢包这些底层开销混进了 Span 统计里。
根本原因:Span.End() 记录的是应用层 write 完成时间,不是数据真正抵达下游的时间。
- 不要只信
Jaeger的耗时数字,用tcpdump或bpftrace抓包验证 SYN-ACK、TLS handshake、first byte to last byte 的真实往返 - 补网络层观测:在
http.Transport.DialContexthook 里记录建连耗时;用自定义RoundTripper记录每个请求的 write+read 总耗时 - 连接池必须按机房隔离 —— 同一个
http.Client复用北京和新加坡的 idle conn,会导致连接复用率暴跌、频繁重建,把网络延迟放大数倍
链路故障溯源最易忽略的点,不是埋点漏了,而是传播路径被中间件、LB 或自定义 Transport 悄悄截断或改写;一旦 traceparent 头失效,整个链路就碎成孤岛。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











