2. OpenTelemetry 核心概念:Signal、Span、Trace、Context Propagation、Exporter
在深入 Go 项目慢查询与链路追踪实践之前,必须先建立对 OpenTelemetry(简称 OTel)核心概念的准确理解。这些概念构成了可观测性的数据模型与传输基础,是后续所有代码实现与问题排查的基石。
2.1 核心概念总览
OpenTelemetry 将可观测性数据抽象为三大类 Signal(信号),每一类信号解决一个维度的观测问题。下表列出了三类信号及其在慢查询场景中的典型用途:
| Signal(信号) | 作用域 | 慢查询场景用途 |
|---|---|---|
| Traces(链路) | 请求端到端路径 | 追踪一条 SQL 从入口到数据库的完整调用链,定位慢在哪一步 |
| Metrics(指标) | 聚合统计 | 采集数据库连接池大小、P99 延迟、QPS,发现慢查询趋势 |
| Logs(日志) | 离散事件 | 记录慢 SQL 原文、执行计划、错误堆栈,辅助根因分析 |
2.2 Span:链路追踪的基本单元
Span 是链路追踪中最小的可观测工作单元。每一个 Span 代表一个操作片段,例如一次数据库查询、一次 RPC 调用、一个函数执行。Span 携带了描述该操作的全部元数据。
一个 Span 必须包含以下关键字段:
- Span Name:操作名称,例如
SELECT * FROM orders或HTTP GET /api/users。 - Span ID:全局唯一标识符,用于在 Trace 内定位该 Span。
- Trace ID:所属 Trace 的全局唯一标识符。
- Parent Span ID:父 Span 的 ID,用于构建父子层级关系。根 Span 无父 ID。
- Start Timestamp / End Timestamp:操作开始与结束时间,用于计算延迟。
- Attributes(属性):键值对形式的元数据,例如
db.system="postgresql"、db.statement="SELECT ..."。 - Status:操作结果状态,包括
Unset、Ok、Error。 - Events(事件):Span 生命周期内的带时间戳的注解,例如记录异常堆栈。
db.statement(SQL 原文)、db.duration(实际执行耗时)、db.rows_affected(影响行数),以便后续快速筛选慢 Span。
2.3 Trace:Span 的有向无环图
Trace 是一个请求从入口到出口的完整调用链,由一组具有父子关系的 Span 组成。这些 Span 通过 Trace ID 关联,并通过 Parent Span ID 形成树状结构(有向无环图)。
下图展示了一个典型的慢查询 Trace 结构:
2.4 Context Propagation:跨进程传递追踪上下文
Context Propagation(上下文传播) 是链路追踪能够跨越服务边界的关键机制。它确保一个 Trace 内的所有 Span 能够通过相同的 Trace ID 关联起来,即使这些 Span 分布在不同的进程、不同的机器甚至不同的数据中心。
传播过程分为两个阶段:
- 注入(Inject):在发送端(例如 HTTP 客户端、gRPC 客户端),将当前 Span 的上下文(Trace ID、Span ID、Baggage 等)序列化到请求的传播载体中(例如 HTTP Header、gRPC Metadata)。
- 提取(Extract):在接收端(例如 HTTP 服务端、gRPC 服务端),从传播载体中反序列化出上下文,并创建新的子 Span,将其父 Span ID 设置为提取到的 Span ID。
OpenTelemetry 定义了标准的传播格式,最常用的是 W3C Trace Context,通过 traceparent 和 tracestate 两个 HTTP Header 传递上下文。
context.WithValue 或 otel.GetTextMapPropagator().Inject 显式传播。
2.5 Exporter:将数据送出进程
Exporter(导出器) 负责将采集到的 Span、Metrics、Logs 数据发送到后端存储或分析系统。OpenTelemetry 采用插件化架构,支持多种 Exporter,开发者可以根据基础设施选择。
下表列出了 Go 项目中常用的 Exporter 及其适用场景:
| Exporter 名称 | 协议/方式 | 适用场景 |
|---|---|---|
| OTLP Exporter | gRPC / HTTP | 发送到 OpenTelemetry Collector,再由 Collector 转发到后端(推荐生产方案) |
| Jaeger Exporter | Thrift / gRPC | 直接发送到 Jaeger 后端,适合中小规模或快速原型 |
| Zipkin Exporter | HTTP (JSON) | 兼容 Zipkin 生态,适合已有 Zipkin 基础设施的团队 |
| Prometheus Exporter | HTTP (Metrics) | 仅用于 Metrics 信号,暴露 /metrics 端点供 Prometheus 抓取 |
| Stdout Exporter | 标准输出 | 开发调试阶段,直接打印到控制台查看原始数据 |
Exporter 的配置通常在应用初始化时完成。以下是一个 Go 中使用 OTLP Exporter 的典型代码片段:
import (
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
)
func initExporter() (*otlptrace.Exporter, error) {
// 创建 OTLP gRPC 客户端,默认连接 localhost:4317
client := otlptracegrpc.NewClient(
otlptracegrpc.WithInsecure(),
otlptracegrpc.WithEndpoint("otel-collector:4317"),
)
exporter, err := otlptrace.New(ctx, client)
if err != nil {
return nil, err
}
return exporter, nil
}
2.6 概念关系总结
下图展示了 Signal、Span、Trace、Context Propagation 与 Exporter 之间的整体关系: