Go项目接入OpenTelemetry SDK应使用go.opentelemetry.io/otel替代废弃库;需正确初始化TracerProvider并调用Shutdown();HTTP用otelhttp.NewHandler自动透传context;gRPC需双向配置拦截器;Span属性应限于低基数稳定字段,动态值走AddEvent();错误须调用RecordError()并设status。

Go 项目中接入 OpenTelemetry SDK 的核心步骤
直接用 go.opentelemetry.io/otel 替代已废弃的 contrib.go.opencensus.io/exporter/jaeger 或旧版 Opentracing,是当前最稳妥的选择。OpenTelemetry 已成事实标准,Go SDK 稳定且兼容主流后端(Jaeger、Zipkin、OTLP HTTP/gRPC)。
- 安装 SDK:
go get go.opentelemetry.io/otel@v1.24.0
go get go.opentelemetry.io/otel/sdk@v1.24.0
go get go.opentelemetry.io/otel/exporters/jaeger@v1.19.0 - 初始化 TracerProvider 时必须显式调用
Shutdown(),否则进程退出时 trace 可能丢失;常见漏写位置在main()结束前或 HTTP server 关闭钩子中 - 不要复用全局
trace.Tracer实例跨 goroutine 传参——它本身是线程安全的,但 span 生命周期需手动管理;推荐用context.WithValue(ctx, key, span)或更规范地用trace.SpanFromContext(ctx)
HTTP 中间件自动注入 trace context 的写法
Go 的 net/http 没有原生 context 透传机制,必须靠中间件解析 traceparent header 并注入到 request context,否则下游服务无法延续链路。
- 使用
otelhttp.NewHandler()包裹 handler 是最简方式,它自动处理 inbound/outbound context 注入与 span 创建 - 若需自定义逻辑(如跳过健康检查路径),需手动调用
otelhttp.TraceRoute()和otelhttp.ExtractTraceIDFromRequest(),注意traceparent格式必须严格匹配 W3C 标准(00-),否则解析失败返回空 span- -01 - 避免在中间件里重复调用
tracer.Start():otelhttp.NewHandler()已创建 root span;自行 start 会导致 span 嵌套错乱,表现为 Jaeger UI 中出现孤立的“double-root”节点
gRPC 客户端和服务端 trace 透传的关键配置
gRPC 默认不传播 context 中的 trace 信息,必须显式启用拦截器,否则 client → server 链路在 server 端断开。
- 客户端侧:用
otelgrpc.UnaryClientInterceptor()和otelgrpc.StreamClientInterceptor()包裹 dial 选项,缺一不可;若只加 unary,stream 调用将无 trace - 服务端侧:注册
otelgrpc.UnaryServerInterceptor()和otelgrpc.StreamServerInterceptor()到grpc.Server,否则收到请求后trace.SpanFromContext(ctx)返回 nil - 注意 gRPC metadata key 大小写敏感:
traceparent必须全小写;若客户端用metadata.Pairs("Traceparent", ...)(首字母大写),server 端将无法识别
Span 属性设置不当导致采样失效或查询困难
很多团队把 trace 当日志用,塞大量动态值进 span.SetAttributes(),结果引发两个问题:采样率骤降(因属性膨胀触发 SDK 内存限流)、ES/ClickHouse 查询变慢(高基数字段)。
立即学习“go语言免费学习笔记(深入)”;
- 仅对稳定、低基数、有业务意义的字段设 attribute,例如:
http.method、rpc.service、db.system;禁止写user_id="12345"、order_no="ORD-2024-xxx" - 动态值应走
span.AddEvent(),比如 “订单创建成功”、“库存扣减失败”,event 支持带 timestamp 和 attributes,且不影响 span 元数据结构 - 错误处理必须调用
span.RecordError(err)并设status = codes.Error,否则即使抛 panic,trace 也会显示为 success,掩盖真实失败率










