2. OpenTelemetry 基础:Trace、Span、SpanContext、TraceId、SpanId 核心概念

在将日志与链路关联之前,必须先理解 OpenTelemetry 的数据模型。OpenTelemetry 是 CNCF 孵化的可观测性标准,它定义了 Trace(追踪)、Span(跨度)、SpanContext(跨度上下文)、TraceId(追踪 ID) 和 SpanId(跨度 ID) 五个核心概念。这些概念构成了分布式追踪的骨架,也是后续日志索引关联的基石。

2.1 核心概念速览

概念 英文 一句话定义 在 Loki 关联中的角色
追踪 Trace 一次完整请求从入口到出口的全链路记录 作为日志分组的逻辑边界
跨度 Span 追踪中的一个独立工作单元(如一次数据库查询) 日志中记录的最小可观测单元
跨度上下文 SpanContext 跨进程传递追踪状态的载体 确保日志与追踪在同一请求内对齐
追踪 ID TraceId 全局唯一的 128 位标识符,标记一次追踪 Loki 日志索引的主键字段
跨度 ID SpanId 64 位标识符,标记追踪内的一个跨度 日志中定位具体操作步骤的辅助键
关键认知:TraceId 是“绳子”,把分散在不同服务、不同时间点的日志串成一条完整的请求故事;SpanId 是“绳结”,标记故事中的每个关键动作。

2.2 Trace(追踪)—— 请求的全景图

Trace 代表一个端到端的请求流。当用户点击一个按钮或调用一个 API 时,这个请求可能会经过网关、认证服务、订单服务、支付服务、数据库等多个组件。Trace 就是这些组件交互的完整记录。

  • 生命周期:从请求进入系统开始,到响应返回给客户端结束。
  • 唯一标识:每个 Trace 有且只有一个 TraceId。
  • 结构:由多个 Span 组成树状结构(DAG,有向无环图)。
  • 在 Loki 中的意义:通过 TraceId 可以聚合所有相关日志,实现“一次搜索,全链路可见”。
Trace 与 Span 树状结构关系图 Trace 与 Span 树状结构关系 Trace (TraceId: abc123) Span: API 网关 Span: 认证服务 Span: 订单服务 Span: 鉴权 Span: 查询用户 Span: 创建订单 Span: 扣减库存 Span: 数据库写入 说明:同一 TraceId 下的所有 Span 共享同一个 TraceId,通过 Parent-Child 关系形成树状结构

2.3 Span(跨度)—— 追踪的最小工作单元

Span 是 Trace 的组成部分,代表一个具有开始时间和结束时间的操作。每个 Span 记录了一个特定操作(如 HTTP 请求、数据库查询、消息队列发送)的详细信息。

  • 属性:Span 名称、开始时间戳、结束时间戳、状态(OK/Error)、属性(Tags)、事件(Events)。
  • 关系:Span 之间通过 Parent Span ID 建立父子关系,形成 Trace 的树状结构。
  • 在 Loki 中的意义:每条日志可以关联一个 SpanId,从而精确知道这条日志属于哪个操作步骤。
实战要点:在微服务架构中,一个请求通常会产生 10~50 个 Span。每个 Span 的开始和结束都可以输出一条日志,配合 SpanId 即可在 Loki 中还原出该 Span 的完整执行时间线。

2.4 SpanContext(跨度上下文)—— 跨进程传递的“信使”

SpanContext 是 OpenTelemetry 中用于跨进程传播追踪信息的对象。它包含了在当前 Span 中识别后续 Span 所需的所有元数据。

SpanContext 字段 说明 是否必须
TraceId 当前追踪的全局唯一 ID 是
SpanId 当前 Span 的 ID 是
TraceFlags 追踪选项(如是否采样) 是
TraceState 供应商特定的追踪元数据 否

SpanContext 通过 HTTP 头(如 traceparent)或 gRPC 元数据在服务之间传递。当服务 A 调用服务 B 时,服务 A 将 SpanContext 序列化后附加到请求中,服务 B 反序列化后创建子 Span,从而保证 TraceId 一致。

