Tempo(Grafana Tempo)是 Grafana 生态中的链路追踪后端,用来接收、存储、查询和关联一次请求跨多个服务产生的 trace 数据。
它关注的问题不是“某个指标是多少”,也不是“某行日志写了什么”,而是一次请求经过哪些组件、每段耗时多少、错误出现在哪个 span 上。
核心问题
分布式系统里,一次用户请求可能经过网关、认证服务、业务服务、消息队列、数据库和第三方 API。只看单个服务的日志,很难回答:
- 请求总耗时由哪一段贡献最多?
- 错误是入口服务产生的,还是下游依赖传播上来的?
- 失败请求和成功请求的调用路径有什么差别?
- 某个 Mimir 指标异常时,能否找到对应的慢请求样本?
Tempo 解决的是 trace 的集中接收、长期保存和查询问题。它通常和 Grafana、Loki、Prometheus、Mimir 配合使用,让 trace 可以和日志、指标互相跳转。
核心对象
| 对象 | 含义 | 工程关注点 |
|---|---|---|
| Trace | 一次完整请求或业务流程的调用树 | trace_id 是否贯穿所有服务 |
| Span | trace 中的一个操作片段,例如一次 HTTP 调用或数据库查询 | 开始时间、结束时间、状态码、属性 |
| Trace ID | 标识整条 trace 的 ID | 要写入日志或作为 exemplar,方便跳转 |
| Span ID | 标识单个 span 的 ID | 用于表达父子关系和调用树 |
| Distributor | Tempo 接收写入的入口组件 | 协议、限流、租户、trace ID 分片 |
| Live-store | 服务最近写入、还没完全落到长期存储的数据 | 查询新 trace 时的可见性 |
| Block-builder | 把 span 组织成块并写入长期存储 | block 大小、flush 周期、失败重试 |
| Query frontend / Querier | 接收查询、切分任务、读取近期和历史数据 | 查询延迟、并行度、缓存 |
| Metrics-generator | 从 span 派生指标的可选组件 | 生成 RED 指标、service graph 或 span metrics |
这里的 trace_id 不是用户 ID,也不是一次 HTTP 连接 ID。它是一次逻辑请求链路的关联键,必须通过 trace context 在服务之间传播。span_id 只标识链路中的一个节点,不能单独代表完整请求。
核心机制
1. 应用先产生 trace
应用通过 OpenTelemetry SDK、框架 instrumentation 或其他 tracing 协议产生 span。典型数据流是:
service A
-> creates root span with trace_id
-> calls service B with trace context
service B
-> creates child span
-> exports spans through OTLP / Jaeger / Zipkin
collector or agent
-> forwards spans to Tempo如果服务之间没有传播 trace context,下游服务会生成新的 trace,Grafana 中看到的就是断裂的调用链。
2. Tempo 接收并组织 span
Tempo 的写入入口接收 OTLP 或其他 tracing 协议请求,校验租户、限流和格式后,按 trace_id 把 span 分发到后续组件。按 trace_id 分片的目标是让同一条 trace 的 span 尽量被组织到可查询的结构里。
在较新的分布式架构中,Tempo 可以用 Kafka 兼容队列作为持久中间层,然后由 block-builder 消费 span、构建列式 block 并写入对象存储;在单体模式中,数据路径更短,组件在同一进程内协作。无论哪种模式,查询都需要同时考虑近期数据和已经写入长期存储的数据。
3. 查询路径组合近期和历史数据
查询时,Grafana 或 API 先把查询发给 Tempo 的查询入口:
Grafana / API
-> query frontend
-> queriers
-> live-store for recent traces
-> object storage for historical traces
-> merged result近期 trace 通常还在 live-store 中,历史 trace 则在对象存储的 block 中。查询前端会把查询拆成多个并行任务,querier 从不同位置读取数据再合并结果。
4. Tempo 通过上下文和其他信号关联
Tempo 本身保存的是 trace 数据,但它和其他信号的关联依赖共同字段:
- 日志里写入
trace_id,Grafana 就可以从 Loki 日志跳到 Tempo trace。 - 指标样本带 exemplar,Grafana 就可以从 Prometheus 或 Mimir 指标点跳到 trace。
- span 属性包含
service.name、HTTP route、status code 等信息,Grafana 就可以按服务、接口和错误状态筛选。
因此 Tempo 的可用性不仅取决于后端部署,还取决于应用 instrumentation 的质量。
工程用途
- 慢请求定位:看一次请求的 waterfall,区分 CPU、数据库、RPC、队列等待和外部依赖耗时。
- 错误传播分析:判断错误最早在哪个 span 出现,后续服务只是被动返回失败还是主动产生新错误。
- 服务拓扑观察:从 span 的父子关系和 service name 推导服务调用关系。
- 指标下钻:从错误率或延迟指标跳到具体 trace 样本,而不是只看聚合趋势。
- 日志关联:用 trace ID 把同一次请求的跨服务日志串起来。
常见观察指标包括 span ingest rate、dropped spans、query latency、object storage error、live-store 可用窗口、trace 搜索命中率和 metrics-generator 生成的 RED 指标。
边界与常见坑
- Tempo 不是 tracing SDK:它接收和查询 trace;应用侧仍需要 OpenTelemetry 或其他 instrumentation 产生 span。
- 有 Tempo 不等于有完整链路:没有 trace context 传播、采样配置过强或某些服务没接入时,trace 会断裂或缺段。
- trace 成功写入不等于立刻可查到全部历史:近期数据、block flush、对象存储和查询索引之间有时间窗口。
- 采样策略会改变事实可见性:被采样丢弃的请求不会出现在 Tempo 中,排障时要结合指标和日志判断总体影响。
- 属性不是越多越好:把请求体、用户隐私、完整 URL 或高基数字段放入 span 属性,会带来成本、隐私和查询问题。
- Tempo 不替代日志和指标:trace 适合解释路径和局部耗时;趋势、容量和阈值仍应由 指标 承担,事件细节仍应由日志补足。