go服务链路监控生效的关键是tracerprovider正确初始化、context透传及span不丢失:需在main()中调用otel.settracerprovider(tp),禁用noop实现,http用otelhttp.newhandler自动埋点,goroutine传ctx而非background,数据库和grpc需专用封装,span.end()必须配对,日志trace_id须从ctx提取。

Go 服务接入链路监控,核心不是“加几个埋点”,而是让 TracerProvider 真正生效、context.Context 正确透传、Span 不被静默丢弃——否则 Jaeger / Tempo 里永远空空如也。
为什么 tracer.Start() 没数据上报?
根本原因是 SDK 默认使用 NoopTracerProvider,所有 tracer.Start() 调用都静默失败,不报错也不打日志。
- 必须在
main()开头就调用otel.SetTracerProvider(tp),其中tp是你用sdktrace.NewTracerProvider()创建并配置了 exporter 的实例 - 别在
init()函数里初始化,Go 的init执行顺序不可控,容易导致 handler 启动时TracerProvider还没 ready - 本地调试优先用
stdouttrace.NewExporter(),它把 span JSON 直接打到终端,比查 collector 日志快得多 - 进程退出前记得调用
tp.Shutdown(context.Background()),否则最后一批 span 可能卡在缓冲区丢失
HTTP 请求入口怎么自动埋点?
手动解析 traceparent header 并创建 span 容易漏逻辑,推荐直接用标准中间件封装。
- 对原生
net/http:用otelhttp.NewHandler()包裹你的http.Handler,它自动 extract + inject + 创建 server span - 对 Gin:用
otelgin.Middleware()(来自go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin),别自己写中间件去改c.Request.Context() - 中间件提取 context 后,务必用
otel.GetTextMapPropagator().Inject()把 trace 上下文写回 response header,否则下游收不到 - 如果前端或旧系统用
b3格式(如X-B3-TraceId),得配复合 propagator:propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.B3{})
下游调用和异步任务怎么保链路不断?
Go 的 goroutine 不继承父 context,go func() { ... }() 是链路断裂最高发场景。
- HTTP 客户端调用下游:别用
http.DefaultClient,改用otelhttp.NewClient(),它自动 injecttraceparent;若必须用原生 client,得手动otel.GetTextMapPropagator().Inject(ctx, otelhttp.HeaderCarrier(req.Header))+req.WithContext(ctx) - gRPC 调用:必须配
otelgrpc.Interceptor()(client 和 server 侧都要),否则只有一层 span - 启动 goroutine 时,传
ctx,不是context.Background();更稳妥的是把span本身作为参数传入,因为span是 thread-safe 的且自带 context - 数据库操作:用
otelpostgresql.Wrap()或otelmysql.Wrap()包装*sql.DB,原生sql.Open不会自动埋点
Span 断裂最隐蔽的三个地方
不是代码没写,而是执行路径绕过了 tracing 生命周期管理。
-
Span.End()必须配对Start(),漏掉会导致 span 卡在 exporter 缓冲区,内存缓慢上涨,几小时后 OOM - 第三方库(如老版本
ent或gorm)回调函数里拿不到有效 span,得手动Inject()补 header - 日志中想打印
trace_id和span_id,不能靠全局变量或中间件塞字段,必须从当前context.Context提取:span := trace.SpanFromContext(ctx),再用span.SpanContext().TraceID().String()
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











