2. OpenTelemetry 核心概念回顾:Trace、Span、Metrics、Logs 与 Context 传播
在深入 Mock 依赖隔离与性能优化之前,必须重新校准对 OpenTelemetry(OTel)核心数据模型的理解。OTel 并非简单的“链路追踪工具”,而是一个统一的可观测性数据面标准。本章将聚焦于三大信号(Trace、Metrics、Logs)及其底层支撑机制——Context 传播,为后续的 Mock 注入与性能压测建立共同语言。
2.1 三大信号与 Span 的基石地位
OTel 将可观测性数据归纳为三种信号(Signal),它们共享相同的元数据模型与传播机制:
| 信号 | 核心单元 | 主要用途 | Mock 隔离关注点 |
|---|---|---|---|
| Traces | Span | 请求端到端路径与延迟分布 | Span 创建/结束的 Mock 注入点 |
| Metrics | Instrument(Counter/Histogram) | 聚合统计(QPS、P99、错误率) | 指标采集频率与 Mock 数据隔离 |
| Logs | LogRecord | 结构化事件与异常详情 | 日志上下文与 Trace 关联 |
其中 Span 是 OTel 数据模型的基石。一个 Trace 由一个或多个 Span 组成,每个 Span 代表一个具有开始时间、结束时间、属性(Attributes)、事件(Events)和状态(Status)的工作单元。
关键认知: 在 Mock 依赖隔离场景中,我们主要 Mock 的是 Span 的创建、结束与属性注入过程,而非 Metrics 或 Logs 的聚合逻辑。理解 Span 的生命周期是性能优化的第一步。
2.2 Span 的完整生命周期与属性结构
一个 Span 从创建到结束经历以下阶段,每个阶段都可能成为 Mock 注入的切入点:
- Span 创建(Start):从 Context 中提取父 Span,生成新的 SpanContext,分配 TraceId 与 SpanId。
- 属性注入(Attributes):添加业务标签(如 HTTP 方法、状态码、数据库语句)。
- 事件记录(Events):记录带时间戳的异常或关键日志。
- Span 结束(End):记录结束时间,计算耗时,将 Span 导出到 Processor。
以下是一个典型的 Span 结构示例(Java SDK 伪代码):
Span span = tracer.spanBuilder("processOrder")
.setSpanKind(SpanKind.SERVER)
.setAttribute("order.id", "12345")
.setAttribute("payment.method", "credit_card")
.start();
try (Scope scope = span.makeCurrent()) {
// 业务逻辑
Thread.sleep(100);
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR);
} finally {
span.end();
}
Mock 隔离提示: 在单元测试或集成测试中,我们通常 Mock
tracer.spanBuilder() 返回的 Span 实例,使其不真正创建 ID、不触发导出,从而消除 I/O 开销。但必须保留 makeCurrent() 的 Context 传播行为,否则下游代码无法获取正确的 Trace 上下文。
2.3 Context 传播:连接所有信号的隐形管道
Context 传播是 OTel 最容易被低估的核心机制。它负责在进程内(线程、协程)和进程间(HTTP、gRPC、消息队列)传递 Trace 上下文、Baggage 以及自定义上下文值。
Context 传播的典型流程如下:
2.4 Metrics 与 Logs 的上下文关联
Metrics 和 Logs 虽然不直接参与 Trace 的树形结构,但它们通过 Attributes 和 Context 与 Trace 建立关联:
- Metrics 关联:OTel 的 Exemplar 机制允许在 Histogram 中嵌入 TraceId/SpanId,实现“指标可追溯”。
- Logs 关联:LogRecord 可以携带 SpanContext,将日志行直接挂载到对应的 Span 下。
性能陷阱: 在 Mock 隔离测试中,如果 Metrics 的采集器(如 PeriodicReader)仍然在后台线程中真实运行,即使 Trace 被 Mock,Metrics 的聚合与导出仍会产生 CPU 和内存开销。必须同时 Mock 或禁用 Metrics 的导出管道,才能获得真实的性能基线。
2.5 核心概念对比总结
| 概念 | 数据模型 | 传播方式 | Mock 隔离难度 | 性能影响权重 |
|---|---|---|---|---|
| Trace / Span | 树形结构,ID 驱动 | Context 传播 | 中(需保留 Context) | 高(Span 创建/结束频繁) |
| Metrics | 聚合数据点 | 独立导出管道 | 低(可直接禁用) | 中(聚合计算 + 导出 I/O) |
| Logs | 结构化记录 | 通常独立 | 低(可关闭) | 低(异步写入为主) |
| Context 传播 | 隐式上下文 | ThreadLocal / 协议头 | 高(必须保留) | 低(仅指针传递) |