Go微服务分布式追踪推荐用OpenTelemetry SDK,需初始化全局TracerProvider并配置Exporter与采样策略,通过HTTP中间件和gRPC拦截器自动传播上下文,并支持自定义Span记录业务细节。

在 Go 微服务架构中,实现请求调用链追踪(Distributed Tracing)核心是统一传播 Trace ID 和 Span ID,并将各服务的调用片段(span)上报到集中式追踪后端(如 Jaeger、Zipkin 或 OpenTelemetry Collector)。Go 生态推荐使用 OpenTelemetry Go SDK,它已成事实标准,替代了旧的 OpenTracing + OpenCensus 双轨方案。
1. 初始化全局 Tracer 并配置 Exporter
启动时创建并设置全局 TracerProvider,指定 exporter(如 Jaeger、OTLP)和采样策略。建议生产环境使用概率采样(如 1%),避免性能与存储压力。
- 安装依赖:
go get go.opentelemetry.io/otel、go.opentelemetry.io/otel/exporters/jaeger(或otlp) - 初始化 Jaeger exporter 示例:
// 创建 exporter
exp, err := jaeger.New(jaeger.WithAgentEndpoint(jaeger.WithAgentHost("localhost"), jaeger.WithAgentPort(6831)))
if err != nil { panic(err) }
// 构建 TracerProvider,启用批量上传和上下文传播
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exp),
sdktrace.WithSampler(sdktrace.AlwaysSample), // 开发期用,生产换为 sdktrace.TraceIDRatioBased(0.01)
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
2. HTTP 中间件自动注入/提取 Trace 上下文
所有入站 HTTP 请求需从 header(如 traceparent)中提取 span 上下文;出站请求需将当前 span 的 context 注入 header。用中间件统一处理,避免每个 handler 手动操作。
- 入站:使用
otelhttp.NewHandler包裹 handler,自动提取、创建 server span - 出站:使用
otelhttp.NewClient包裹 http.Client,自动注入traceparentheader
// 入站示例
http.Handle("/api/order", otelhttp.NewHandler(http.HandlerFunc(orderHandler), "POST /api/order"))
// 出站示例
client := otelhttp.NewClient(http.DefaultClient)
resp, _ := client.Get("http://user-svc/users/123")
3. gRPC 服务集成追踪
gRPC 使用拦截器(interceptor)实现类似逻辑。OpenTelemetry 提供了开箱即用的拦截器:
立即学习“go语言免费学习笔记(深入)”;
- 服务端:用
otelgrpc.UnaryServerInterceptor拦截请求,生成 server span - 客户端:用
otelgrpc.UnaryClientInterceptor拦截调用,注入 context
// gRPC Server 启动时
server := grpc.NewServer(
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
)
// gRPC Client 连接时
conn, _ := grpc.Dial("user-svc:50051",
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()),
)
4. 自定义 Span 记录业务逻辑细节
在关键路径(如 DB 查询、外部 API 调用、耗时计算)中手动创建子 span,添加属性(attribute)、事件(event)和错误标注,提升可分析性。
- 用
tracer.Start(ctx, "db.query")创建新 span,返回带 context 的新 ctx - 用
span.SetAttributes(attribute.String("sql", query))记录元数据 - 发生错误时调用
span.RecordError(err)并设span.SetStatus(codes.Error, err.Error())
ctx, span := tracer.Start(r.Context(), "process-order")
defer span.End()
span.SetAttributes(
attribute.String("order.id", orderID),
attribute.Int("item.count", len(items)),
)
if err := chargePayment(ctx, orderID); err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, "payment failed")
return
}










