HTTP服务端需用otel.GetTextMapPropagator().Extract()显式解析traceparent,否则SpanFromContext()返回nil;客户端每次Do前必须Inject,header名须全小写traceparent且格式符合W3C标准。

HTTP服务端必须用otel.GetTextMapPropagator().Extract()解析traceparent
Go的http.Request.Context()不带任何trace信息,直接用trace.SpanFromContext(r.Context())拿到的一定是nil。你得在入口(比如中间件或handler开头)显式提取:
- 调用
otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header)),不是自己r.Header.Get("traceparent")再手写解析 - 提取失败时别传
nilcontext——要fallback创建root span:tracer.Start(context.Background(), "fallback-root") - W3C标准要求
traceparentheader全小写,代理或CDN可能过滤掉驼峰或下划线命名的header,比如Trace-Parent或X-Traceparent都不可靠
HTTP客户端每次Do前必须Inject,不能只设一次header
http.Client完全不读取context里的span,也不会自动往outbound请求塞header。漏掉一次Inject,链路就在那一跳断开。
- 构造
*http.Request后、调client.Do(req)前,必须执行otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(req.Header)) - 别用
req.Header.Set()硬编码"traceparent"字符串——W3C格式是"00-{traceid}-{spanid}-01",其中traceid和spanid必须是32位小写十六进制,不足补0;采样标志01表示sampled,00表示not sampled - 如果下游是OTel服务,还要加
tracestateheader,比如otel=00000000000000000000000000000001
context.WithValue的key必须是unexported类型,不能用字符串
用context.WithValue(ctx, "span", s)看着简单,但极易被其他包覆盖——只要另一个模块也用"span"当key,值就丢了。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 定义私有key类型:
type spanKey struct{},然后用ctx = context.WithValue(ctx, spanKey{}, s) - 更推荐直接用OpenTelemetry封装好的取值方式:
span := trace.SpanFromContext(ctx),它内部已处理好key安全 - 别把整个
context.Context传给goroutine——goroutine启动后父context可能已cancel;应该把span本身作为参数传进去,span是thread-safe的
gRPC场景优先用otelgrpc.UnaryClientInterceptor()
手动处理metadata比HTTP header更易出错:gRPC不支持自由header,所有上下文传播必须走metadata.MD,且每次RPC调用都要显式携带。
- 客户端侧:用
otelgrpc.UnaryClientInterceptor()(v1.20+ otelgrpc默认支持W3Ctraceparent),避免自己写metadata.Pairs("traceparent", ...) - 服务端侧:确保用
otelgrpc.UnaryServerInterceptor()包装handler,否则metadata.FromIncomingContext(ctx)取不到上游注入的span - 注意gRPC metadata key约定:server端读
md["traceparent"],client端写metadata.Pairs("traceparent", value),大小写必须一致,且不能带空格或下划线
跨服务传播最脆弱的点不在代码逻辑,而在header命名和大小写——traceparent写成Trace-Parent或trace-parent,哪怕内容完全正确,也会被Nginx、Envoy或某些SDK静默丢弃。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










