4. 创建基础 TracerProvider:初始化、Resource 与 Propagator

在 OpenTelemetry Go SDK 中,TracerProvider 是整个追踪体系的起点。它负责创建 Tracer、管理 Span 的导出行为,并承载服务标识与上下文传播策略。本章将手把手搭建一个生产可用的基础 TracerProvider,涵盖三个核心步骤:初始化 Provider、配置 Resource 属性、设置默认 TextMapPropagator。

4.1 初始化 TracerProvider

OpenTelemetry Go SDK 通过 sdktrace.NewTracerProvider() 创建 Provider 实例。初始化时通常需要指定 SpanProcessor 与 Exporter,但在基础阶段,我们可以先使用 Noop 导出 或 内存导出 来验证链路结构。

关键概念: TracerProvider 是全局唯一的追踪工厂。每个服务只应初始化一次,并在进程生命周期内复用。

以下代码展示了最简初始化方式:

import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/sdk/trace"
)

func initTracerProvider() *trace.TracerProvider {
    // 使用默认配置创建 TracerProvider
    tp := trace.NewTracerProvider(
        // 后续会添加 Resource、Processor 等选项
    )
    // 设置为全局 TracerProvider
    otel.SetTracerProvider(tp)
    return tp
}
初始化选项 作用 推荐场景
trace.WithSampler(sampler) 设置采样策略(如 AlwaysSample、RatioBased) 控制数据量,生产环境必配
trace.WithSpanProcessor(processor) 注册 Span 处理器(如 BatchSpanProcessor) 异步导出到 Collector 或后端
trace.WithResource(resource) 绑定服务标识元数据 区分不同服务/环境
trace.WithIDGenerator(idGen) 自定义 TraceID/SpanID 生成器 兼容已有追踪系统

4.2 配置 Resource 属性

Resource 是描述服务运行环境的元数据集合,每个 Span 都会携带这些属性。OpenTelemetry 定义了标准资源属性(如 service.name、service.version、host.name),也支持自定义标签。

配置 Resource 的推荐方式是通过 resource.New() 合并多个来源:

import (
    "go.opentelemetry.io/otel/sdk/resource"
    semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
)

func newResource() *resource.Resource {
    r, _ := resource.New(
        context.Background(),
        resource.WithAttributes(
            semconv.ServiceNameKey.String("go-web-demo"),
            semconv.ServiceVersionKey.String("1.0.0"),
            semconv.DeploymentEnvironmentKey.String("production"),
        ),
        resource.WithHost(),
        resource.WithOS(),
        resource.WithContainer(),
    )
    return r
}
最佳实践: 始终使用 semconv 包中的常量键名,确保跨服务语义一致性。自定义属性建议加命名空间前缀,如 team.owner。

将 Resource 注入 TracerProvider:

tp := trace.NewTracerProvider(
    trace.WithResource(newResource()),
)

4.3 设置默认 TextMapPropagator

Propagator 负责在服务间传递追踪上下文。OpenTelemetry 支持多种传播协议,最常用的是 W3C TraceContext(traceparent / tracestate 头)。

设置全局 Propagator 的代码:

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

func initPropagator() {
    // 组合 W3C TraceContext 与 Baggage
    otel.SetTextMapPropagator(
        propagation.NewCompositeTextMapPropagator(
            propagation.TraceContext{},
            propagation.Baggage{},
        ),
    )
}
Propagator 类型 HTTP Header 适用场景
TraceContext{} traceparent / tracestate W3C 标准,推荐默认
Baggage{} baggage 传递业务上下文(如用户ID)
JaegerPropagator{} uber-trace-id 兼容 Jaeger 旧版
注意: 如果服务同时接收外部请求并发出下游调用,必须确保 Propagator 在 HTTP 中间件和 HTTP Client 中一致使用,否则链路会断裂。

4.4 完整初始化流程

将上述三个步骤整合为一个函数,形成基础 TracerProvider 的完整初始化:

func InitOpenTelemetry() (*trace.TracerProvider, error) {
    // 1. 创建 Resource
    res, err := resource.New(
        context.Background(),
        resource.WithAttributes(
            semconv.ServiceNameKey.String("go-web-demo"),
            semconv.ServiceVersionKey.String("1.0.0"),
        ),
        resource.WithHost(),
    )
    if err != nil {
        return nil, err
    }

    // 2. 创建 TracerProvider
    tp := trace.NewTracerProvider(
        trace.WithResource(res),
        // 生产环境应替换为 BatchSpanProcessor
        trace.WithSpanProcessor(trace.NewSimpleSpanProcessor(exporters)),
    )

    // 3. 设置全局 Provider 和 Propagator
    otel.SetTracerProvider(tp)
    otel.SetTextMapPropagator(
        propagation.NewCompositeTextMapPropagator(
            propagation.TraceContext{},
            propagation.Baggage{},
        ),
    )

    return tp, nil
}

4.5 基础架构图

下图展示了 TracerProvider 初始化后,在 Go Web 服务中的核心位置与数据流向:

TracerProvider 初始化架构 TracerProvider 初始化与链路数据流 TracerProvider Resource (service.name, version) TextMapPropagator (TraceContext) SpanProcessor (Batch/Simple) Exporter (OTLP / Console) HTTP 请求 (traceparent) 下游服务 (传播上下文) 初始化顺序:Resource → TracerProvider → Propagator → 全局注册

4.6 初始化检查清单

  • Resource 完整性: 至少包含 service.name 和 service.version,建议添加 deployment.environment。
  • Propagator 一致性: 全局 Propagator 必须与 HTTP 中间件和 HTTP Client 使用的传播器一致。
  • Provider 生命周期: 在 main() 或服务启动阶段初始化,并在进程退出前调用 Shutdown() 刷新缓冲。
  • 采样策略: 生产环境不要使用 AlwaysSample,推荐 ParentBased(TraceIDRatioBased(0.1))。
验证方法: 初始化后,通过 otel.GetTracerProvider() 获取全局 Provider,调用 Tracer("test").Start() 创建 Span,检查 Span 的 Resource 属性是否包含预期标签。