ARCHIVE / INITIALIZING000%
正在载入档案界面SYS.07
Interview Prep

Traces 与 OpenTelemetry

Trace、Span、Context Propagation、采样、Collector、Tempo,以及Planner、Model、Tool与Retrieval的Agent Span设计。

Traces 与 OpenTelemetry

Trace描述一次请求或任务穿过系统的完整路径,Span描述其中一个具有起止时间的操作。Metrics显示checkout-api P99=2.4s时,Trace可以进一步确定耗时来自鉴权、队列、模型、数据库还是某个Tool调用。

1. Trace和Span

Trace: checkout request
└─ Span: HTTP POST /orders                 2.4s
   ├─ Span: auth.verify                    0.1s
   ├─ Span: cart.load                      0.2s
   ├─ Span: db.query                       1.8s
   └─ Span: payment.reserve                0.1s

一个Span通常包含:

字段含义
Trace ID标识整条链路
Span ID标识当前操作
Parent Span ID表示同步调用或层级关系
Name / Kind操作名称和Client、Server、Producer等角色
Start / End开始、结束和Duration
Attributes可查询的结构化属性
StatusUnset、OK或Error语义
EventsSpan内部某个时刻发生的事件
Links与另一个Trace或Span的非父子关系
Resourceservice.name、environment、版本等进程身份

不应将动态信息全部写入Span Name。tool/loki/query?tenant=acme&...会产生无界名称;应使用稳定名称tool.call,并将tool.name=loki记录为有界属性。

2. Parent、Event与Link的选择

  • Child Span:当前操作属于父操作的执行树,例如HTTP Handler调用数据库;
  • Span Event:Span内部一个瞬时事件,例如Retry、Exception或模型产生一个Action;
  • Span Link:存在因果或批处理关系,但不适合单一父子结构,例如Kafka消费者处理批次、任务续跑或Fan-in。

异步队列不应机械保留一个持续数小时的父Span。生产者注入Context,消息携带Trace信息;消费者提取后创建消费Span,并根据语义选择Parent或Link。设计目标是准确表达因果关系,而不是强制所有操作组成单一父子树。

3. Context Propagation

跨进程Trace成立的前提,是上下文随请求传播:

HTTP Header / gRPC Metadata / Message Header
→ trace_id + parent span + sampling decision
→ 下游提取
→ 创建Child Span

W3C Trace Context常见Header:

traceparent: 00-<trace-id>-<parent-id>-<flags>
tracestate: vendor-specific state

传播链中任意一跳忘记Inject或Extract,Trace都会断成两截。常见断点:线程池、异步任务、消息队列、自建HTTP Client、定时任务和跨语言SDK。

Baggage的使用边界

Baggage是随Context传播的键值信息,可以帮助跨信号关联,但它可能跨越多个服务和信任边界:

  • 不放Token、密码、PII;
  • 控制大小和键数量;
  • 明确允许传播的字段;
  • Baggage不会自动变成Span Attribute,需要显式映射;
  • 不传播未经校验和授权的用户输入值。

4. OpenTelemetry的职责

OpenTelemetry提供Vendor-neutral的API、SDK、Semantic Conventions、OTLP协议和Collector生态,用于产生、处理和导出Traces、Metrics与Logs。

业务代码
→ OTel API / 自动或手动Instrumentation
→ SDK:Sampler、Processor、Exporter
→ OTLP
→ Collector:Receive、Process、Export
→ Tempo / Prometheus / Loki或其他后端

OpenTelemetry本身不是存储和查询后端。部署OTel SDK后,仍需配置Collector、后端存储、Data Source和Dashboard。

Collector的价值

Collector把应用与后端解耦,可以统一:

  • Batch与Queue;
  • Retry和Backpressure;
  • Resource Detection;
  • 属性增删、脱敏和过滤;
  • Tail Sampling;
  • 多后端导出;
  • 多租户路由和限流。

Sidecar、DaemonSet或Gateway各有权衡。Gateway集中治理方便,但会成为共享容量和故障域;Agent或DaemonSet降低应用直连压力,但节点资源和配置管理更复杂。

5. 采样

全量Trace成本可能过高,需要Sampling。

Head Sampling

在Trace开始时决定是否采样:

  • 延迟低、实现简单;
  • 不能预先知道最终是否错误或是否成为慢请求;
  • 低概率异常可能未被采样保留。

Tail Sampling

Collector等待Trace的足够多Span后再决定:

  • 可以保留错误、慢请求、关键服务和特定属性;
  • 需要缓存未完成Trace,增加内存和决策延迟;
  • Trace跨多个Collector时,需要保证同一Trace进入一致的决策点;
  • decision_wait过短会得到残缺Trace,过长会增加资源占用。

一种策略:错误和高延迟100%保留,关键租户或新版本提高比例,其余正常请求概率采样。但必须让采样策略本身可观察:接收多少Trace、因何保留、因何丢弃、是否内存不足。

