opentelemetry 是当前 golang 微服务链路追踪的首选,因其已合并 opentracing 与 opencensus、是 cncf 毕业项目、官方 sdk 成熟且生态统一;旧方案如 opentracing-go 和 jaeger-client-go 已归档或过时,手动埋点易导致 trace id 不透传、context 丢失、采样失效。

为什么 OpenTelemetry 是当前 Golang 微服务链路追踪的首选
因为 OpenTelemetry 已合并 OpenTracing 和 OpenCensus,成为 CNCF 毕业项目,Golang 生态中所有新项目都应直接对接它,而非继续用已归档的 opentracing-go 或过时的 jaeger-client-go。官方 SDK(go.opentelemetry.io/otel)对 Go 原生支持好,且导出器(如 Jaeger、Zipkin、OTLP)维护活跃。
常见错误是:用 jaeger-client-go 自行构造 span 并上报,结果 trace ID 不跨服务透传、context 丢失、采样率不生效——根本原因是没走 OTel 的全局 TracerProvider 和 TextMapPropagator。
- 必须通过
otel.Tracer("service-name")获取 tracer,不能 new 自定义 tracer - HTTP 请求头传播必须用
otel.GetTextMapPropagator().Inject()和.Extract(),不能手动 copyuber-trace-id或traceparent - 启动时需显式设置全局 propagator:
otel.SetTextMapPropagator(otelhttp.NewPropagator())
如何让 HTTP 客户端和服务端自动注入/提取 trace context
别手写 Inject/Extract 逻辑。直接用 otelhttp 包封装标准 http.Client 和 http.ServeMux,它会自动处理 traceparent 头的读写、span 生命周期和 parent-child 关联。
典型场景:一个 Gin 服务调用另一个 REST 接口,但下游 trace ID 断了——大概率是用了裸 http.DefaultClient,或没给 http.ServeMux 加中间件。
- 客户端:用
otelhttp.NewClient(http.DefaultClient)替代原生 client,所有Do()调用自动携带 context - 服务端:把
otelhttp.NewHandler(yourMux, "service-name")作为最终 handler,不是简单 wrap 某个路由 - Gin 用户注意:
gin.Engine本身不兼容http.Handler接口,得用gin.WrapH(otelhttp.NewHandler(...)) - gRPC 场景换用
otelgrpc,同样要替换grpc.Dial()和grpc.Server()
Span 名称怎么设才不影响下游聚合分析
Span 名不是随便取的字符串。Jaeger/Zipkin 等后端按 span name 分组统计,如果每个 HTTP handler 都用 r.URL.Path 作为 name(比如 /user/123),会导致成千上万个唯一 name,UI 卡死、指标爆炸。
正确做法是:用稳定、可聚合的 operation name,例如 HTTP GET /users/{id},而不是带真实参数的路径。
- HTTP server:推荐用
otelhttp.WithSpanNameFormatter自定义格式,提取路由模板(如 Gin 的c.FullPath()) - HTTP client:用
otelhttp.WithSpanNameFormatter把 URL 路径泛化,例如将https://api.example.com/v1/users/456→HTTP POST /v1/users/{id} - 数据库调用:不要用
"db.Query"这种泛化名,而应包含表名和操作类型,如"sql SELECT users"或"redis GET user:token"
本地开发时 trace 数据发不到 Jaeger?检查这三处
本地跑不通 ≠ 代码错,大概率是 OTLP endpoint 或 exporter 配置不对。Jaeger 默认监听 localhost:14268(collector HTTP)或 localhost:6831(agent thrift),但 OpenTelemetry SDK 默认走 OTLP/gRPC localhost:4317,两者不兼容。
- 确认 Jaeger 是否启用了 OTLP receiver:启动命令加
--otlp.address=0.0.0.0:4317,或改用otel-collector(更推荐) - Go 代码里 exporter 必须匹配:用
otlptracehttp.NewClient就连http://localhost:4318/v1/traces;用otlptracegrpc.NewClient才连localhost:4317 - 环境变量
OTEL_EXPORTER_OTLP_ENDPOINT和代码里硬编码 endpoint 冲突时,以代码为准——SDK 不读该变量,除非你手动调otel.Init()或用otel/sdk/trace的 env config
最易忽略的是:忘记调 tp.Shutdown(context.Background()) —— 程序退出前不 flush,最后一段 trace 就丢了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











