Go 用 OpenTelemetry 打通分布式追踪:从 span 链路到采样策略

test12026-10-161 次阅读

为什么需要分布式追踪

一个请求穿过网关、多个服务和数据库,光看各服务的日志很难还原完整路径。分布式追踪把一次请求标记成一条 trace,里面每个环节是一个 span,串起来就能看到耗时都花在哪。

初始化 Tracer

用 OTLP 把数据导出到 Collector,再由 Collector 转发到 Jaeger 等后端。

package main

import (
    "context"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    "go.opentelemetry.io/otel/semconv/v1.21.0"
)

func newTracerProvider(ctx context.Context) (*sdktrace.TracerProvider, error) {
    exp, err := otlptracegrpc.New(ctx)
    if err != nil {
        return nil, err
    }
    res, _ := resource.Merge(
        resource.Default(),
        resource.NewWithAttributes(
            semconv.SchemaURL,
            semconv.ServiceName("order-service"),
        ),
    )
    tp := sdktrace.NewTracerProvider(
        sdktrace.WithBatcher(exp),
        sdktrace.WithResource(res),
        sdktrace.WithSampler(sdktrace.AlwaysSample()),
    )
    otel.SetTracerProvider(tp)
    return tp, nil
}

在请求里创建 Span

func handleOrder(ctx context.Context, id string) error {
    tracer := otel.Tracer("order")
    ctx, span := tracer.Start(ctx, "handleOrder")
    defer span.End()

    span.SetAttributes(attribute.String("order.id", id))

    // 子 span:查库
    ctx, dbSpan := tracer.Start(ctx, "queryDB")
    if err := queryDB(ctx, id); err != nil {
        dbSpan.RecordError(err)
        dbSpan.SetStatus(codes.Error, err.Error())
        return err
    }
    dbSpan.End()

    return nil
}

跨服务传播

HTTP 调用时把 trace 上下文注入请求头,下游服务解析后就能接在同一条 trace 上。

import "go.opentelemetry.io/otel/propagation"

func callDownstream(ctx context.Context, url string) error {
    req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
    otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(req.Header))
    _, err := http.DefaultClient.Do(req)
    return err
}

工程注意点

  • 生产环境别 AlwaysSample,高流量下改成 ParentBased(TraceIDRatioBased(0.1)) 抽样。
  • span 名字要有业务含义,避免用动态参数拼名字,否则后端聚合会爆炸。
  • 错误要 RecordError 并设 status,否则 Jaeger 里看不出失败。
  • BatchSpanProcessor 是异步导出,进程退出前记得 tp.Shutdown 把缓冲刷出去。
T

test1

文章作者

为什么需要分布式追踪 一个请求穿过网关、多个服务和数据库,光看各服务的日志很难还原完整路径。分布式追踪把一次请求标记成...

分类
技术
发布时间
2026-10-16
字数
约 2094 字
阅读
1 次