3、OpenTelemetry SDK 安装与初始化:otel-go SDK 安装、TracerProvider 配置、基本 Tracer 与 Span 创建
本章是链路追踪实战的起点。我们将完成 OpenTelemetry Go SDK 的安装、TracerProvider 的初始化配置,并创建第一个 Tracer 与 Span。掌握这些基础操作后,你就能在任何 Go 应用中埋入可观测的追踪点。
3.1 安装 otel-go SDK
OpenTelemetry Go 生态采用模块化设计,核心包与导出器分离。最小安装组合如下:
| 包路径 | 作用 | 是否必须 |
|---|---|---|
go.opentelemetry.io/otel |
核心 API:Tracer、Span、Context 接口 | 是 |
go.opentelemetry.io/otel/sdk |
SDK 实现:TracerProvider、SpanProcessor、Exporter 基类 | 是 |
go.opentelemetry.io/otel/exporters/stdout/stdouttrace |
控制台导出器,用于开发调试 | 推荐 |
go.opentelemetry.io/otel/semconv/v1.26.0 |
语义约定常量(服务名、HTTP 属性等) | 推荐 |
版本锁定建议:生产项目请使用
go get 固定主版本,例如 go.opentelemetry.io/otel@v1.32.0,避免自动升级引入破坏性变更。
在项目根目录执行以下命令安装上述依赖:
go get go.opentelemetry.io/otel@v1.32.0
go get go.opentelemetry.io/otel/sdk@v1.32.0
go get go.opentelemetry.io/otel/exporters/stdout/stdouttrace@v1.32.0
go get go.opentelemetry.io/otel/semconv/v1.26.0
安装完成后,go.mod 中应出现对应版本记录。你可以运行 go mod tidy 清理未使用的依赖。
3.2 TracerProvider 配置
TracerProvider 是追踪系统的入口,负责创建 Tracer 并管理 Span 的生命周期。一个典型的初始化流程包含三个步骤:
- 创建 Exporter:决定 Span 数据输出到哪里(控制台、Jaeger、OTLP 等)。
- 创建 SpanProcessor:决定 Span 何时被导出(同步/异步、批处理)。
- 组装 TracerProvider:将 Processor 和 Resource 注入 Provider。
下面是一个完整的初始化代码,使用控制台导出器:
package main
import (
"context"
"log"
"os"
"os/signal"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)
func initTracerProvider() (*sdktrace.TracerProvider, error) {
// 1. 创建控制台 Exporter
exporter, err := stdouttrace.New(
stdouttrace.WithPrettyPrint(),
)
if err != nil {
return nil, err
}
// 2. 创建 BatchSpanProcessor(异步批量导出)
processor := sdktrace.NewBatchSpanProcessor(exporter)
// 3. 定义 Resource(服务标识)
res, err := resource.New(context.Background(),
resource.WithAttributes(
semconv.ServiceNameKey.String("my-go-service"),
attribute.String("environment", "development"),
),
)
if err != nil {
return nil, err
}
// 4. 组装 TracerProvider
tp := sdktrace.NewTracerProvider(
sdktrace.WithSpanProcessor(processor),
sdktrace.WithResource(res),
sdktrace.WithSampler(sdktrace.AlwaysSample()),
)
// 5. 设置为全局 Provider
otel.SetTracerProvider(tp)
return tp, nil
}
func main() {
tp, err := initTracerProvider()
if err != nil {
log.Fatal(err)
}
// 优雅关闭:确保所有 Span 在进程退出前被导出
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
<-ctx.Done()
if err := tp.Shutdown(context.Background()); err != nil {
log.Fatal(err)
}
}
关键配置说明:
WithSampler(AlwaysSample()):开发阶段采样率设为 100%;生产环境建议改用TraceIDRatioBased(0.1)降低开销。WithResource:必须设置service.name,否则后端无法区分服务来源。Shutdown:必须在进程退出前调用,否则可能丢失未导出的 Span。
3.3 基本 Tracer 与 Span 创建
有了全局 TracerProvider 后,我们可以通过 otel.Tracer(name) 获取 Tracer 实例,然后创建 Span。下面演示一个模拟数据库查询的追踪场景:
func businessLogic(ctx context.Context) {
// 从全局 Provider 获取 Tracer
tracer := otel.Tracer("com.example.business")
// 创建根 Span
ctx, span := tracer.Start(ctx, "handle-request")
defer span.End()
// 给 Span 添加属性
span.SetAttributes(
attribute.String("request.id", "req-12345"),
attribute.Int("priority", 1),
)
// 模拟子操作:数据库查询
dbQuery(ctx, tracer)
// 记录事件
span.AddEvent("request.processed",
attribute.String("result", "success"),
)
}
func dbQuery(ctx context.Context, tracer trace.Tracer) {
// 创建子 Span,自动继承父 Span 的 TraceID
ctx, span := tracer.Start(ctx, "db-query")
defer span.End()
span.SetAttributes(
attribute.String("db.system", "postgresql"),
attribute.String("db.statement", "SELECT * FROM users"),
)
// 模拟耗时
// time.Sleep(50 * time.Millisecond)
}
常见陷阱:
- 忘记调用
span.End()会导致 Span 永远不被导出,造成内存泄漏。 - 子 Span 必须使用父 Span 传递下来的
ctx,否则会丢失父子关系,变成独立 Trace。 tracer.Start第二个参数是 Span 名称,建议使用package.function格式,便于检索。
3.4 初始化与调用流程总览
下图展示了从 SDK 安装到 Span 导出的完整数据流:
3.5 配置参数速查表
下表汇总了 TracerProvider 初始化时最常用的配置项及其推荐值:
| 配置项 | 类型 | 推荐值 / 说明 |
|---|---|---|
WithSampler |
sdktrace.Sampler |
开发:AlwaysSample();生产:TraceIDRatioBased(0.1) |
WithResource |
*resource.Resource |
必须包含 service.name,可选 service.version、deployment.environment |
WithSpanProcessor |
sdktrace.SpanProcessor |
推荐 NewBatchSpanProcessor,吞吐量优于同步处理器 |
WithIDGenerator |
sdktrace.IDGenerator |
默认随机生成;可替换为自定义生成器(如兼容 AWS X-Ray 格式) |
WithRawSpanLimits |
sdktrace.SpanLimits |
默认限制:属性 128 个、事件 128 个、链接 32 个;按需调整 |
3.6 验证初始化是否成功
完成上述代码后,运行程序并发送一个请求。控制台应输出类似下面的 JSON 结构:
{
"Name": "handle-request",
"SpanContext": {
"TraceID": "a1b2c3d4e5f6...",
"SpanID": "12345678...",
"TraceFlags": "01"
},
"Parent": "",
"StartTime": "2025-03-20T10:00:00.000Z",
"EndTime": "2025-03-20T10:00:00.050Z",
"Attributes": [
{"Key": "request.id", "Value": {"Type": "STRING", "Value": "req-12345"}}
],
"Events": [
{
"Name": "request.processed",
"Attributes": [{"Key": "result", "Value": {"Type": "STRING", "Value": "success"}}]
}
]
}
快速验证技巧:如果控制台没有输出,请检查:① 是否调用了
otel.SetTracerProvider(tp);② 进程退出前是否执行了 tp.Shutdown();③ 采样器是否设置为 AlwaysSample()。
至此,你已经完成了 OpenTelemetry Go SDK 的安装、TracerProvider 的配置,并成功创建了第一个 Tracer 与 Span。下一阶段我们将深入 Span 的属性、事件与链路传播机制。