TracerProvider 必须在 main() 开头注册,否则 span 为 nil;需调用 otel.SetTracerProvider 和 otel.SetTextMapPropagator;HTTP 入口用 otelhttp.NewHandler,出向用 otelhttp.Transport;数据库用 otelsql.InjectDriver;避免高基数字段作 attribute。
TracerProvider 必须在 main() 开头就注册,否则所有 span 都是空的
Go 里
调用不会报错,但如果你没提前注册
,它返回的
永远是
,日志里看不到 trace_id,HTTP 中间件也不生效——这是最典型的“静默失效”。
必须在
函数最开始、任何 handler 启动前完成注册,全局只做一次
别放在
里:单元测试并发初始化会 panic
别在某个 HTTP handler 里临时 new 一个:会导致多个 provider 冲突,采样逻辑错乱
注册后务必调用
和
,缺一不可
HTTP 入口必须用
,手写 extract + StartSpan 是错的
很多人自己解析
再调
,结果 span 名字全是
、没有状态码、不带
、跨 goroutine 传播失败——因为漏掉了 OpenTelemetry 对 HTTP server 语义的完整封装。
会自动:提取 header、注入 context、记录 method/path 为 span name、捕获 response status、结束 span 时机精准到 writeHeader 完成
Gin 用户不要用
包裸
;要用
,否则路由名丢失(如
变成
)
自定义中间件里要起 child span,必须从
取 context,不能用
下游 HTTP 调用必须用
,
是断链元凶
入向用了
,但出向还用原始
,那整个链路只有一跳——下游服务收不到
,span 就断在第一个服务出口。
正确做法:
千万别直接改
:会影响其他未 instrument 的库,且无法隔离采样策略
如果用了第三方 SDK(比如 AWS SDK Go v2),要确认它是否支持传入自定义
;不支持的话得换用
等专用插件
数据库追踪不能靠手动 Start/End,必须用
看到有人在
前后手动埋点,结果 span duration 比实际慢几倍或快几倍——因为没覆盖连接池等待、驱动参数解析、预编译 Stmt 创建、Rows.Scan 等关键阶段。
立即学习
“
go语言免费学习笔记(深入)
”;
只 wrap 了
层,对
或
无效
真正生效的是
这类底层 driver 注入,它拦截所有
底层调用
避免高基数字段当 span attribute:比如把
或
直接塞进 span,会撑爆 collector 内存
事情说清了就结束。最常被忽略的是:采样器配置和传播器初始化顺序,这两处一错,整条链路就断在第一个服务里。
otel.Tracer()sdktrace.TracerProviderspannilmain()init()otel.SetTracerProvider(tp)otel.SetTextMapPropagator(propagation.TraceContext{})otelhttp.NewHandlerreq.Header.Get("traceparent")tracer.Start()HTTP GETnet.peer.ipotelhttp.NewHandlergin.Use(...)http.Handlerginotel.MiddlewareGET /api/order/:idGET /r.Context()context.Background()otelhttp.Transporthttp.DefaultClientotelhttp.NewHandlerhttp.Clienttraceparentclient := &http.Client{Transport: otelhttp.NewTransport(http.DefaultTransport)}http.DefaultClienthttp.Clientotelawsotelsql.InjectDriverdb.Query()otelsql.Open()*sql.DB*sql.TxStmt.Execotelsql.InjectDriver("postgres", &pq.Driver{})database/sqluser_emailrequest_body