OpenTelemetry 是当前 Golang 微服务链路追踪的首选,因其整合 OpenTracing 与 OpenCensus、官方维护、生态统一且 Go SDK 成熟;旧方案面临弃用、传播格式不兼容(如 b3 vs w3c)及采样同步难等问题。

为什么 OpenTelemetry 是当前 Golang 微服务链路追踪的首选
因为 OpenTracing 和 OpenCensus 已合并为 OpenTelemetry(OTel),官方维护、生态统一、Go SDK 成熟。继续用老方案会遇到模块弃用、HTTP 传播格式不兼容(如 b3 vs w3c)、采样配置难同步等问题。
关键判断:新项目必须用 go.opentelemetry.io/otel,存量 opentracing-go 项目应逐步迁移,尤其当引入 Istio、Jaeger v1.50+ 或需要对接 AWS X-Ray 时。
如何在 Gin / Echo 中自动注入 trace context 并透传
Gin/Echo 默认不处理 HTTP header 中的 trace 上下文,需手动注册中间件提取 traceparent(W3C 标准)或兼容旧 uber-trace-id。
- 使用
otelhttp.NewHandler包裹 handler 时,它只适用于标准net/http;Gin 需改写为自定义中间件 - 推荐用
otelhttp.WithPropagators显式传入propagation.TraceContext{},否则默认不解析traceparent - 若下游是 Java Spring Cloud,必须启用
b3propagator(propagation.B3{})并确保 header 名为X-B3-TraceId,否则链路断裂
func TraceMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
ctx := c.Request.Context()
// 从 header 提取并注入 span context
prop := propagation.TraceContext{}
ctx = prop.Extract(ctx, propagation.HeaderCarrier(c.Request.Header))
tracer := otel.Tracer("gin-server")
_, span := tracer.Start(ctx, "http-server", trace.WithSpanKind(trace.SpanKindServer))
defer span.End()
c.Next()
if len(c.Errors) > 0 {
span.RecordError(c.Errors.Last().Err)
span.SetStatus(codes.Error, c.Errors.Last().Err.Error())
}
}
}
如何让 gRPC 客户端和服务端自动传递 trace context
gRPC 的 metadata 是透传 trace 的关键载体,但默认不启用 OTel 集成,必须显式包装 grpc.ClientConn 和注册 server interceptor。
立即学习“go语言免费学习笔记(深入)”;
-
otelgrpc.UnaryClientInterceptor()和otelgrpc.UnaryServerInterceptor()是必须的,遗漏任一端都会断链 - 若服务间通过
context.WithValue手动塞 traceID,会导致 span parent 错乱——必须用trace.SpanContextFromContext()获取并注入 - 注意
otelgrpc.WithFilter:高 QPS 场景下可过滤健康检查接口(如/grpc.health.v1.Health/Check),避免埋点拖慢
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()), ) // … s := grpc.NewServer( grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()), )
采样策略怎么配才不会压垮 Jaeger 或 Otel Collector
全量上报在微服务规模超 20 个、QPS > 500 时极易打爆 collector 内存或 Kafka buffer。必须按业务分级采样,而非全局固定率。
- 用
ParentBased采样器:继承上游决策,对入口请求(如 API 网关)用TraceIDRatioBased(0.01),内部调用默认Drop - 对错误路径强制采样:
span.SetStatus(codes.Error, "...")后,collector 可通过 tail-based sampling 补采,但需提前开启tail_samplingpipeline - 避免在代码里硬编码
AlwaysSample—— 测试环境可以,上线即事故
真正容易被忽略的是:HTTP header 大小限制(如 Nginx 默认 4KB)和 gRPC metadata 体积。一个带 10 个 baggage key 的 traceparent 可能超限,导致 context 丢失——baggage 要精简,且优先走异步上报 metric 而非塞进 trace。









