# exemplar · 三支柱之间那座桥

> 从一条指标曲线，一键跳到那一刻的某条真实 trace——exemplar 是可观测性里最巧妙的设计之一。这一章讲它怎么工作、为什么它「用聚合的便宜换到了个案的入口」，以及三支柱关联的完整图景。

- 作者：David（道雾轩）
- 专栏：指标采集与观测（https://daiw.org/manual/observability.md）
- 最后更新：2026-07-15
- 原文：https://daiw.org/manual/observability/exemplars
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# exemplar · 三支柱之间那座桥

[第 0 部分说过](https://daiw.org/manual/observability/three-pillars)：“**支柱之间的桥和支柱本身一样重要**”。这一章讲最巧妙的那座桥——**exemplar**。

## 它解决什么

<Callout type="warn">
  **一个每天都在发生的场景：**

  你在 Grafana 上看到 P99 延迟在 10:23 有个尖峰。**然后呢？**

  - **指标只告诉你“有个尖峰”** —— [它是聚合的，个案早被抹掉了](https://daiw.org/manual/observability/three-pillars)。
  - **你想去看那一刻的 trace** —— 但**怎么找？** 去 Jaeger 里按时间范围搜？那一分钟里有几万条 trace，**哪条是造成尖峰的那条？**
  - **而且它很可能[根本没被采样](https://daiw.org/manual/observability/sampling)** —— 头部采样下，那条慢的 99% 被丢了。

  **这个“从聚合跳到个案”的鸿沟，是可观测性最恼人的断层。** 你有指标、有追踪，但**它们之间没有桥**——你只能靠时间戳手工对，效率极低。
</Callout>

## exemplar：在指标里挂几个样例

<Callout type="info">
  **exemplar 的思路极简：**在记录指标时，顺便挂上几条“代表性样本”的 trace_id**。**

  ```
  # 一个带 exemplar 的直方图桶([OpenMetrics 格式](https://daiw.org/manual/observability/exposition-format))
  http_request_duration_seconds_bucket{le="2.5"} 1027 # {trace_id="abc123"} 2.3 1719849600
                                                       └── exemplar ──┘  值   时间戳
  ```

  **含义：“这个桶（≤2.5s）里有 1027 个请求，**其中有一个具体的请求**，它的 trace_id 是 abc123，耗时 2.3s，发生在那个时刻。”**

  于是 Grafana 上，那条 P99 曲线的尖峰旁边会出现**一个小点**——点它，**直接跳到那条 trace 的火焰图**。

  **鸿沟被一座桥填平了。**
</Callout>

## 为什么这个设计如此巧妙

<Callout type="info">
  **它同时绕开了两个死结：**

  **① 绕开了[基数](https://daiw.org/manual/observability/defining-metrics)**

  你**不能**把 trace_id 放进[标签](https://daiw.org/manual/observability/defining-metrics)——[那是无限基数，会当场炸掉](https://daiw.org/manual/observability/defining-metrics)。但 exemplar **不是标签**：它是**挂在采样点上的附属数据**，**不参与 series 的身份**。

  **于是：series 数一点没涨，但你有了跳转入口。** 这是个漂亮的规避——**它承认“trace_id 不能索引”，但保留了“trace_id 可以被携带”**。

  **② 绕开了[采样](https://daiw.org/manual/observability/sampling)**

  更妙的是：**exemplar 是从“真实发生过的请求”里挑的**，而且实现通常会**优先挑那些异常的**（慢的、错的）。

  **结合[采样位](https://daiw.org/manual/observability/traces-basics)看**：客户端库可以**因为“这个请求要被记成 exemplar”而强制采样它**——于是**那条你最想看的慢 trace，一定会被存下来**。

  **这就直接解决了[上一章头部采样的致命缺陷](https://daiw.org/manual/observability/sampling)**：1% 随机采样会漏掉慢请求，但 exemplar 机制**保证了“每个桶里至少有一个真实样本被完整保留”**——**尤其是最慢那个桶里的**。

  **“用聚合的便宜，换到了个案的入口”**——你没有存下所有 trace（那太贵），但你为**每个指标的每个桶**留了一条“证据”。**这可能是可观测性里性价比最高的一个机制。**
</Callout>

## 三支柱关联的完整图景

exemplar 只是桥之一。把所有桥放一起看：

```mermaid
flowchart LR
    M["指标<br/>(聚合,便宜)"] -->|"① exemplar<br/>(挂 trace_id)"| T["追踪<br/>(个案,揭示因果)"]
    T -->|"② trace_id 关联"| L["日志<br/>(细节,昂贵)"]
    L -->|"③ trace_id 反查"| T
    T -->|"④ span metrics<br/>(connector 生成)"| M
    style M fill:#3b82f6,color:#fff
    style T fill:#a855f7,color:#fff
    style L fill:#f59e0b,color:#000
```

<Callout type="info">
  **四座桥，方向不同：**

  | 桥 | 怎么实现 | 用途 |
  | --- | --- | --- |
  | **① 指标 → 追踪** | **exemplar** | [看到尖峰，跳到那条慢 trace](https://daiw.org/manual/observability/exemplars) |
  | **② 追踪 → 日志** | **日志里带 `trace_id`** | 看到慢的那一跳，查它当时的日志 |
  | **③ 日志 → 追踪** | 同上，反向 | 看到一条错误日志，查它属于哪个请求 |
  | **④ 追踪 → 指标** | **[spanmetrics connector](https://daiw.org/manual/observability/collector-architecture)** | [从 trace 自动生成 RED 指标](https://daiw.org/manual/observability/red-method) |

  **桥 ② ③ 的关键是：**你的日志里必须有 `trace_id`**。** 这需要：
  - **日志库能拿到当前 context** —— [和上下文传播是同一个问题](https://daiw.org/manual/observability/traces-basics)：[context 断了，trace_id 就注不进日志](https://daiw.org/manual/observability/traces-basics)。
  - **结构化日志** —— `trace_id` 得是个**字段**，不是塞在消息文本里（否则没法索引和跳转）。

  **桥 ④ 值得单说**：[`spanmetrics` connector](https://daiw.org/manual/observability/collector-architecture) 从 traces **自动生成 [RED 指标](https://daiw.org/manual/observability/red-method)**——**你不用在应用里再埋一遍指标**。这是[第 6 部分说“connector 让管道成为图”](https://daiw.org/manual/observability/collector-architecture)的最有价值的实例。

  **注意它的代价**：[生成的指标基数取决于 span 的属性](https://daiw.org/manual/observability/defining-metrics)——如果你的 span name 里带了 ID（`GET /orders/12345`），**生成的指标会当场基数爆炸**。[又一次，基数是万恶之源](https://daiw.org/manual/observability/defining-metrics)。
</Callout>

## 实战：怎么开

<Callout type="warn">
  **exemplar 需要三方配合，少一环就没有：**

  1. **应用侧** —— SDK/客户端库要支持记录 exemplar（Prometheus 的各语言客户端库大多支持，但**默认可能关着**）。
  2. **暴露格式** —— [必须是 OpenMetrics](https://daiw.org/manual/observability/exposition-format)（经典文本格式**表达不了** exemplar）。Prometheus 抓取时要协商到 OpenMetrics。
  3. **Prometheus 侧** —— 要开 `--enable-feature=exemplar-storage`（**exemplar 是单独存的**，有自己的存储和 API）。
  4. **Grafana 侧** —— 数据源里配好“exemplar → 跳到哪个 trace 数据源”的映射。

  **最常见的失败：第 2 步。** 你开了 exemplar，但抓取协商到的是经典文本格式 → exemplar 被静默丢弃 → **Grafana 上啥也没有，还不报错**。

  **另外注意 exemplar 是“少量样本”** —— 每个 series 只留最近的几个，**不是全部**。它的定位是“[给你一个入口](https://daiw.org/manual/observability/exemplars)”，不是“完整记录”。
</Callout>

## 小结与下一章

**exemplar 填平了“从聚合跳到个案”的鸿沟**——[看到 P99 尖峰后，你以前只能按时间戳在几万条 trace 里手工捞，而且那条慢的很可能根本没被采样](https://daiw.org/manual/observability/sampling)。做法极简：[记录指标时顺便挂几条代表性样本的 `trace_id`](https://daiw.org/manual/observability/exposition-format)。**它同时绕开两个死结**：①**绕开[基数](https://daiw.org/manual/observability/defining-metrics)**——[exemplar 不是标签、不参与 series 身份，所以 series 数一点没涨](https://daiw.org/manual/observability/defining-metrics)（**承认 trace_id 不能索引，但保留它可以被携带**）；②**绕开[采样](https://daiw.org/manual/observability/sampling)**——[实现会优先挑异常的样本并强制采样它，于是“最慢那个桶里一定有一条完整 trace”](https://daiw.org/manual/observability/sampling)，**直接解决了头部采样漏掉慢请求的致命缺陷**。**“用聚合的便宜换个案的入口”——性价比最高的机制之一。** 三支柱有**四座桥**：[指标→追踪（exemplar）、追踪↔日志（`trace_id`，前提是**结构化日志 + context 没断**）、追踪→指标（**[spanmetrics connector](https://daiw.org/manual/observability/collector-architecture) 自动生成 RED，但 span name 带 ID 会基数爆炸**）](https://daiw.org/manual/observability/red-method)。实战上 exemplar [需要应用/OpenMetrics 格式/Prometheus feature/Grafana 四方配合](https://daiw.org/manual/observability/exposition-format)，**最常见的失败是格式没协商到 OpenMetrics → 静默丢弃、不报错**。

三支柱的最后一根：日志。下一章收尾第 7 部分。👉 [日志支柱](https://daiw.org/manual/observability/logs-pillar)。

## 参考资料

- [1] OpenMetrics 规范：Exemplars;Prometheus 文档：Exemplars、`--enable-feature=exemplar-storage`。
- [2] OTel Collector contrib：`spanmetricsconnector`；OTel 文档：Exemplars。
- [3] 关联[基数](https://daiw.org/manual/observability/defining-metrics)、[采样](https://daiw.org/manual/observability/sampling)、[三大支柱](https://daiw.org/manual/observability/three-pillars)。
