opentelemetry是go链路追踪的事实标准,选错方案会导致span静默丢弃;根本原因是sdk未初始化、导出器未注册或网络不通,需显式调用otel.settracerprovider(tp)并配置正确endpoint与采样策略。

OpenTelemetry 是当前 Go 语言链路追踪的事实标准,不是 opentracing,也不是 runtime/trace。选错方案会导致链路静默断裂、数据无法导出、调试时完全看不到 span。
为什么本地能看到 stdout 输出,但 Jaeger / Tempo 里空空如也
这不是代码写错了,八成是 exporter 配置或网络连通性问题:
-
OTLPexporter 默认超时仅 5 秒,失败不报错、不 panic、也不打日志,trace 直接丢弃 - 本地开发用
localhost:4317,K8s 环境却硬编码了这个地址——服务发现走的是otel-collector.default.svc.cluster.local:4317 - collector 端口配错:gRPC 导出用
4317,HTTP 导出用4318;若 collector 只监听4318,而 client 死磕4317,就全量静默丢弃 - 没跑 collector:只启了
jaeger-all-in-one(默认监听16686UI +14268HTTP 接收),但 exporter 配的却是jaeger-thriftUDP 端口6831,根本对不上
快速验证:用 curl -v http://<em>your-collector</em>:4318/v1/traces -X POST -H "Content-Type: application/json" -d "{}" 看是否返回 200。
HTTP 中间件里 Span 总是断在第一个 handler 就结束
根本原因是 context 没绑定,或中间件顺序导致下游拿不到带 span 的 context:
-
otelhttp.NewHandler必须包裹最终业务 handler,不能套在其他中间件之后——比如放在gin.Logger()后面,gin.Logger()会先用req.Context(),但此时还没注入 span - 没调
span.End()或 defer 位置错误:在写 response body 后才End(),可能漏记 status code;更糟的是在defer里span.End()前修改了 header,span 记录的状态码仍是初始值 - 自定义中间件中手动创建 span 时,忘了把新 span 注入 request context:
req = req.WithContext(otel.TraceContextWithSpan(req.Context(), span))
数据库、gRPC、MQ 调用后链路就断了
HTTP 自动传播靠 header,但 DB/gRPC/MQ 不认 traceparent,必须显式透传:
- 数据库:不能直接用
sql.Open,得用go.opentelemetry.io/contrib/instrumentation/database/sql包,且必须otel.WrapDriver(driver)替换 driver - gRPC 客户端:必须加
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()),光传ctx不够 - RabbitMQ/Kafka 生产者发送消息前,要手动
tracer.Inject(span.Context(), opentracing.TextMap, carrier)把 trace context 写进消息 headers;消费者收到后,先tracer.Extract再StartSpan(..., ChildOf(...)) - 启动 goroutine 时:写
go doWork(ctx),别写go doWork(context.Background())——闭包捕获的外层ctx不会自动跨协程生效
采样率设了却还是全量上报,或压根不采样
采样器必须在 TracerProvider 初始化时绑定,后续无法热更新:
- 错误写法:
otel.SetTracerProvider(tp)之后再调tp.WithSampler(...)—— 无效 - 正确写法:构建
TracerProvider时就传参,例如oteltrace.NewTracerProvider(oteltrace.WithSampler(oteltrace.ParentBased(oteltrace.TraceIDRatioBased(0.1)))) - 如果用了
otelhttp.NewHandler,确保它用的是你刚创建的TracerProvider,而不是全局默认 provider(否则采样配置不生效) - 测试阶段建议用
AlwaysSample,上线前切到ParentBased+TraceIDRatioBased,避免低流量服务因采样率过低导致 trace 看不到
最易被忽略的一点:所有 instrumented client(SQL、gRPC、HTTP client)都依赖同一个 TracerProvider 实例,复用失败会导致 metrics 错乱、exporter 连接泄漏、甚至 span 丢失。全局只初始化一次,别在 handler 里反复 new。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