2.5 TraceId 与 SpanId —— 关联日志的“钥匙”

TraceId 和 SpanId 是 OpenTelemetry 中最核心的两个标识符,也是与 Loki 日志索引关联的直接桥梁。

TraceId(追踪 ID)

  • 格式:128 位,通常表示为 32 个十六进制字符(如 0af7651916cd43dd8448eb211c80319c)。
  • 生成规则:由入口服务(如 API 网关)在请求开始时生成,全局唯一。
  • 在 Loki 中的用法:作为日志的索引标签(Label),实现“一次 TraceId 搜索,召回全链路日志”。

SpanId(跨度 ID)

  • 格式:64 位,通常表示为 16 个十六进制字符(如 a3f4b8c9d0e1f2a3)。
  • 生成规则:每个 Span 在创建时自动生成,在 Trace 内唯一。
  • 在 Loki 中的用法:作为日志的附加字段,用于在 TraceId 结果集中进一步过滤到具体操作。
TraceId 与 SpanId 在日志关联中的角色 TraceId 与 SpanId 在日志关联中的角色 Loki 日志条目 time="2025-01-15T10:30:00Z" level="info" trace_id="abc123..." span_id="def456..." service="order-service" message="订单创建成功" └─ 每条日志携带 TraceId + SpanId 关联过程 1. 提取 TraceId 2. 构建 Loki 索引 3. 按 SpanId 排序 4. 还原请求时间线 还原的追踪视图 Span: API 网关 (5ms) Span: 认证服务 (12ms) Span: 订单服务 (8ms) Span: 数据库写入 (3ms)

2.6 代码示例:在 Go 中创建 Trace 和 Span

以下代码展示了如何使用 OpenTelemetry Go SDK 创建一个 Trace 及其子 Span,并获取 TraceId 和 SpanId:

package main

import (
    "context"
    "fmt"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/trace"
)

func main() {
    // 创建 Tracer
    tracer := otel.Tracer("example-tracer")

    // 创建根 Span(代表一个 Trace 的起点)
    ctx, rootSpan := tracer.Start(context.Background(), "root-operation")
    defer rootSpan.End()

    // 获取 TraceId 和 SpanId
    rootSpanContext := rootSpan.SpanContext()
    traceID := rootSpanContext.TraceID().String()
    spanID := rootSpanContext.SpanID().String()

    fmt.Printf("TraceId: %s\n", traceID)
    fmt.Printf("SpanId: %s\n", spanID)

    // 创建子 Span(代表 Trace 内的一个子操作)
    _, childSpan := tracer.Start(ctx, "child-operation")
    defer childSpan.End()

    childSpanContext := childSpan.SpanContext()
    childSpanID := childSpanContext.SpanID().String()
    fmt.Printf("Child SpanId: %s\n", childSpanID)

    // 输出示例:
    // TraceId: 0af7651916cd43dd8448eb211c80319c
    // SpanId: a3f4b8c9d0e1f2a3
    // Child SpanId: b4c5d6e7f8a9b0c1
}
常见误区:不要将 TraceId 和 SpanId 混淆。TraceId 在整个请求生命周期内保持不变,用于关联所有相关日志;SpanId 在每个操作步骤中变化,用于区分日志属于哪个具体操作。在 Loki 中,TraceId 应作为索引标签,SpanId 作为日志内容字段即可,不需要为 SpanId 建立索引。

2.7 本章小结

  • Trace 是请求的全景图,由多个 Span 组成树状结构。
  • Span 是追踪的最小工作单元,记录单个操作的开始、结束和状态。
  • SpanContext 是跨进程传递追踪信息的载体,包含 TraceId、SpanId 等关键字段。
  • TraceId 是 128 位全局唯一标识,用于在 Loki 中聚合全链路日志。
  • SpanId 是 64 位标识,用于在 Trace 内定位具体操作步骤的日志。