Go 的 oteltrace.Tracer 默认使用 sdktrace.NeverSample() 全丢弃 span,必须通过 TracerProvider 配置 sdktrace.WithSampler(如 ParentBased + TraceIDRatioBased)并确保 trace context 正确传播。

Go 的 oteltrace.Tracer 默认不采样,必须显式配置
Go 生态里用 OpenTelemetry 做分布式追踪时,oteltrace.Tracer 创建后默认行为是「全丢弃」——不是低采样,是根本没开启。很多人跑完服务发现 Zipkin/Jaeger 里空空如也,查日志也没报错,就是因为漏了采样器配置。
关键点在于:TracerProvider 初始化时必须传入 sdktrace.WithSampler,否则所有 span 都被 sdktrace.AlwaysSample() 之外的默认策略(即 sdktrace.NeverSample())直接跳过。
- 最常用的是按比例采样:
sdktrace.TraceIDRatioBased(0.1)表示约 10% 的 trace 被保留 - 调试阶段可临时用
sdktrace.AlwaysSample(),但上线前务必改掉 - 若依赖环境变量控制(比如 K8s ConfigMap 注入),建议读取后做校验:
if rate 1 { rate = 0.001 }
采样率写死在代码里容易出问题,得支持运行时动态调整
硬编码 sdktrace.TraceIDRatioBased(0.05) 看似简单,但线上一旦流量突增或出现异常,你没法快速调高采样率抓问题;反过来,大促期间采样太高又可能压垮 collector 或打爆存储。
OpenTelemetry Go SDK 本身不支持运行时热更新采样器,但可以绕过:用自定义 Sampler 包一层可变变量,并配合信号或 HTTP endpoint 触发重载。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 定义一个带
sync.RWMutex的结构体,暴露SetRate(float64)方法 - 它的
ShouldSample方法里加读锁,取当前 rate 构造TraceIDRatioBased判断(注意别每次 new,复用内部逻辑) - 避免在
ShouldSample里做 HTTP 请求或 DB 查询——这个函数每毫秒可能被调上千次
sdktrace.ParentBased 是生产环境必配的组合策略
单纯用 TraceIDRatioBased 会丢失「被调用方」的子 span:比如 A → B → C,A 决定不采样,那 B 和 C 即使自己想采也收不到 trace context,结果整条链路消失。这就是为什么线上常看到“只有入口有 trace,下游全断开”。
sdktrace.ParentBased 解决这个问题——它尊重上游决定:如果父 span 已被采样,子 span 强制继承;只有无父 span(即入口请求)时,才按你配的比率决策。
- 正确写法:
sdktrace.ParentBased(sdktrace.TraceIDRatioBased(0.01)) - 别写成
sdktrace.ParentBased(sdktrace.AlwaysSample()),这等于全量采集,和没设一样危险 - 如果你用的是 Gin/echo 等框架的 OTel 中间件,确认它们是否自动注入了 context;否则
ParentBased也救不了丢失的 propagation
HTTP header 透传不完整会导致采样决策错乱
Go 的 http.Client 默认不会转发所有 tracing header,尤其当你手动拼 req.Header.Set 时,容易漏掉 traceparent 或写错格式(比如多加空格、大小写混用),导致下游服务解析失败,误判为无 parent,触发独立采样逻辑。
- 务必使用
otelhttp.Transport替代原生http.Transport,它会自动处理traceparent/tracestate的注入与提取 - 检查下游服务收到的 header:用
fmt.Printf("headers: %+v", r.Header)打印验证,重点看traceparent是否存在且长度为 55 字符(标准格式) - 如果用 gRPC,对应要配
otelgrpc.Interceptor,不能只靠 HTTP 那套
采样不是设个数字就完事的事——它卡在 trace context 生成、传播、解析、决策四个环节里,任一环松动,数据就断。尤其是跨语言调用时,header 格式或 clock skew 都会让 TraceIDRatioBased 的数学意义彻底失效。

