6. Tempo的职责

Tempo是Trace后端,用于接收、存储和查询Trace;Grafana可以通过Tempo Data Source展示Waterfall,并配置Trace to Logs、Trace to Metrics和Service Graph。

Trace通常以对象存储承载大量Span数据,查询依赖Trace ID、属性索引或派生结构。不要期待它像Prometheus一样对任意维度做超低延迟聚合;聚合趋势应产生Metrics,具体调用链留给Trace。

7. Metrics、Trace与Exemplar

Exemplar是在Histogram Bucket等聚合样本旁附带的代表性Trace引用:

P99曲线在10:20突然升高
→ 点击异常点的Exemplar
→ 打开一个属于该Bucket的Trace
→ 找到db.query慢Span

Exemplar支持从“一分钟内整体延迟升高”的聚合信号定位到一个具体慢请求。Exemplar不包含全部请求,也不能保证单个样本代表完整分布;它用于关联聚合指标与具体Trace。

8. Agent Span设计

一次Agent任务可建成:

agent.run
├─ planner.step
│  └─ model.generate
├─ agent.delegate metrics-agent
│  └─ tool.call prometheus.query
├─ agent.delegate log-agent
│  └─ tool.call loki.query
├─ retrieval.search
│  ├─ milvus.search
│  └─ neo4j.query
└─ synthesizer.generate

建议属性:

Span属性
agent.runrun_id、workflow、tenant、stop_reason、success
planner.stepstep_index、selected_action、budget_remaining
model.generateprovider、model、input/output token、cache hit
tool.calltool.name、status、retry、approval、result size
retrieval.searchindex.version、top_k、filter、candidate count
synthesizer.generateevidence count、citation count、schema valid

以下内容要谨慎:

  • Prompt和Completion可能含PII与Secret;
  • Tool参数可能含URL、SQL或用户数据;
  • Observation可能极大;
  • 模型的隐藏思维链不应写入Trace;
  • run_id可作为Trace属性便于查询,但不要复制成Prometheus Label。

记录版本、计数、Hash、Evidence ID和错误分类,原始大对象存受控Evidence Store,Trace里保存引用。

9. Trace Recorder和OTel Trace的关系

二者重叠但不完全相同:

OTel TraceAgent Trace Recorder
关注调用链、耗时和系统诊断关注决策、证据、状态和可重放性
可能采样与按期删除关键任务可能要求完整、持久化
Span/Attribute/Event模型Domain Event、State Snapshot、Evidence引用
主要服务于运维排障还服务于审计、评测和回归

推荐共用trace_id,但分开保留策略:

OTel Span → Tempo:链路与性能
Agent Event → SQL / Object Store:决策与证据
共同字段 → run_id / trace_id / evidence_id

这样既能在Grafana看链路,又能从失败Trace安全构造评测Case,不必要求Tempo永久保存所有Prompt和原始Tool结果。

10. Trace常见故障模式

  • Span状态总是OK,即使操作返回业务错误;
  • Span在异步操作完成前提前结束;
  • 每个Span复制完整Prompt,导致存储成本和隐私风险显著增加;
  • Context跨队列丢失;
  • 时间不同步造成负Duration或瀑布错位;
  • Tail Sampler没看到完整Trace;
  • 服务名随Pod变化,Service Map像细胞分裂;
  • 把异常事件记录了,却忘记设置错误状态和错误类型;
  • Retry各自产生Span,但没有关联Attempt和最终结果。

11. Tracing测试

  • 使用In-memory Exporter断言Span树、属性和状态;
  • 契约测试HTTP、gRPC、Kafka的Context Inject/Extract;
  • 模拟错误、取消、超时和Retry;
  • 验证敏感属性被Processor删除;
  • 检查Trace to Logs查询能命中对应日志;
  • 压测SDK与Collector开销;
  • 故障注入Collector、Exporter和后端;
  • 验证采样策略始终保留关键安全和错误Trace。

12. 高频追问

Trace和日志有什么区别?

Trace提供一次请求跨组件的结构与耗时;日志记录某个时刻的具体事件。Trace用于定位耗时步骤,日志通常用于解释该步骤缓慢或失败的原因。

为什么不能所有Span都100%保存?

高流量系统的存储、网络和查询成本可能不可接受。应结合业务风险做Head/Tail Sampling,并完整保留错误、慢请求和关键操作。

OpenTelemetry和Tempo是什么关系?

OpenTelemetry负责产生、传播、处理和导出Telemetry;Tempo负责存储和查询Trace。它们分别处在采集标准和后端存储层。

多Agent之间如何串Trace?

委派消息传播Trace Context,每个Agent创建自己的Span;同步委派用父子关系,异步、续跑或Fan-in可使用Span Link,同时记录角色、任务ID和Handoff版本。

参考资料