必须改用 opentelemetry,因 jaeger-client-go 已归档、不支持 go 原生 context 透传,易致 span 断裂;tracerprovider 需在 main() 开头初始化,配合 otelhttp/otelgrpc/otelgin 等中间件自动埋点并严格对齐请求生命周期。

别再用 jaeger-client-go,它已归档、不支持现代 Go 的 context 透传模型,90% 的“单服务链路”或“父子关系错乱”都源于此。
初始化 TracerProvider 必须在 main() 开头执行
所有 Tracer().Start() 调用依赖全局 TracerProvider。如果 otel.SetTracerProvider(tp) 晚于 HTTP handler 启动或 goroutine 创建,后续 span 全部退化为 noop —— 看不到任何数据,也不报错。
- 必须在
main()函数最开始调用initTracer(),且确保返回无误 - 导出器优先选
otlphttp.NewClient()(对接 Jaeger Collector 的/v1/tracesHTTP 接口),避免 UDP 端口(如6831)不稳定和防火墙问题 -
WithResource中至少设置semconv.ServiceNameKey.String("your-service"),否则 Jaeger UI 无法按服务分组
Gin/gRPC/HTTP 客户端必须用对应 otelxxx 中间件或包装器
手动 StartSpan 极易遗漏生命周期,且无法对齐请求边界。自动化中间件才是生产级做法,但必须配对使用,缺一不可。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- Gin:导入
go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin,注册router.Use(otelgin.Middleware())—— 注意不是旧版otelgin.NewMiddleware - gRPC:服务端加
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),客户端加grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()) - 下游 HTTP 调用:禁用
http.DefaultClient,改用otelhttp.NewClient()实例,它会自动 injecttraceparentheader
DB 和 goroutine 中 context 透传最容易漏
跨 goroutine 不传 req.Context(),或 DB 驱动没 wrap,是链路断裂最常见原因——你在 Jaeger 里看到的“孤立 root span”,基本就是这里断的。
- 启动 goroutine 时写成
go doWork(req.Context()),而不是go doWork() - MySQL 用
otelmysql.Wrap(sql.Open(...)),PostgreSQL 用otelpostgresql.Wrap(pgsql.Open(...)),否则 SQL 执行不计入 span -
time.AfterFunc回调中,原req.Context()可能已被 cancel,应改用otel.WithSpanContext(context.Background(), span.SpanContext())构造新 ctx - 手动
StartSpan时,parent 必须来自req.Context(),禁止用context.Background()
调试时先验证 traceparent 头是否透传成功
上线前本地快速验证链路完整性,比等部署后查日志高效得多。
- 启动服务时加
stdoutexporter(不用改 Jaeger 配置),看控制台是否输出 span 数据 - cURL 请求时带上
-H "traceparent: 00-12345678901234567890123456789012-1234567890123456-01",确认入口 span 能解析并生成子 span - 检查下游服务收到的请求 header 是否含
traceparent,缺失说明中间件未生效或 client 未包装
真正难的不是埋点,而是让 span 生命周期与请求生命周期严丝合缝;一个 defer span.End() 漏写、一次 context 传递遗漏,整条链就断了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










