2. 核心概念:Trace、Span、Metrics、Logs、Baggage、Context Propagation

在构建多租户可观测性体系之前,必须统一理解 OpenTelemetry 定义的六大核心概念。这些概念构成了数据采集、关联与传递的语义基础。下面逐一拆解其定义、职责与相互关系。

2.1 Trace 与 Span:分布式追踪的骨架

Trace(追踪)代表一个端到端请求在分布式系统中的完整路径。它由多个 Span(跨度)组成,每个 Span 对应系统中的一个操作单元(如一次数据库查询、一次 RPC 调用)。

  • Trace ID:全局唯一标识一次完整请求,贯穿所有服务。
  • Span ID:标识单个操作单元,父子 Span 通过 ParentSpanId 关联。
  • Span 属性:键值对,记录操作元数据(如 HTTP 方法、状态码、租户 ID)。
  • Span 事件:带时间戳的日志记录,用于标记异常或关键里程碑。
多租户关键点:在 Span 属性中注入 tenant.id 或 tenant.name,即可在 Trace 级别实现租户隔离查询。
Span 生命周期状态:Unset → Ok / Error。错误状态应携带描述性消息,便于快速定位故障租户。

2.2 Metrics:聚合的量化视图

Metrics(指标)是聚合后的数值数据,用于反映系统在时间维度上的状态变化。OpenTelemetry 支持三种基本指标类型:

指标类型 说明 典型场景
Counter 单调递增计数器 请求总数、错误总数
Histogram 值分布统计 请求延迟 P50/P99、响应大小
UpDownCounter 可增减的计数器 活跃连接数、队列深度

Metrics 不包含单个请求的细节,但能提供高层次的健康度与趋势信息。在多租户场景中,通常按租户维度打标签(tenant_id)实现租户级聚合。

2.3 Logs:结构化的事件记录

Logs(日志)是带有时间戳的结构化或非结构化文本记录。OpenTelemetry 将日志视为 LogRecord 对象,包含:

  • Timestamp:事件发生时间。
  • Severity:严重级别(TRACE / DEBUG / INFO / WARN / ERROR / FATAL)。
  • Body:日志正文。
  • Attributes:结构化键值对,可关联 Trace ID、Span ID、租户 ID。
常见误区:不要将日志视为独立孤岛。通过注入 trace_id 和 span_id,日志可以与 Trace 无缝关联,形成“日志→追踪”的快速跳转链路。

2.4 Baggage:跨服务的上下文数据传递

Baggage(行李)是一种键值对集合,随请求上下文跨服务传播。与 Span 属性不同,Baggage 会沿着 Trace 路径传递到所有下游服务。

  • 用途:传递租户 ID、用户角色、请求来源等业务上下文。
  • 传播方式:通过 HTTP 头(baggage)或 gRPC 元数据传递。
  • 注意事项:Baggage 数据会暴露给所有下游服务,避免传递敏感信息(如密码、Token)。
多租户最佳实践:将 tenant.id 放入 Baggage,确保所有下游服务自动继承租户上下文,无需手动传递参数。

2.5 Context Propagation:上下文传播机制

Context Propagation(上下文传播)是 OpenTelemetry 的核心机制,负责将 Trace、Span、Baggage 等信息在服务间传递。它通过以下方式实现:

  1. 创建 Context:在请求入口处创建包含 Trace ID、Span ID、Baggage 的上下文对象。
  2. 注入(Inject):将上下文序列化到传输协议(HTTP 头、gRPC 元数据、消息队列属性)。
  3. 提取(Extract):下游服务从协议中反序列化上下文,恢复 Trace 链路。
  4. 传递(Propagate):在异步调用、线程池、消息队列中保持上下文不丢失。

OpenTelemetry 默认支持 W3C Trace Context 标准(traceparent 和 tracestate 头),同时兼容 Baggage 头。

OpenTelemetry 六大核心概念关系图 Context Propagation Trace Span Metrics Logs Baggage Context Propagation 是中枢,负责在 Trace、Span、Baggage、Metrics、Logs 之间传递上下文

2.6 核心概念对比总结

概念 数据形态 粒度 跨服务传播 多租户用途
Trace 树状结构 请求级 是(通过 Context) 租户请求全链路追踪
Span 操作单元 操作级 否(父子关联) 租户操作耗时与状态
Metrics 聚合数值 时间窗口级 否 租户级 QPS、延迟、错误率
Logs 结构化文本 事件级 否(可关联 Trace) 租户错误日志快速定位
Baggage 键值对 请求级 是(显式传播) 传递租户 ID、角色
Context Propagation 传播机制 基础设施级 是(核心能力) 确保租户上下文不丢失

2.7 上下文传播流程示例

以下是一个典型的多租户请求上下文传播流程:

  1. API 网关:从请求头提取 X-Tenant-ID,创建 Trace 并注入 Baggage(tenant.id=abc123)。
  2. 服务 A:提取上下文,创建子 Span,记录 tenant.id 属性,调用服务 B。
  3. 服务 B:自动继承 Baggage,创建子 Span,记录 Metrics(按租户标签聚合),输出日志(携带 Trace ID)。
  4. 异步处理:通过消息队列传递上下文,确保消费者 Span 正确关联父 Trace。
代码示例:在 Go 中注入租户上下文
import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/baggage"
    "go.opentelemetry.io/otel/propagation"
)

// 在入口处注入租户 ID
func injectTenantContext(ctx context.Context, tenantID string) context.Context {
    member, _ := baggage.NewMember("tenant.id", tenantID)
    bag, _ := baggage.New(member)
    return baggage.ContextWithBaggage(ctx, bag)
}

// 在下游提取租户 ID
func extractTenantID(ctx context.Context) string {
    bag := baggage.FromContext(ctx)
    return bag.Member("tenant.id").Value()
}
多租户上下文传播流程 API 网关 服务 A 服务 B 消息队列 消费者 注入 tenant.id 传播 Baggage 异步发送 消费并恢复上下文 每个服务自动提取 Baggage 中的 tenant.id,注入 Span 属性,确保 Metrics 和 Logs 携带租户标签 异步场景通过消息头传递 W3C Trace Context,保证 Trace 不中断

2.8 多租户上下文传播避坑指南

  • 不要遗漏异步场景:线程池、消息队列、定时任务必须显式传递 Context,否则 Trace 断裂。
  • 不要过度使用 Baggage:只传递必要的业务上下文(如租户 ID),避免传递大量数据导致性能下降。
  • 不要忽略 W3C 标准:使用 traceparent 和 tracestate 头,确保与第三方系统兼容。
  • 不要混淆 Span 属性与 Baggage:Span 属性仅本地可见,Baggage 跨服务传播,按需选择。
推荐实践:在服务入口处统一提取 Baggage 中的租户 ID,并注入到当前 Span 的 Attributes 中,同时传递给 Metrics 和 Logs 的标签系统,实现“一次注入,全局可用”。