# Metric、Log、Trace、Profiling 联合分析研究报告（图解与案例版）

版本：v1.6.1  
更新日期：2026-09-20  
适用场景：四类可观测系统已独立建设，希望在保留存量投资的基础上改善排障。  
研究依据：27 项官方规范与产品文档，来源见文中链接和文末资料表。  
配套文档：《联合分析数据契约与治理规范》v1.3 final（字段级定义、状态取值、证据契约 Schema、查询预算、验收指标清单），其载荷契约版本为 `contract_version = 2.0`（已冻结）。

| 版本 | 日期 | 更新概要 |
| --- | --- | --- |
| v1.0 | 2026-09-17 | 分析行业实践、实体模型、OTel 采用边界、演进路线与验收 |
| v1.1 | 2026-09-17 | 重写阅读主线；增加 7 张 Mermaid 图、贯穿式故障案例、字段映射和查询示例；将详细工程约束移至附录 |
| v1.2 | 2026-09-19 | 评审后修订：修正主案例时间线矛盾；补 Exemplar 留存与 Profiling 语言支持两项工程约束；新增存量 APM 并存期身份策略、注册表归属、智能体消费三节；分层表述 OTel 状态；修正三处引用对应性 |
| v1.3 | 2026-09-20 | 架构评审意见修订：6.3 改为以 Canonical Trace Context 为核心并区分实体映射与请求级桥接；6.2 结果状态由单一枚举改为四维契约；新增第 9 章安全、权限与治理；新增 2.3 上下文与变更数据面；变更事件升为一级诊断维度（4.2）；四层结果升级为五级证据等级模型（4.4）；7.3 由“单一写入口”改为“统一治理入口＋多权威源”；EXACT 错误关联设为阻断项；新增 4 张图；字段级内容拆分至配套分册 |
| v1.4 | 2026-09-20 | 终版审查收口：修正证据等级前两级的升级条件（图 6）；权威源由“业务归属”等笼统说法拆至字段级，消除 2.3/7.3/附录 A.1 与分册间的矛盾；canonical 宣告门槛的判定单位由 Agent 数量改为链路上的实际传播边界；修正 2.3 关于身份字段来源的过满表述；安全章节的鉴权与自主账号原则去绝对化；补充四维状态的归属层级说明；拒答率纳入同步验收 |
| v1.5 | 2026-09-20 | 契约冻结前收口：6.2 第四维由“证据记录的确定性”改为“关联边的确定性”；7.3 冲突示例拆分为表面冲突与真实冲突，并修正“冲突即权威性不同”的过度简化；7.3 事实供给改用字段名；附录 B.1 关联准确率的统计单位改为关联边并按关联类型分别统计 |
| v1.6 | 2026-09-20 | 冻结前副本同步：6.2 枚举表补齐 `query_status` 与 `completeness` 的 `UNKNOWN` 取值并说明其降级语义；6.2 关联边示例修正为同源表述并补充不可传递复合规则；附录 B.2 自主智能体反例改为与 9.2 节一致的条件式授权表述。本版起主报告内容冻结 |
| v1.6.1 | 2026-09-20 | 补丁版，无内容扩展：6.2 复合边状态由“按最弱环节取值”改为“不得高于最弱环节”，与分册同步消除“上游均精确即复合精确”的误读 |

**先给答案：不用先把四个平台全部改成 OTel。可以先让各平台认识同一批服务和实例，再补齐同一次请求的标识，逐步把指标、日志、调用链和代码采样连起来。**

“实体模型”主要解决**这是谁的数据**；请求上下文解决**这是哪一次操作的数据**；OTel 提供一套可复用的公共约定和采集组件。它们共同改善分析条件，但根因仍需要证据验证。

全文术语约定：**Profiling** 指持续采样这一行为，**Profile** 指采样产生的数据；**实体模型**指通用概念，**企业实体模型**特指企业自建、承载别名映射与历史关系的那一份实现。

> **示例说明：**本文订单服务、平台组合、ID、时刻、请求耗时、采样数和代码均为教学构造，来源为本文设计，不是某企业事故记录，也不是实测结果。`T7`、`S2`、`P17` 等均是方便阅读的缩写，不是可直接发送的标准 Trace/Span/Pod ID。涉及业界事实的内容另附官方来源。

建议先读第 1—5 节理解机制，再读第 6—9 节安排实施与治理；需要设计评审和验收时使用附录。字段级契约、取值定义和完整指标清单不在本文，见配套分册——本文负责方法论与决策依据，分册负责实现口径，两者独立版本迭代。

## 1. 先看一个具体问题：订单服务为什么变慢了？

### 1.1 四类信号各自能告诉我们什么

假设订单服务在 10:00 发布 v2.4，10:05 收到延迟告警。已有 Prometheus 指标、Elasticsearch 日志、既有 APM 调用链，以及独立 Profiling 平台。这个组合只是例子，不是产品选型建议。

| 信号 | 通俗理解 | 案例中的一条信息 | 仅靠这条信息还不知道什么 |
| --- | --- | --- | --- |
| Metric（指标） | 统计结果：慢了多少、多少请求失败 | 最近 5 分钟 P99 从约 200 ms 升至约 1,900 ms | 哪个请求、哪段代码造成问题 |
| Log（日志） | 程序记下的具体事件 | `coupon rule reloaded`，优惠规则被重新加载 | 它是否属于那个慢请求 |
| Trace（调用链） | 一次请求经过哪些操作、各用多久 | 请求 T7 耗时 1,860 ms，其中算优惠耗时 1,800 ms | 算优惠内部是在计算、等待锁还是等待别的资源 |
| Profiling（性能剖析） | 采样观察代码在哪里消耗 CPU、分配内存或等待 | 一份 CPU Profile 中大量样本落在规则解析函数 | 这些样本是否属于请求 T7，还是其他并发任务 |

P99 是“第 99 百分位耗时”，描述一批请求的分布；Trace 中的 1,860 ms 描述一次具体请求，两者不是同一个统计量。CPU Profile 也不能直接解释锁等待，需要对应的锁或阻塞观测。[S17：分布与分位数](https://prometheus.io/docs/practices/histograms/)、[S14：Profiling 类型](https://grafana.com/docs/pyroscope/latest/introduction/profiling-types/)。

### 图 1：同一个问题，四种观察角度

```mermaid
flowchart TB
    U["用户感受：提交订单变慢"] --> M["Metric：P99 升至约 1,900 ms"]
    U --> L["Log：优惠规则被重新加载"]
    U --> T["Trace：算优惠用了 1,800 ms"]
    U --> P["Profile：规则解析出现 CPU 热点"]
    M --> Q["需要核对：同一服务？同一实例？同一次请求？"]
    L --> Q
    T --> Q
    P --> Q
    Q --> H["待验证假设：新版本反复解析优惠规则"]
```

**读图要点：**四条信息都出现了，还不能直接宣布根因。接下来要把“可能相关”逐步变成“有依据地相关”。不要求每次故障都用齐四种信号；如果错误日志与配置差异已经足以定位，就不必强行加入 Profiling。

## 2. 第一步：让四个平台认识同一个服务

### 2.1 改造前：同一个服务有四种叫法

| 原平台 | 原始字段示例 | 原平台实际知道的对象 |
| --- | --- | --- |
| 指标 | `job=order-api, pod_uid=P17` | 名为 order-api 的抓取目标及 Pod |
| 日志 | `app=trade-order, pod_uid=P17, container_id=C17` | 某容器中的 trade-order 应用 |
| APM | `service=OrderService, instance=I17` | 名为 OrderService 的实例 |
| Profiling | `application=order-java, host_id=H3, pid=731, process_start=10:01` | 主机 H3 上这次启动的进程 |

这些名称看起来相似，但不能靠字符串相似就合并。服务目录、发布记录和运行信息确认它们都属于“生产环境中的订单服务”之后，建立如下映射。

**映射工作量取决于源端是否已有结构化资源属性，差异很大，不应按平台个数平均估算。**存量自研 APM 常见两类形态：

| 源端形态 | 典型表现 | 对齐工作的性质 | 相对工作量 |
| --- | --- | --- | --- |
| 已有扁平资源属性块 | 报文顶层带独立的元数据块，承载 region/zone/应用名/集群/机房/主机 IP 等字段 | 本质是**字段映射**：把已有属性对到统一实体的对应位置 | 低，可脚本化 |
| 属性混在标签里 | 早期 Zipkin 风格扩展，用 `client.*` 之类前缀把 app/region/pod/container 塞进 tags | 需要**从标签中反解**，还要处理前缀不一致、缺字段、历史写法 | 高，需逐版本核对 |

前者已经接近实体描述符的雏形，可以直接复用；后者要先做一轮清洗和补全。在第 7.1 节估算第②步工期时，应按形态分别计量。

| 标准对象 | 本例统一身份 | 映射依据 |
| --- | --- | --- |
| 逻辑服务 | `svc-042` | 服务目录中的稳定服务 ID |
| 本次部署 | `dep-240` | 发布系统确认是 prod 环境、v2.4 的此次部署 |
| 运行实例 | `I17` | 实例注册和运行记录 |
| 进程执行实例 | `proc-731-A` | H3 的启动周期、PID 731、10:01 启动时间 |
| 承载资源 | 容器 C17、Pod P17、主机 H3 | 编排控制面和运行时的归属记录 |

其中 `svc-042` 是本文假设的企业内部服务 ID，不是 OTel 规定的字段。一个服务可对应多个部署和实例，不能把所有身份压成一个 ID。

注意时间关系：v2.4 于 10:00 发布，因此 `proc-731-A`（10:01 启动）才可能是 v2.4 的进程。同一服务此时仍有未升级的 v2.3 部署 `dep-239` 在运行，其进程 `proc-655-A`（09:20 启动、Pod P11）将在第 4 节充当对照组。**把 10:00 之前启动的进程归入 v2.4 部署，是实体建模中最典型的错误之一**，第 6.1 节会说明为什么必须按事件发生时间而不是按当前归属来判定。

### 图 2：实体模型就是一份可核验的“身份及关系表”

```mermaid
flowchart LR
    M["指标：order-api"] --> S["订单服务 svc-042"]
    L["日志：trade-order"] --> S
    T["APM：OrderService"] --> S
    P["Profile：order-java"] --> S
    S -->|"本次发布"| D["prod 部署 dep-240 / v2.4"]
    D -->|"包含"| I["运行实例 I17"]
    I -->|"本次进程"| R["proc-731-A"]
    R -->|"运行在"| C["容器 C17 / Pod P17"]
    C -->|"调度在"| H["主机 H3"]
```

**读图要点：**左边解决“不同叫法是否指向同一服务”；右边解决“服务当时运行在哪些实例和资源上”。Profile 的进程信息必须沿右边关系定位，不能仅凭应用名称归到某个实例。

这里需要先厘清三个常被混用的概念，否则评审中很容易把实体模型误解为服务拓扑：**实体模型**定义“对象是谁”，**关系模型**定义“对象之间在某一时间段如何关联”，**拓扑**是关系模型在某一时刻或某个时间窗口下的一种视图。本文讨论的是前两者；拓扑图是它们的产物，而不是它们的替代。

OTel 也在定义实体身份与描述属性；服务语义特别要求实例身份明确，并提醒采集器不能在无法确定实例时随意填入 `service.instance.id`。[S7：Entity Data Model](https://opentelemetry.io/docs/specs/otel/entities/data-model/)、[S15：服务与实例语义](https://opentelemetry.io/docs/specs/semconv/resource/service/)。

### 2.2 改造后：查一次“订单服务”，自动翻译为四种查询

用户在工作台选择：**prod → 订单服务 → 实例 I17 → 10:04—10:06**。

下面是查询意图示例，不是可复制执行的产品语法。跨平台查询仍必须执行各源权限检查。

| 工作台动作 | 适配器翻译后的查询意图 |
| --- | --- |
| 查看指标 | 查 `job=order-api`、`pod_uid=P17`、相同环境与时间范围 |
| 查看日志 | 查 `app=trade-order`、`container_id=C17`、相同环境与时间范围 |
| 查看 Trace | 查 `service=OrderService`、`instance=I17`、相同时间范围 |
| 查看 Profile | 查 `host_id=H3`、`pid=731`、启动时间 10:01、相同时间范围 |

**获得的效果：**工程师不用再记四种名字和手工拷贝 IP，能一次取得同一对象附近的证据。底层数据库仍各自保存原始数据，映射表和查询适配器负责翻译。Grafana 的跨数据源 Correlations 公开提供了“用一处字段构造另一处查询”的机制。[S1：Grafana Correlations](https://grafana.com/docs/grafana/latest/administration/correlations/)。

**此时的边界：**I17 在两分钟内可能处理很多请求。实体模型能找到这一批数据，尚不能指出哪条日志、哪些 Profile 样本属于 T7。

### 2.3 支撑实体关联的不是遥测，而是上下文与变更数据面

回看 2.1 节那张映射表：Metric 带着 `pod_uid`、Log 带着 `container_id`、APM 带着 `instance`、Profile 带着 `host_id/pid/process_start`——身份字段确实可以随遥测一起携带。但真正支撑关联的不止这些字段本身，还包括它们背后的部署 `dep-240`、版本 v2.4、以及“I17 在 10:04—10:06 这段时间运行在 H3 上”这类历史有效关系。

关键区别在于：**身份和生命周期信息可以随遥测携带，但它们的权威归属与历史有效关系不能仅依赖遥测自身确认**，仍须由服务目录、发布平台、编排控制面和运行时这些事实源校验。遥测里的 `pod_uid` 只说明“上报时自称是这个 Pod”，它不能回答这个 Pod 当时属于哪个部署、哪个版本、从什么时候起生效到什么时候失效。

也就是说，四类遥测信号提供的是“发生了什么”，而权威回答“这是谁的数据、当时它长什么样”的，是另一类数据。本文把它统称为**上下文与变更数据面（Context / Change Data Plane）**，至少包括：

| 来源 | 供给的字段 |
| --- | --- |
| 服务目录 | 逻辑服务身份（`service_identity`）、业务服务责任人（`business_service_owner`）、环境 |
| CMDB / 资产系统 | 资产台账与资产责任人（`asset_owner`）、成本中心（`cost_center`）、机房与网络位置（`physical_location`）、依赖登记 |
| Kubernetes / 云资源控制面 | Pod、容器、节点的生命周期与有效期 |
| 发布平台 | 部署批次、版本、灰度范围、发布时刻 |
| 配置中心 | 配置项版本与生效时间 |
| 变更事件源 | 扩缩容、路由调整、数据库与中间件变更（见 4.2 节） |
| 运行时 | 进程身份（`process_identity`）、启动时间、实际加载的制品（`running_artifact`）、连接关系 |

### 图 3：上下文与变更数据面是实体关联的供给侧

```mermaid
flowchart TB
    subgraph CDP["上下文与变更数据面"]
        SC["服务目录"]
        CM["CMDB / 资产"]
        K8S["K8s / 云控制面"]
        RE["发布平台"]
        CF["配置中心"]
        CH["变更事件"]
        RT["运行时进程信息"]
    end
    CDP --> ES["实体与历史关系服务<br/>统一模型 / 冲突规则 / 时间有效期"]
    ES --> MT["Metric"]
    ES --> LG["Log"]
    ES --> TR["Trace"]
    ES --> PF["Profile"]
```

**读图要点：**箭头方向是“供给身份”，不是“数据流向”。遥测数据仍然留在各自平台，实体服务只提供翻译与历史关系；如果供给侧缺失，下游四类信号无论接入多完整，都无法完成 2.1 节那张映射表。

**一个必须正面回答的评审问题：实体服务是不是在重建 CMDB？**

不是。CMDB 和服务目录仍然是部分字段的权威来源，实体服务不接管资产台账、成本归属、合规属性等企业资产管理职责。实体层重点维护的是**用于遥测关联的稳定身份、别名映射、历史运行关系和证据来源**——即“在 10:05 这个时刻，哪个 Pod 承载着哪个版本的哪个服务”，而这恰恰是多数 CMDB 不保存的时序信息。两者是供给关系，不是替代关系。权威源边界与冲突处理规则见 7.3 节。

## 3. 第二步：从“同一个服务”精确到“同一次请求”

### 3.1 为什么只统一服务名仍然不够

假设 I17 同时处理两个请求：T7 很慢，T8 很快；后台线程还在刷新规则。只按 I17 和时间搜索，三者的记录都会出现。

### 图 4：时间重叠不等于执行关系相同

```mermaid
sequenceDiagram
    participant A as 请求 T7
    participant B as 请求 T8
    participant R as 同一实例 I17
    participant J as 后台任务
    A->>R: 提交订单，开始慢请求
    B->>R: 查询订单，开始快请求
    J->>R: 刷新规则，产生日志和 CPU 样本
    R-->>B: 快请求完成
    R-->>A: 慢请求完成
    Note over A,J: 按实例和时间查询会同时看到三者，仍需请求上下文区分
```

**读图要点：**即使三个活动发生在同一个进程，也不能把后台任务的 CPU 热点算到慢请求头上。需要在记录产生时留下“它属于谁”的信息。

### 3.2 给请求、操作和样本建立明确联系

沿用主案例，假设我们已在关键位置补齐上下文，获得以下示例记录：

| 信号 | 补齐后的示例 | 用来回答什么 |
| --- | --- | --- |
| Metric Exemplar | 观测耗时 `1.86 s`，携带 `trace_id=T7` | 这一个指标观测样本对应哪条 Trace |
| Trace | `T7` 中的操作 `S2=calculateCoupon` 耗时 `1,800 ms` | 哪个操作慢 |
| Log | `trace_id=T7, span_id=S2, message=rule cache miss; parsing rules` | S2 内实际发生了什么事件 |
| Profile 样本 | `process=proc-731-A, trace_id=T7, span_id=S2, stack=...parseRules` | 执行 S2 时采到了哪些代码栈 |

Exemplar 可理解为**统计结果旁附带的一张样本记录卡**：它把某次观测值连接到 Trace。不要给每条普通指标序列加一个请求 ID；应使用支持该机制的 Exemplar 通路。Prometheus 官方对标签基数给出的经验参考是：单个指标的基数尽量控制在 10 以下，超过 100 或有增长到该量级的可能时，应考虑降维或把分析移出监控系统。这个例子的 1.86 s 是单次观测，不是“P99 本身的请求”。[S9：Exemplar 数据模型](https://opentelemetry.io/docs/specs/otel/metrics/data-model/)、[S23：指标标签基数](https://prometheus.io/docs/practices/instrumentation/)。

**Exemplar 有自己的开关和留存边界，必须在设计时确认。**以 Prometheus 为例，该能力需通过 `--enable-feature=exemplar-storage` 启用；其实现为所有序列共用的固定大小内存环形缓冲区，启用后也会写入 WAL，但仅在 WAL 保留期内持久化。这意味着 **Exemplar 往往比它指向的 Trace 先消失**，高吞吐服务还会因缓冲区被覆盖而丢失。Thanos、Mimir、VictoriaMetrics 等的支持程度各不相同，须在试点前单独验证，不能默认“指标存多久，Exemplar 就能用多久”。[S25：Prometheus Exemplar 存储](https://prometheus.io/docs/prometheus/latest/feature_flags/)。

### 图 5：三座不同的“桥”，分别补齐不同关系

```mermaid
flowchart TB
    M["指标直方图：记录了一批请求"] -->|"Exemplar：1.86 s，Trace=T7"| T["Trace T7：总计 1,860 ms"]
    T --> V["校验订单：20 ms"]
    T --> S["S2 算优惠：1,800 ms"]
    T --> D["写数据库：40 ms"]
    S -->|"相同 Trace ID 和 Span ID"| L["日志：规则缓存未命中，开始解析"]
    S -->|"采样时绑定执行上下文"| P["Profile：S2 的规则解析样本"]
    E["实体身份：svc-042 / I17 / proc-731-A"] -.-> T
    E -.-> L
    E -.-> P
```

**读图要点：**虚线表示这些数据属于哪些运行对象；实线标签说明为什么能够进一步关联到具体请求。示例中三个操作串行且覆盖整个请求，`20＋1,800＋40＝1,860 ms`。真实 Trace 可能有并行、嵌套和未埋点区间，不能机械地把所有 Span 耗时相加。

日志可在保留原日志框架和存储的情况下写入 Trace/Span ID，非 OTLP 格式也有规范支持。Profile 则需要采样器及运行时集成提供执行上下文；只改后端字段名做不到。[S6：非 OTLP 日志上下文](https://opentelemetry.io/docs/specs/otel/compatibility/logging_trace_context/)、[S19：Span Profiles](https://grafana.com/docs/pyroscope/latest/configure-client/trace-span-profiles/)。

### 3.3 没有“桥”时怎么办？

| 缺口 | 仍然能做什么 | 必须承认的限制 |
| --- | --- | --- |
| 没有 Exemplar | 按服务、窗口、接口、耗时找候选慢 Trace | 不能称为“这一指标样本对应的请求” |
| Exemplar 已被环形缓冲覆盖或超出 WAL 期 | 同上，按服务与窗口找候选慢 Trace | Trace 可能仍在，但“指标样本↔请求”的桥已断；这是独立于 Trace 采样的**第二个失效点** |
| 日志没有 Trace ID | 按实例和时间找相关日志 | 并发请求多时无法可靠区分 |
| Profile 没有 Span 上下文 | 看同进程同窗口的热点 | 不能直接归因到 T7 或 S2 |
| Trace 被采样丢弃或已过期 | 保留日志、指标及已知关联信息 | 有 ID 不代表目标数据还存在 |
| 短 Span 没采到 Profile 样本 | 看其他证据，或聚合同类操作研究 | 不能把“无样本”解释为“没有消耗” |

Profile 样本与 Trace 是否保留受具体采样和留存机制影响。官方文档明确提示，日志可以有 Trace ID 但找不到对应 Trace，带 Profile 关联标记的 Span 也可能没有样本。[S13：日志与 Trace 的采样边界](https://docs.datadoghq.com/tracing/other_telemetry/connect_logs_and_traces/)、[S19：Profile 样本限制](https://grafana.com/docs/pyroscope/latest/configure-client/trace-span-profiles/)。

## 4. 第三步：把线索变成经验证的诊断

### 4.1 顺着主案例走一遍

现在假设已获得图 5 所示的关联证据，分析过程如下。注意第一步不是看遥测，而是看变更——原因见 4.2 节。

| 步骤 | 看到的示例证据 | 此时可以作出的判断 | 下一步 |
| --- | --- | --- | --- |
| ⓪ 变更检索 | 故障窗口前 30 分钟内，订单服务有一次 v2.3→v2.4 发布（10:00），无配置变更、无扩缩容 | 有一个时间上接近的变更候选，但接近不等于相关 | 用变更把指标分组 |
| ① 指标分组 | v2.4（dep-240）的 P99 升高，同期 v2.3（dep-239）对照组暂未升高 | 异常与新版本有关，仍需检查流量和环境差异 | 找新版本的慢请求 |
| ② 调用链 | T7 的 S2 算优惠占用 1,800 ms；写库仅 40 ms | 对这个请求，主要慢在算优惠 | 查 S2 的日志和代码采样 |
| ③ 关联日志 | S2 记录规则缓存未命中并开始解析 | S2 确实走了规则解析路径 | 核实是否重复解析及消耗 |
| ④ 关联 Profile | 假设 S2 有 100 个有效 CPU 样本，其中 72 个栈包含 parseRules | 在这些样本中，解析路径占比较高 | 查代码及版本差异 |
| ⑤ 代码检查 | 新版本将本应复用的解析结果改成每次请求重新构建 | 出现能解释观测的机制假设 | 构造相同条件的对照验证 |
| ⑥ 对照实验 | 使用同一输入、流量和资源，比较旧版、新版、修复版 | 若仅新版复现且修复版恢复，可在该范围内确认机制 | 检查其他路径并渐进上线 |

“72/100”是本文构造的采样计数，不能直接换算成“1,800 ms ×72% 的请求时间”。样本可能来自 CPU 时间，Span 耗时则包含等待；嵌套函数的累计占比也不能重复相加。

为了让假设更容易理解，下面给出仅表达逻辑的伪代码：

```python
# 示例旧版：规则更新后解析一次，后续请求复用。
parsed_rules = load_and_parse_rules()
def calculate_coupon(order):
    return evaluate(parsed_rules, order)

# 示例问题版：每个请求都重新解析规则。
def calculate_coupon(order):
    rules = load_and_parse_rules()
    return evaluate(rules, order)
```

这不是生产修复代码。真实缓存还需要正确处理规则版本、更新、并发、失效和数据一致性。

### 4.2 变更事件应作为一级诊断维度，而不是背景信息

回看上一节：整条推理链的起点是“v2.4 与 v2.3 的对照”。也就是说，**真正让四类信号产生分组意义的，是一次发布变更**。如果没有这次变更作为分界，指标只是“变慢了”，Trace 只是“某条请求慢”，Profile 只是“某个函数占 CPU”，三者之间没有可对照的边。

因此本文把变更事件列为与 Metric、Log、Trace、Profiling 并列的一级诊断维度，而不是附带的背景信息。至少应纳入：

| 变更类型 | 典型来源 | 在诊断中的作用 |
| --- | --- | --- |
| 应用发布 | 发布平台 | 划出版本对照组，定位代码层机制 |
| 配置变更 | 配置中心 | 解释“代码没动但行为变了” |
| 依赖升级 | 构建/制品系统 | 解释运行时、驱动、SDK 层的行为漂移 |
| 扩缩容 | 编排控制面 | 解释容量、连接数、缓存命中率的阶跃 |
| 路由与流量调整 | 网关、服务网格、灰度平台 | 解释局部实例异常而整体正常 |
| 数据库与中间件变更 | DBA 变更单、中间件平台 | 解释下游等待与超时 |
| 基础设施迁移 | 云/机房管理平台 | 解释跨可用区延迟与网络异常 |

据此，标准排障流程的第一步应当是：**故障窗口附近发生了什么变更**，而不是先打开某一类信号。这也意味着“联合分析”不应只理解为遥测信号之间的关联，还包括**遥测与变更事件的关联**。

需要同时说明边界：变更与故障在时间上接近，只是提供了一个候选；它既不能替代版本对照实验，也不能替代机制验证（4.1 节第⑤⑥步）。大量变更与故障同时发生却互不相关，尤其在发布密集的时段。**把“变更窗口内”当作“变更所致”，是本维度最容易产生的误判。**

### 4.3 两个反例：证据指向别处，以及证据根本不足

**反例一：慢请求不是总能用 CPU 火焰图解释。**假如 Trace 显示的 1,800 ms 实际花在等待数据库连接，而 CPU Profile 热点来自后台规则刷新，那么应转向连接池排队、连接占用、数据库侧和锁等待证据。实体与请求身份帮助我们发现“哪些证据不属于这个问题”，这与找到关联同样重要。

**反例二：证据不足时，正确的输出是“不足”，不是凑齐。**再假设另一种情况：Profile 在该 Span 窗口内没有采到任何有效样本，日志也未写入 Trace ID，但 Trace 已明确显示 1,800 ms 中的绝大部分处于数据库连接等待。此时正确的结论是：

> 已确认耗时集中在数据库连接等待（执行级证据充分）；当前证据不足以确认是否同时存在代码热点（Profiling 无样本，日志无请求上下文）。下一步验证动作：检查连接池配置与等待队列指标。

而不是为了让四类信号看起来齐全，去调高采样率重跑、或用进程级火焰图冒充 Span 级归因。**联合分析的目标不是信号齐全，而是证据充分。**这两件事在很多整合项目里被混为一谈，结果是指标覆盖率报表很漂亮，诊断结论却并没有更可信。

### 4.4 证据等级模型

读到这里，应能区分五级结果。这是全文的核心方法论，建议直接沉淀为工作台展示、智能体输出和验收指标的共同分级。

### 图 6：证据等级，每一级都不能自动推出下一级

```mermaid
flowchart TB
    A["ENTITY_RELATED<br/>同一实体：都是订单服务 I17 的数据"]
    B["WINDOW_RELATED<br/>同一窗口及相关资源：都在故障附近，I17 当时运行在 H3"]
    C["EXECUTION_RELATED<br/>同一次执行：日志和样本明确绑定 T7/S2"]
    D["MECHANISM_HYPOTHESIS<br/>机制假设：重复解析可以解释观测"]
    E["MECHANISM_VERIFIED<br/>经验证的机制：控制条件下复现，修复后恢复"]
    A -.->|"需补事件时间与历史生命周期/资源关系"| B
    B -.->|"需补请求上下文：Trace/Span 绑定或其他确定性执行键"| C
    C -.->|"需读代码与版本差异"| D
    D -.->|"需对照实验"| E
    X["每一级都不能由前一级自动推导"]
    A -.- X
```

**读图要点：**虚线表示“还需要额外证据才能上升一级”，不是自动流转。注意前两级跃迁所需的证据类型不同：从“同一实体”到“同一窗口”，靠的是事件时间和历史生命周期关系（I17 当时运行在 H3 上）；请求上下文属于再下一级跃迁，它回答的是“同一次执行”，而不是“同一个窗口”。把这两者混淆，会导致在还没有请求身份时就宣称执行级关联。工作台和智能体输出诊断结论时，必须标注当前处在哪一级；停在 `MECHANISM_HYPOTHESIS` 并说明还缺什么，比直接输出 `MECHANISM_VERIFIED` 的结论更有价值。

这五级是本文的讲解与验收分类，不是业界统一颁布的成熟度等级。它与 6.2 节的 `correlation_status` 是两个不同维度：后者描述“这条证据的身份是否确定”，前者描述“整组证据支撑到了什么程度”。二者的字段定义见配套分册。

## 5. OTel 与实体模型究竟是什么关系？

### 5.1 用“通讯录、运单号、公共填写规则”帮助理解

| 概念 | 类比 | 在本例中的作用 |
| --- | --- | --- |
| 实体模型 | 通讯录及组织关系 | 知道 order-api 和 trade-order 都属于 svc-042，I17 当时在哪运行 |
| Trace/Span 上下文 | 一次业务过程的运单号及操作编号 | 知道这条日志属于 T7 中的 S2 |
| 语义约定 | 公共填写规则 | 约定服务名、版本、单位、状态等字段表达什么 |
| OTel SDK / 埋点 | 按规则记录信息的工具 | 在业务执行时采集数据，并支持相关上下文 |
| OTel Collector | 接收、整理、转发数据的中间站 | 按组件能力接入既有格式，转换后送到后端 |
| OTLP | 一种公共数据传输格式/协议 | 让支持它的采集端、转发端和后端交换遥测数据 |

OTel 的实际组成与职责见 [S12：Overview](https://opentelemetry.io/docs/specs/otel/overview/) 和 [S5：Collector](https://opentelemetry.io/docs/collector/)。OTel 不会自动替企业判定两个旧系统中的名称是否属于同一业务服务，也不会自动恢复过去未记录的请求信息。

### 图 7：可以分三步采用，不用一次替换全部平台

```mermaid
flowchart TB
    subgraph A["第一步：先对齐，保留既有采集"]
        A1["既有指标 / 日志 / APM / Profile"] --> A2["企业实体映射和查询适配"]
    end
    subgraph B["第二步：关键链路补信息"]
        B1["应用或网关：补请求传播和日志 ID"] --> B2["关键指标：Exemplar；Profile：执行上下文"]
    end
    subgraph C["第三步：适用部分采用公共标准"]
        C1["新增应用：评估 OTel SDK / 自动埋点"] --> C2["OTLP / Collector：按兼容性接入"]
    end
    A2 --> W["同一个联合分析工作台"]
    B2 --> W
    C2 --> W
```

**读图要点：**这是能力演进图，不是说第二步必须依赖 OTel，也不是说新旧数据必须进入同一个数据库。第二、三步可以在不同服务上并行推进。

### 5.2 哪些旧数据能“翻译”，哪些不能“补造”

| 已有数据 | 可行的改造 | 无法通过翻译补出的信息 |
| --- | --- | --- |
| 日志里已有旧 APM Trace ID | 做有规则、可追溯的 ID 映射或原生查询 | 未记录的 Span 边界和跨线程传播 |
| 指标叫 `request_latency_ms`，单位确认为 ms | 建语义映射；在类型、聚合等一致时换算单位 | 已被聚合丢掉的单请求、实例或分布 |
| 日志和指标都有 Pod UID | 用编排记录关联到同一 Pod 和服务 | Pod 内多个进程的精确归属，若原数据没记录 |
| Profile 有进程和时间 | 关联该进程当时所属服务与版本 | 每个样本属于哪个 Span，若采样时没捕获 |

转换不是只把 `app` 改名成 `service.name`。OTel 提供转换与资源补充机制，也有专门的 Prometheus/OpenMetrics 兼容规则，具体处理仍需检查类型、单位、累计/增量和版本支持；指标名本身也应遵循既有命名规范，不要在迁移中顺手重命名。[S20：遥测转换](https://opentelemetry.io/docs/collector/transforming-telemetry/)、[S16：兼容规则](https://opentelemetry.io/docs/specs/otel/compatibility/prometheus_and_openmetrics/)、[S18：指标命名规范](https://prometheus.io/docs/practices/naming/)。

### 5.3 为什么说“非二选一”

实体模型可以接纳 OTel 数据，也可以接纳既有平台的数据；OTel 本身也正在发展实体模型。**因此建议采用兼容公共语义的企业实体模型，并按实际缺口引入 OTel 能力。**

截至 2026-09-19 检索，相关状态需要分两层看，不能只引用一个标签：

| 对象 | 检索到的状态 | 对落地的含义 |
| --- | --- | --- |
| OTel Entity Data Model | Development | 规范页同时注明实体关系建模将在后续规范工作中细化；可参考其身份思路，不宜直接绑定字段结构 |
| Profiles **信号** | Alpha（2026-03-26 进入 public Alpha） | 可用于有限的非关键生产场景并向社区反馈 |
| OTLP 协议中的 **profiles signal** | Development（同一份 OTLP 规范对 trace/metric/log 标记为 Stable） | 真正约束“能否用 OTLP 传输 Profile”的是这一层，而不是信号成熟度 |

**“信号成熟度”与“该信号在 OTLP 协议中的状态”是两个口径**，评审时容易被混淆为一个。本文沿用上述检索证据，不据此承诺任何 SDK、Collector 或后端已稳定支持；落地时应固定具体版本。[S7：实体规范](https://opentelemetry.io/docs/specs/otel/entities/data-model/)、[S10：Profiles 规范](https://opentelemetry.io/docs/specs/otel/profiles/)、[S26：OTLP 规范状态](https://opentelemetry.io/docs/specs/otlp/)。

## 6. 三个落地细节：避免“连错”“查不到却误判正常”和“身份无主”

### 6.1 为什么关系必须带时间

假设 10:06 因回滚 v2.4 而销毁旧 Pod P17，10:07 新 Pod P29 启动并复用了 IP `10.*.*.*`。10:08 才收到一条 10:05 发生的旧日志。如果只查询当前 IP 归属，就会把旧日志错误关联给新 Pod。

### 图 8：历史数据必须查历史身份

```mermaid
flowchart LR
    L["日志发生于 10:05<br/>10:08 才到达"] --> Q["按事件发生时间查映射"]
    Q -->|"10:05 落在旧有效期"| O["P17 使用 10.*.*.*<br/>有效期 10:01 至 10:06"]
    N["当前 P29 使用 10.*.*.*<br/>有效期从 10:07 开始"] -.-> X["不能据当前 IP<br/>把旧日志分给 P29"]
```

**读图要点：**IP 是可能复用的地址，Pod UID 才是这个例子中用于区分对象的身份。若旧日志只有 IP 且事件时间不可信，就应返回“无法确定”，而不是随便选一个对象。企业关系表要保留有效期与信息来源，不能只存最新状态。

### 6.2 “没有数据”必须用多维状态表达，不能压成一个枚举

把“暂无数据”当成一句界面文案，是联合分析工作台最容易犯、也最难在事后发现的错误。但**改成单一枚举同样不够**：查询是否成功、数据是否存在、结果是否完整、身份是否确定，这四件事彼此独立，一次查询完全可能同时命中多个——例如“源超时 + 只拿到部分结果”，或“找到了记录 + 但身份仍有歧义”。压成一个值就会丢掉信息，而丢掉的恰好是智能体最需要的部分。

因此结果状态应当按四个正交维度分别表达。**注意这四维的归属层级不同**：前三维描述“这一次对某个数据源的查询”，第四维描述“一条证据与某个目标之间、某一条关联关系的确定性”。

第四维的归属需要再精确一层。同一条证据往往同时参与多条关联，而它们的确定性可能不同：

```text
证据 trace-span-S2  →  T7 / S2     （请求/执行关联）   EXACT
证据 trace-span-S2  →  实例 I17     （实体历史关系）    CANDIDATE
```

这条证据到底算 `EXACT` 还是 `CANDIDATE`？答案是两者都不是——**确定性属于关系，而不属于记录**。请求身份可以精确到某一次执行，同时该次执行归属哪个实例仍可能存在歧义。实现上应为每条证据保存一组关联边，逐边标注状态，而不是给整条记录贴一个标签。

关联边的起点恒为它所属的那条证据，不做链式串接；跨多跳的关系（例如“这条日志归属哪个部署”）必须显式建边，且其状态**不得高于链路最弱环节**。注意最弱环节是上限而不是充分条件：即使各跳都精确，也要先确认该关系可传递、时间区间一致，才能判定复合边精确。字段结构见配套分册。

一次跨源诊断会产生多条查询记录、多条证据记录和更多条关联边，三者数量互不相等。这一点决定了数据结构的分层，字段定义见配套分册。

| 维度 | 回答什么问题 | 取值 |
| --- | --- | --- |
| `query_status` | 这次查询本身成功了吗 | `OK` / `TIMEOUT` / `UNAVAILABLE` / `PERMISSION_DENIED` / `ERROR` / `UNKNOWN` |
| `data_status` | 目标数据存在吗 | `FOUND` / `NO_MATCH` / `EXPIRED` / `SAMPLED_OUT` / `NOT_COLLECTED` / `UNKNOWN` |
| `completeness` | 拿到的是全部吗 | `COMPLETE` / `PARTIAL` / `UNKNOWN` |
| `correlation_status` | 身份确定吗 | `EXACT` / `CANDIDATE` / `AMBIGUOUS` / `UNKNOWN` |

其中 `UNKNOWN` 在前三维中既是实际取值，也是消费方遇到未知取值时的安全降级目标（见配套分册的兼容策略）。**`completeness = UNKNOWN` 不得当作 `COMPLETE` 处理**，否则“没查到异常即无异常”会从兼容规则这个口子重新进来。

对照几个典型场景：

| 场景 | query | data | completeness | correlation |
| --- | --- | --- | --- | --- |
| 查询成功，范围完整，确实无记录 | `OK` | `NO_MATCH` | `COMPLETE` | — |
| Trace 保留审计确认已过期 | `OK` | `EXPIRED` | `COMPLETE` | — |
| 日志源超时，已返回部分结果 | `TIMEOUT` | `UNKNOWN` | `PARTIAL` | — |
| 一个 IP 在误差窗口内有两个 Pod 候选 | `OK` | `FOUND` | `COMPLETE` | `AMBIGUOUS` |
| 扫描预算触顶被截断 | `OK` | `FOUND` | `PARTIAL` | `EXACT` |

**关于 `SAMPLED_OUT` 与 `NOT_COLLECTED` 的硬约束**：绝大多数数据源并不记录“这条我采样丢弃了”或“这个字段我没采集”。因此这两个值**只有在来源提供了明确的采样决策或采集配置审计证据时才允许填写**；无法区分时必须返回 `UNKNOWN`，不得由“查不到”推断为“被采样丢弃”。否则契约本身就会生产出带确定性外观的猜测——正是本文全篇反对的行为。

关键区别始终是：`NO_MATCH` 表示“查到了，没有”，其余情况都表示“没查到，原因各异”。这两类在诊断上的含义截然相反。

同样，监控数据库采集器时，需要分清“采集器运行在哪”与“它在监控哪台数据库”。共享主机的 CPU 也不能直接作为每个服务各自的 CPU 用量。详细身份与数据角色规则见附录 A；四个维度的完整取值定义、字段结构和机器可读示例见配套分册《联合分析数据契约与治理规范》。

### 6.3 存量多版本 APM 并存期：先定义 Canonical Trace Context

前面的第二步假设“补齐同一次请求的标识”是一个动作。但当企业自研 APM 处在新旧版本交替期时，它是一组相互牵连的决策，其中最容易被跳过的是第一个。

典型并存形态：早期版本基于 Zipkin 扩展（traceId 为 32 位十六进制、span id 为 16 位十六进制，属性以标签前缀承载），新版本参考其他 APM 设计并采用全自定义报文（顶层含时间与扁平元数据块）；两者都不支持 OTLP 摄取，也都不实现 W3C Trace Context 传播。

#### 第一决策：谁是 Canonical Trace Context

并存期真正需要控制的，不是“系统里出现了几种 ID 格式”，而是**企业是否定义了唯一的权威请求上下文（Canonical Trace Context），以及不同格式之间是否存在清晰、可测试、可审计的桥接规则**。允许多种格式在不同区段流转并不可怕，可怕的是没有任何一种被指定为主关联键。

因此第一步是明确声明：哪一个上下文是 canonical；工作台和智能体以它作为主关联键；其余格式以带命名空间的属性形式保留（例如 `legacy.apm.trace_id`），供反查和审计，但不参与关联判定。

**但 canonical 不能凭文件宣告，必须满足门槛。**这里存在一个容易被架构评审忽略的前提：上述两套 APM 都不实现 W3C 传播，因此“通过传播器、网关或 Agent 桥接”并不是绕开并存期的捷径——它本身就是要改造两套 Agent 的工作量。建议设定如下门槛：

> **范围内宣告**：在目标试点范围内，Canonical Trace Context 所经过的**全部实际传播边界**均已具备原生传播或经实测的桥接能力，且不存在静默截断、重建或身份分叉，方可在该范围内宣告 canonical 成立。
>
> **平台级宣告**：须覆盖网关、RPC、异步调用、消息队列等**代表性边界矩阵**，并证明各类边界上均无残留的截断路径。

这里的判定单位是**调用链上的边界**，不是 Agent 版本数量。某一套新版 Agent 支持全部四类场景，并不意味着同一条调用链上残留的旧 Agent 不会截断 canonical 上下文——**只要链路上还有一个不承载该上下文的环节，canonical 在该链路上就不成立**。反过来，若某试点应用根本不使用消息队列，也没有必要强制它先完成 MQ 边界测试。

门槛未达成之前，默认策略是**不新增并行传播格式**——这是未满足前提时的工程选择，不是普遍原则。

#### 第二决策：桥接优先于映射

一旦 canonical 确定，两种技术路径的优先级就清楚了：

| 路径 | 做法 | 适用与代价 |
| --- | --- | --- |
| **上下文桥接（优先）** | 在网关、Agent 或传播器上把 legacy 上下文转换为 canonical 上下文，全链路保持同一个 trace 身份 | 改造集中在少数组件，不产生持久数据；但依赖 Agent 具备承载能力 |
| **请求级 ID 映射（次选）** | 建立 `legacy_trace_id ↔ canonical_trace_id` 的映射记录 | 无需改造传播层，但会产生**每日可达数十亿条的高基数数据**，须限定为短生命周期 |

**必须明确区分两类映射，它们不是同一个系统：**

| | 实体身份映射 | 请求上下文桥接 |
| --- | --- | --- |
| 例子 | `trade-order → svc-042` | `legacy_trace_context ↔ canonical_trace_context` |
| 基数 | 低（服务、实例、Pod 量级） | 极高（请求量级） |
| 生命周期 | 长期保留，含历史有效期 | 短期，与 Trace 留存窗口对齐即可 |
| 治理方式 | 进入企业实体注册表，走变更评审（7.3 节） | 独立的高基数存储，按 TTL 自动淘汰，不进注册表 |

把请求级 ID 桥接塞进实体注册表，是这一步最常见也最昂贵的设计错误：注册表会在数周内被请求级数据淹没，而它本应承载的低基数历史关系反而查不动。

### 图 9：请求上下文迁移与 canonical 声明

```mermaid
flowchart TB
    LA["存量应用 / 旧 APM Agent"] -->|"legacy context"| BR["网关 / Agent / 传播器桥接层"]
    BR -->|"canonical trace context（主关联键）"| NEW["新 APM / OTel 通路"]
    BR -.->|"legacy.apm.trace_id<br/>命名空间属性，仅供反查"| NEW
    NEW --> LOG["日志注入"]
    NEW --> EX["指标 Exemplar"]
    GATE["宣告门槛：试点链路上的<br/>全部实际传播边界均已实测通过"] -.->|"仍有截断路径则不新增传播格式"| BR
    TTL["请求级 ID 映射：高基数、短 TTL<br/>不进入实体注册表"] -.- BR
```

**读图要点：**canonical 是唯一主关联键，legacy ID 以命名空间属性保留但不参与关联判定；请求级映射是桥接不可用时的次选路径，须独立存储并设定 TTL。门槛的判定单位是链路上的实际传播边界而非 Agent 数量，且属于前置门槛，不是事后验证。

#### 第三决策：判等规则与不可判等时的行为

映射方向（单向/双向）、转换规则的版本号必须显式定义；**无法判等时返回 `correlation_status = AMBIGUOUS`**，而不是就近匹配。

这里有一条容易误用的事实：W3C `traceparent` 的 trace-id 为 32 位十六进制（16 字节）、parent-id 为 16 位十六进制（8 字节）；Zipkin 的 spanId 固定为 64 位，traceId 则允许 64 位或 128 位，采用 128 位时与 W3C **位宽完全一致**。正因为位宽相同却分属不同生成域（且同一套 Zipkin 部署内还可能长短并存），格式相同这件事本身不构成任何判据。准确的表述是：

> 仅凭 ID 长度和十六进制格式相同，不能判定两个 Trace ID 属于同一 Trace。能否互认，取决于传播和桥接规则是否明确保持了同一个 Trace Context。

映射与桥接规则本身要版本化并保留历史，否则旧版本下线后，此前做出的诊断结论无法复现（见附录 A.4）。

#### 切换点会打断历史关联

还有一个具体失效窗口需要写进计划：**canonical 上下文切换之后，切换点之前产生的 Exemplar 指向的是旧身份空间。**由于 Exemplar 本身留存很短（3.2 节），跨切换点的“指标样本 → 请求”链路会在一段时间内同时受到身份不一致和留存过期两重影响。应在切换前后各保留一段双向可查窗口，并在工作台显式提示该窗口内的关联降级。

#### 退出条件

并存期的退出条件应当明确写入计划：旧版本承载的应用比例降到约定阈值以下、请求级桥接数据的新增量持续下降、且 canonical 上下文已在代表性边界矩阵上完成实测，才具备收敛到单一公共标准的条件。

在未明确 Canonical Trace Context 和桥接策略之前直接叠加新的传播格式，会显著增加并存期的身份复杂度和诊断歧义风险。

## 7. 实际项目怎么做：每一步都能交付可见效果

### 图 10：从四个入口到可复查的故障证据

```mermaid
flowchart TB
    U["用户选择：订单服务 / 实例 / 故障窗口"] --> W["联合分析工作台"]
    W --> E["实体服务：翻译身份，查当时关系"]
    E --> Q["查询适配器：构造各平台受控查询"]
    Q --> M["原指标库"]
    Q --> L["原日志库"]
    Q --> T["原 Trace 库"]
    Q --> P["原 Profile 库"]
    M --> R["结果：事实、关联依据、缺口"]
    L --> R
    T --> R
    P --> R
    R --> D["形成假设，检查代码，再对照验证"]
```

**读图要点：**先建有限场景的查询适配器，不必先开发通用跨库 SQL 引擎。每次查询先收窄实体和时间，再取明细；原平台仍负责保存数据和执行其查询能力。

把前面各节的要素放在一起，总体逻辑分层如下。

### 图 11：联合分析总体逻辑架构

```mermaid
flowchart TB
    U["使用方：SRE / 应用工程师 / 诊断智能体"]
    U --> API["联合分析 API 与工作台"]
    subgraph CAP["横切能力（不可后补）"]
        EC["证据契约与证据等级"]
        ST["四维状态与不确定性表达"]
        QB["查询预算 Query Budget"]
        AU["权限、脱敏与审计"]
    end
    API --- CAP
    API --> ES["实体与历史关系服务"]
    CDP["上下文与变更数据面<br/>服务目录 / CMDB / K8s / 发布 / 配置 / 变更"] --> ES
    ES --> AD["查询适配层"]
    AD --> MT["Metric"]
    AD --> LG["Log"]
    AD --> TR["Trace"]
    AD --> PF["Profile"]
```

**读图要点：**中间那组横切能力（证据契约、状态表达、查询预算、权限审计）不是上线后再补的功能，它们决定了上层能否被智能体稳定消费、能否通过合规评审。把它们排到界面之后开发的项目，通常要在接入第二类使用方时返工。

### 7.1 一份可以照着开工的改造清单

| 步骤 | 具体要做的事 | 用户能看到的变化 | 主要负责人 |
| --- | --- | --- | --- |
| ① 选案例和盘点 | 选发布变慢、连接池等待等案例；记录原流程耗时、各源接口及缺失字段 | 明确本次要改善哪些排障步骤 | SRE＋各平台负责人 |
| ② 对齐身份 | 为 svc-042 建四种名称映射和实例历史关系 | 一次选择服务，四个入口自动带对条件 | 平台＋服务目录/基础设施团队 |
| ②′ 接入变更源 | 接发布、配置、扩缩容事件，按实体和时间归档 | 打开故障窗口即可看到“这段时间改了什么” | 平台＋发布/配置平台团队 |
| ③ 接通查询 | 实现实例、版本、时间过滤及失败提示 | 同一工作台看相关证据，无需手工抄 ID | 平台团队 |
| ④ 补请求标识 | 日志写入既有 Trace/Span ID；修补网关、RPC、异步边界 | 从 T7 直接看到它的日志 | 应用＋APM/平台团队 |
| ⑤ 补样本联系 | 为关键耗时指标接 Exemplar，为适用运行时接 Span Profile | 指标样本到请求，请求到 Profiling 样本 | 应用＋平台团队 |
| ⑥ 验证再推广 | 重放或注入故障，测正确性、耗时、开销和降级 | 确认真的更好排障，再扩展服务范围 | SRE＋平台＋应用团队 |

步骤是推荐交付顺序，不是统一行业工期。新增应用可以同时评估 OTel；不需要等所有旧系统完成迁移。实施期间保留既有告警主路径，新增关联服务不可用时仍应能按原方式排障。

### 7.2 用“能否完成一个动作”验收，比统计接了多少数据更直观

| 验收动作 | 成功表现 | 失败例子 |
| --- | --- | --- |
| 查 10:05 的实例 I17 | 四类数据都定位到当时正确对象 | 日志被分给 10:07 新建的实例 |
| 点 Exemplar 打开 T7 | 请求身份一致，目标实际存在 | 打开同一服务里另一条慢 Trace |
| 查询超出 Exemplar 留存窗口的历史指标 | 明确提示 Exemplar 不可得，指标本身仍可查 | 静默退化为“该指标没有关联请求” |
| 在 S2 查看关联日志 | 日志确实带 T7/S2 上下文 | 混入后台任务的日志 |
| 查看 S2 的 Profile | 有采样上下文；无样本则明确提示 | 把整个进程火焰图标成 S2 专属 |
| 让日志源不可用 | 明确提示失败，其他证据仍可查看 | 显示“没有错误日志” |
| 排查同一故障案例 | 在不增加错误结论的前提下减少定位时间 | 只减少页面点击，却更容易误归因 |

例如，假设测试集中发起 100 次合法日志→Trace 关联：80 次找到正确目标、15 次有审计证明已过期、5 次查询超时。原始命中率是 80/100＝80%；若只讨论未过期尝试，条件命中率为 80/85≈94.1%，两者须一起报告，5 次超时也须保留。这个算例说明：指标定义不清楚，很容易把“好看”误当作“好用”。

详细覆盖率、准确率、延迟、诊断效率和开销口径，以及待执行的反例测试见附录 B。本文没有实际执行这些试验。

### 7.3 统一治理入口，多权威事实源

前面的方案有一个隐含前提：存在一份权威的实体与别名映射注册表。这份注册表往往横跨多个团队的职责边界——指标与日志、调用链、Profiling 可能分属不同团队，而注册表恰好落在边界最模糊的位置。**这是组织问题而不是技术问题，但它在很大程度上决定方案的长期可维护性。**

这里要避免一个常见误读：所谓“统一”，不是要求所有事实都先复制进一个中央系统才能生效。正确的形态是**统一治理入口、统一模型、统一冲突规则，同时允许多个权威事实源自动供给**：

| 事项 | 建议做法 | 缺失时的风险 |
| --- | --- | --- |
| 治理归属 | 注册表有唯一 owner 团队，负责模型定义、规则版本、冲突裁决、历史快照和查询输出；**不负责生产所有事实** | 模型由多方各自解释，同一服务出现互相矛盾的映射且无人收敛 |
| 事实供给 | `service_identity` 与 `business_service_owner` 来自服务目录，部署版本来自发布平台，Pod 生命周期来自编排控制面，`running_artifact` 与 `process_identity` 来自运行时，人工别名修正经治理入口提交；**各源自动供给，不做人工二次录入** | 中央系统成为录入瓶颈，数据天然滞后于现实 |
| 权威源边界 | 按字段而非按系统划分权威；**没有一个系统天然是所有字段的权威源** | 字段冲突时按“谁先写谁赢”处理，历史关系不可信 |
| 冲突裁决 | 预先定义裁决规则：按字段类型、权威源优先级、发生时间和证据强度决定，并保留冲突记录 | 冲突被静默覆盖，事后无法解释为什么关系变了 |
| 变更评审 | 模型与规则变更走评审并产生版本号；影响面（受影响实体数、受影响历史诊断）在评审材料中列出 | 一次静默的规则修改让此前的诊断结论无法复现 |

需要区分两类冲突，处理方式完全不同。

**第一类：表面冲突。**看起来矛盾，拆到字段后自然消失。

```text
CMDB：   该实例的资产责任人属于 A 团队
运行时： 该进程实际加载的是 service-A 的构建产物
```

这两条根本没有冲突，只是回答了不同字段——`asset_owner` 与 `running_artifact`。**大量所谓的“数据不一致”属于这一类，根因是用了“业务归属”这种笼统说法而非字段名。**因此权威性必须按字段划分：

| 字段 | 权威源 |
| --- | --- |
| `service_identity`、`business_service_owner` | 服务目录 |
| `asset_owner`、`cost_center`、`physical_location` | CMDB |
| `running_artifact`、`process_identity` | 运行时 |

**第二类：真实冲突。**同一字段，两个来源给出不同值。

```text
service_catalog.service_identity = service-A
k8s.annotation.service_identity  = service-B
```

这才需要裁决规则。按上表，`service_identity` 的权威源是服务目录，服务目录胜出；K8s annotation 在此属于登记信息，对该字段不具权威性。但**冲突记录必须保留**，因为 annotation 与服务目录长期不一致本身就是一个需要修复的配置问题。只有当两个同等权威级别的来源无法裁定时，才置为 `AMBIGUOUS`。

还要纠正一个容易过度简化的说法：**冲突不一定意味着数据错误**，它可能来自权威级别差异、数据新鲜度、迟到数据、配置偏差，也可能确实是真实的数据错误。裁决规则只负责给出当前采信值，不代表其余来源都无需修复。

附录 A.1 指出“共享标签治理、服务目录维护和源端上下文改造必须有明确负责人，否则统一模型会持续退化”。在缺少明确治理归属和冲突规则的情况下，映射表的长期可维护性风险较高。

### 7.4 按人与智能体双接口设计

如果企业已经在建设故障诊断智能体，那么智能体可能成为联合分析能力的重要消费者之一，甚至可能早于统一人工工作台形成规模化调用。这不是所有企业都会出现的情况，但一旦出现，设计重心的排序会发生变化：

| 设计要素 | 面向人 | 面向智能体 |
| --- | --- | --- |
| 结构化证据契约（附录 A.1 的四类记录） | 中 | **最高**，没有契约就无法稳定消费 |
| 缺口、歧义、超时的机器可读表达（6.2 节四维状态） | 中 | **最高**，否则智能体会把“查不到”当作“正常” |
| 受控查询接口与查询预算（附录 C.1） | 中 | 高，智能体的查询频次和递归深度远高于人 |
| 界面交互与跳转体验 | 高 | 低 |

因此架构目标应写为 **Human-readable + Machine-consumable 双接口**：同一套证据契约，一路渲染为人可理解的界面，一路序列化为智能体可消费的结构。**不能为了智能体牺牲人工排障的可解释性**——诊断结论最终要由人承担责任，一个人读不懂的证据面板即使智能体用得很顺，也无法在故障复盘中站住脚。

实施顺序上建议先冻结证据契约和状态维度，再做界面渲染。反过来做容易在接入第二类使用方时被迫重写查询层。

无论消费方是人还是智能体，附录 A.4 的约束同样成立。对智能体还需增加两条强制要求：

- **每个诊断结论必须附带证据引用**：结论、证据列表、证据等级（4.4 节）、数据完整性、反证、缺失证据、下一步验证动作，缺一不可。
- **必须区分事实、关联、假设与验证结论**：避免把“同一时间窗口观察到 CPU 热点”改写为“CPU 热点导致了本次请求变慢”。

6.2 节把结果状态拆成四个维度，正是为了让这些约束可被机器执行，而不是停留在规范文字上。字段级定义见配套分册。

## 8. 回到行业问题：哪些是共同做法，哪些是企业自己的选择

### 8.1 行业公开机制如何佐证该路线具有可实现性

| 一手资料 | 明确公开的机制 | 对本例的启示 |
| --- | --- | --- |
| Grafana Correlations | 用某数据源字段构造另一源查询 | 四个存储可以保留，先建立查询联系 |
| Datadog Unified Service Tagging | 以 env、service、version 关联遥测 | 统一服务、环境、版本确实是共同基础 |
| Datadog 日志/Trace 关联 | 日志加入 Trace/Span ID | 对象标签之外还需要请求身份 |
| Dynatrace Grail | 多来源接入、标准化、拓扑映射与统一查询 | 实体/语义和分析组织同样重要 |
| Elastic ECS | trace.id、span.id 等及 OTel 对应关系 | 非 OTel 原生模型也能参与关联 |
| OTel/W3C | 公共语义、传播及样本关联机制 | 可减少企业逐一自定义的工作 |
| OTel Profiles（Alpha） | 明确定义 Profile 与其他信号的双向关联，分为请求上下文关联（到具体 Trace/Span）与资源上下文关联（到同一服务实例） | 这是目前最接近“四类信号统一关联模型”的公共定义，与本文第 2、3 节的两层关联结构一致；但受成熟度限制，短期内宜作为设计参照而非实现依赖 |

上表末行的双向关联描述，出自 OTel 的 Profiles 概念说明而非规范正文，二者层级不同，引用时应分别标注。[S27：Profiles 概念与关联模型](https://opentelemetry.io/docs/concepts/signals/profiles/)。

来源：[S1：Grafana](https://grafana.com/docs/grafana/latest/administration/correlations/)、[S2：Datadog 标签](https://docs.datadoghq.com/getting_started/tagging/unified_service_tagging/)、[S13：Datadog 请求关联](https://docs.datadoghq.com/tracing/other_telemetry/connect_logs_and_traces/)、[S3：Dynatrace](https://docs.dynatrace.com/docs/platform/grail/dynatrace-grail/architecture)、[S4：ECS](https://www.elastic.co/docs/reference/ecs/ecs-tracing)、[S11：W3C](https://www.w3.org/TR/trace-context/)、[S12：OTel](https://opentelemetry.io/docs/specs/otel/overview/)。

**因此，有可归纳的共同原则，也已出现覆盖四类信号的公共关联定义（OTel Profiles 的双向关联），但尚未发现被各方统一采用并在生产规模验证的完整最佳实践。**这是对所选样本的比较结论，不是全行业普查。厂商文档能证明提供哪些机制，不能证明本企业使用后必然达到某准确率或成本收益。

### 8.2 对存量企业的路线建议

优先交付“一个服务的故障可以顺畅查清楚”，再把成功做法复制到更多服务。以实体对齐为起点，以请求和样本上下文提升精度，以对照验证衡量诊断质量。OTel 应在减少接入、传播和语义治理成本的位置逐步采用。

如果旧平台接口足够、存储能力可用，不必为关联分析先换库；如果跨源查询已经无法满足时延、留存或成本要求，再单独论证集中存储或选择性物化。无论选择哪种存储，都要解决“对象是谁、数据是哪次执行产生、证据是否足够”这三个问题。

最后要强调三条与技术选型同等重要的前提：实体模型需要**统一治理入口加多权威事实源**（7.3 节）；存量自研 APM 处于版本交替期时，应先明确 Canonical Trace Context 及其宣告门槛（6.3 节），再谈向公共标准演进；权限、审计与聚合风险属于横切能力，须与功能同期建设（第 9 章）。在缺少这些治理机制的情况下，前面的技术措施长期可维护性风险较高，容易逐步退化为又一套无人维护的映射表。

## 9. 安全、权限与治理

当联合分析平台能够同时访问日志、调用链、指标、Profile、CMDB、发布与配置数据时，安全治理不再是工程细节，而是架构的一级能力。对强合规行业尤其如此：能力建成之日，就是它成为一个高价值数据聚合点之时。

### 9.1 权限继承，以及聚合本身的风险

基本原则：

> 联合查询结果的可见范围，不得超过用户在各原始数据源中的合法权限范围。

联合分析平台不得以“已统一接入”为由绕过底层数据源的权限检查。但实现方式需要务实：存量后端未必都支持用户级委托授权，不少系统只接受平台服务身份。因此原则应表述为：

> **有效调用主体和授权上下文必须贯穿整条查询链路。**源端支持委托授权（delegated authorization）时，由源端执行鉴权；源端不支持时，由受信查询代理执行等价的最小权限控制，并完整审计实际调用主体。

不可接受的是查询链路上调用主体丢失——即无论哪种实现，都不能出现“查询到达数据源时只剩一个平台账号，无法追溯是谁发起的”。

查不到与无权限必须区分表达（`query_status = PERMISSION_DENIED`，见 6.2 节），不能合并为“暂无数据”——否则用户无法判断是系统没有，还是自己看不到。

但仅有权限继承并不充分。**逐源合法不等于聚合合法**：把日志正文、调用链参数、Profile 代码栈、资产归属和发布记录聚合到同一个查询面，合成出的全景视图，其敏感度可能高于任何单一来源。这一点需要单独论证，至少回答：

- 哪些字段组合在聚合后构成新的敏感等级；
- 是否需要对聚合视图设置独立于各源的访问审批；
- 跨应用、跨条线的横向查询是否需要额外授权。

### 9.2 智能体的两种调用身份

智能体访问多源数据时，必须先区分调用模式，二者的授权范围不能合并：

| 模式 | 场景 | 身份 | 授权建议 |
| --- | --- | --- | --- |
| 交互式 | 工程师在会话中请智能体协助排查 | **on-behalf-of 当前用户** | 权限严格等同该用户；查询预算按会话计 |
| 自主 / 定时 | 告警自动触发、巡检、批量分析 | 平台服务账号 | **目的绑定 + 最小权限 + 敏感字段受限 + 横向扩展受控**（见下） |

自主模式的授权范围不宜用“比人工用户更窄”来定义。跨多应用巡检的诊断账号，其横向覆盖面本就可能大于任何一位应用工程师，机械比较没有意义。应当约束的是四件事：

- **目的绑定（purpose-bound）**：每个自主账号绑定明确用途，不得复用于其他场景；
- **最小权限**：仅授予完成该用途所需的最小字段与操作集合；
- **敏感字段受限**：默认不可读取日志正文等敏感内容，需要时单独申请并审计；
- **横向扩展受控**：不得自动展开到关联基础设施或跨条线查询，递归深度设上限（见附录 C.1 的 `max_recursive_depth`），超出范围须转为人工确认。

无论哪种模式，是否允许自动展开、是否允许读取日志正文、是否允许跨应用扩大范围，默认均应为否，按需逐项开通并留痕。

### 9.3 查询与证据审计

跨源诊断的每一次查询都应留痕，且审计记录本身要能支撑事后复查：

```text
谁发起查询（人 / 智能体，何种调用模式）
查询了哪些数据源
查询条件与时间范围
访问了哪些实体
使用了哪个映射与关联规则版本
返回状态（四维）与是否完整
智能体据此形成了什么判断、引用了哪些证据
```

其中“使用了哪个规则版本”一项常被遗漏，但它是诊断结论可复现的前提（附录 A.4）。

### 9.4 敏感信息与缓存治理

| 对象 | 治理要求 |
| --- | --- |
| 日志正文 | 敏感字段脱敏须在联合查询层之前完成，不能依赖界面遮蔽；脱敏规则版本纳入审计 |
| Profile 数据 | 包含方法名、类名、文件路径等代码结构信息，属于需要保护的资产，不应默认对全员开放 |
| 查询缓存 | 缓存键必须绑定租户、授权范围、时间窗口和映射版本，防止命中旧身份或越权结果 |
| 诊断快照 | 明确保留时长与访问授权；快照冻结的是证据，不应随实体关系变更而被改写 |
| 送入大模型的数据 | 是否允许将跨源查询结果送入大模型、送入哪一类模型、是否脱敏、是否留存，须有明确策略而非默认放行 |

这些要求应在试点阶段即纳入设计，而不是等推广时再补。权限与审计属于横切能力，后补的代价通常高于同期建设。

## 附录 A：工程设计中不能省略的约束

### A.1 最小实体与关联契约

这里“契约”指各系统共同遵守的字段含义、身份规则和版本约定，不是要求统一所有原始数据格式。

| 记录 | 最少应说明什么 | 主案例中的对应内容 |
| --- | --- | --- |
| 实体 | ID、类型、租户/环境作用域、身份版本、生命周期 | svc-042、I17、proc-731-A |
| 别名映射 | 来源、原身份、统一实体、有效期、规则版本、证据 | trade-order 在某作用域下属于 svc-042 |
| 关系 | 两端实体、关系类型、有效期、观察时间、来源 | proc-731-A 当时运行在 C17 |
| 信号引用 | 原记录或序列引用、时间、实体角色、执行上下文、采样与查询状态 | T7/S2 的日志和 Profile 样本引用 |

建议采用左闭右开有效期；优先稳定 ID，再按作用域和历史别名匹配。时间误差跨越身份切换点、映射多解或来源冲突时，返回歧义或未知。迟到关系允许修订，但保留版本，以便重现原诊断。跨生命周期的聚合数据若不能准确拆分，应保留混合范围标记。

权威性须按字段划分：服务目录负责 `service_identity` 与 `business_service_owner`，CMDB 负责 `asset_owner`、`cost_center`、`physical_location`，编排控制面负责 Pod 生命周期，运行时负责 `running_artifact` 与 `process_identity`；没有一个系统天然是所有字段的权威源。最初可用关系型表和索引实现，不要求先采购图数据库。

对实体关系、别名映射和精确关联，建议统一保存来源与血缘字段，使“这条关系从哪来、什么时候成立、由哪版规则产生”可被查证：

```text
source_system     事实来自哪个系统（服务目录 / K8s / 发布平台 / 运行时 / 人工）
source_record_id  在该系统中的原始记录标识
observed_at       该事实被观察到的时间
valid_from        关系生效时间
valid_to          关系失效时间（未失效则为空）
rule_version      产生该关系所用的规则或映射版本
confidence        确定性等级（对应 6.2 节 correlation_status）
generated_by      自动规则 / 人工修正 / 源端直供
```

字段级定义与取值约束见配套分册《联合分析数据契约与治理规范》。

### A.2 数据角色与单位不能混淆

应区分 producer（谁产生数据）、subject（数据在描述谁）、peer（请求对端）。数据库 Exporter 的宿主机不等于被监控数据库的宿主机；应用客户端 Span 也不能证明数据库部署在应用主机上。

度量字典应定义：单位、指标类型、累计/增量、统计窗口、维度、统计人口、重置规则。各实例 P99 的平均值不是服务总体 P99；换算单位也不能修复分布已经丢失的问题。[S17：分位数与聚合](https://prometheus.io/docs/practices/histograms/)。

### A.3 请求传播、重复与缺失

旧 APM ID 需保留原值并定义转换规则；不能任意截断或填充后宣称身份相同（存量多版本并存的处理见第 6.3 节）。新旧传播器共存应测试网关、RPC、异步和消息边界。重试需区分执行尝试，批处理的一对多/多对一关系可用显式链接表达。[S11：W3C](https://www.w3.org/TR/trace-context/)、[S24：Span Links（Trace API 规范）](https://opentelemetry.io/docs/specs/otel/trace/api/)。

Exemplar 应验证生成、转发、存储、查询、界面和 Trace 留存全路径，并把 Exemplar 自身的留存窗口作为独立于 Trace 留存的参数（见 3.2 节）。Profile 需验证类型、采样频率、进程身份、构建 ID、符号文件和运行时支持。eBPF 是可选采集手段，不自动等于能感知语言协程或 Span；按目标环境实测。

**Span 级 Profiling 的可得粒度按语言和运行时差异很大，必须先实测再排期。**不同语言在“能否把采样样本绑定到 Span”上的能力并不一致：有的依赖运行时提供执行上下文，有的需要额外的采样器扩展，有的只能做到进程级聚合。第 7.1 节第⑤步的工作量完全取决于这张表，因此应在选案例阶段先填空，而不是假设四类信号都能一步到位：

| 目标语言/运行时 | 可得最细粒度（进程级 / Span 级） | 依赖的采样器与集成方式 | 实测开销（CPU / RSS / 延迟） | 结论（本轮是否纳入） |
| --- | --- | --- | --- | --- |
| （按试点范围逐行填写） | | | | |

该表留空是有意为之：各语言的支持状态随采样器版本变化，应以目标运行时上的实测结果填写，不引用厂商宣传矩阵。对以单一语言为主的存量环境，这张表通常只需 1—2 行，但不能省略。

采集双写需明确唯一统计来源和退出期限。原始日志、由该日志派生的指标、摘要中的同一事件，不能当作三份独立证据。查询超时、权限不足、部分返回与源数据缺失要分别记录。

### A.4 诊断结果要能复查

每份判断保存：影响对象和窗口、源记录/查询、关联依据、机制假设、支持与反证、缺口、验证结果及结论范围。若原始数据会过期，按授权和保留策略保存必要快照及映射版本，不能只保存深链接。

SRE/诊断团队负责故障假设和验证；平台团队负责基础关联与数据质量；应用团队负责正确埋点与传播；基础设施团队负责运行身份。后续智能体也应使用这些证据和受控查询，不能用推理填补不存在的数据。

## 附录 B：验收口径与反例用例

### B.1 分别评价覆盖、准确、速度和成本

下列阈值是**首轮 POC 的建议讨论起点**，不是行业统一指标，也不是已经达到的结果。冻结前须结合故障风险、负载、样本规模和现有基线调整。所有指标报告分子、分母和样本构成，不仅报告百分比。

| 指标 | 计算或测量方法 | 建议验收方式 |
| --- | --- | --- |
| 实体映射覆盖率 | 正确且唯一映射的对象数 / 试点应覆盖对象数；另报按流量加权覆盖 | 可先以 ≥95% 为试点目标，关键对象单列 |
| 精确关联（EXACT）准确率 | 经独立核验正确的 EXACT 关联边数 / 已核验 EXACT 关联边总数，**按关联类型分别统计** | **测试集中 EXACT 的任一 false positive 为阻断项**；同时报告样本量、构成与统计不确定性。跨租户错误关联同为阻断项 |
| 候选关联（CANDIDATE）质量 | 启发式候选搜索的 Precision、Recall、Top-K 命中率，同样按关联类型分别统计 | 单独统计，**不得与 EXACT 混合计算**；候选结果在界面和契约中必须标明为候选 |
| 请求上下文覆盖率 | 具备可用关联上下文的适用请求日志 / 适用请求日志 | 不把无请求上下文的系统日志混入分母；可先以 ≥95% 为目标 |
| 关联实际命中率 | 成功取得目标证据的关联尝试 / 全部合法关联尝试 | 同时报告原始命中率及已确认采样/过期影响，禁止只报过滤后指标；**Exemplar 自身留存须作为独立于 Trace 留存的变量单独报告**，否则该指标会在高流量服务上无法解释地劣化 |
| 查询体验 | 代表性并发、窗口、数据量下的 P95 时延、超时率 | 可从常用查询 P95 ≤5 秒讨论，须限定查询类型和负载 |
| 诊断效率 | 相同案例集达到正确定位所需时间的中位数和 P90 | 与原流程对照，控制人员熟练度与案例记忆；例如中位数降低 ≥30% |
| 诊断正确性 | 正确结论、错误结论、无法判断分别计数 | 在已知真值且不向诊断者泄露的案例上评估，不把拒答计为正确根因；**拒答率须与有效回答覆盖率同时验收**，不得靠扩大拒答换取表面准确率 |
| 采集与平台成本 | 应用 CPU/RSS/延迟、网络量、存储、扫描量、运维工时 | 按服务约定预算；必须测量新增开销而非引用宣传值 |

覆盖率还应分别按信号、服务、语言和环境统计，防止少数高流量服务掩盖关键小流量系统的缺口。小样本“零错误”不能证明生产准确率达到 99%，应报告样本量及统计不确定性——这一点同样适用于上表的 EXACT 阻断项：**测试集零错误是准入门槛，不能据此推断生产环境 false positive 为零。**

统计单位必须是**关联边**而不是证据记录，且**按关联类型分开**：请求/执行关联误判，会把不相干的日志或样本塞进本次诊断；实体关系关联误判，会把证据归到错误的版本或实例上。两者的后果、排查路径和阻断条件都不同，混在一个比率里等于抹掉了 6.2 节刚建立的区分。

这里体现的是一条贯穿全文的取舍：**对诊断系统而言，错误地声称“精确关联”比返回“不确定”危险得多。**在智能体场景下尤其如此——一条错误关联的日志或 Profile 很可能直接诱导出错误根因。因此关联结果应分为 `EXACT` / `CANDIDATE` / `AMBIGUOUS` / `UNKNOWN` 四级（6.2 节），不能确定时应主动降级，宁可不知道，也不能精确地关联错。

诊断效率与定位时间受人员熟练度、故障类型和当班经验影响很大，改善不能直接归因于本平台。运维价值类指标必须沿用同一对照设计（同案例集、控制熟练度与案例记忆），否则会得出无法归因的结论。POC 验收建议拆为技术质量组与运维价值组分别统计，另需纳入联合分析平台自身的可用性、查询成功率、映射同步延迟、PARTIAL 与 AMBIGUOUS 比例、权威源数据新鲜度等自观测指标——**联合分析平台本身也必须可观测，不能只依赖被它查询的四类系统**。完整指标清单与计算口径见配套分册。

### B.2 必测反例与失败路径

| 用例 | 期望表现 |
| --- | --- |
| 不同租户/环境存在同名服务 | 不串联、不泄露 |
| Pod 重建、IP 复用、进程 PID 复用 | 旧数据仍关联原对象，新数据不会误归属 |
| 服务改名、滚动发布、新旧版本同时存在 | 服务历史连续，部署与进程正确区分 |
| CMDB 与运行时关系冲突或迟到 | 展示冲突/未知，历史关系可修订且可追溯 |
| 日志乱序、时钟偏差、采集延迟 | 不使用到达顺序伪造执行顺序；可按偏差范围扩大候选窗口 |
| 消息重试、批处理、异步跨线程、上下文断裂 | 不混淆不同尝试，不把批处理强制拼成单父链 |
| Trace 采样但日志保留 | 有 ID 却无 Trace 时给出有依据的状态或未知 |
| Exemplar 存在但目标 Trace 过期 | 明确不可获取，不跳到其他 Trace |
| Exemplar 环形缓冲被覆盖或超出 WAL 期 | 区分“无 Exemplar”与“有 Exemplar 但目标不可得”，不合并为同一状态 |
| 新旧 APM 身份空间无法判等 | 返回 `correlation_status = AMBIGUOUS`，不做截断或补零后的就近匹配 |
| 跨 Canonical Trace Context 切换点查询历史 | 显式提示该窗口关联降级，不把旧身份空间的记录当作 canonical 命中 |
| 用户对某一数据源无权限 | 返回 `query_status = PERMISSION_DENIED`，不与“无记录”合并显示 |
| 智能体以服务账号自主触发跨应用查询 | 无对应授权策略时拒绝或转人工确认；有目的绑定授权时仅在查询预算范围内执行（9.2 节） |
| 数据源未提供采样审计证据 | `data_status` 返回 `UNKNOWN`，不得推断为 `SAMPLED_OUT` |
| 权威源之间字段冲突（CMDB / K8s / 运行时） | 按预定义裁决规则处理并保留冲突记录，不静默覆盖 |
| Profile 有标记无样本、符号不全、只有进程聚合 | 显示限制，不输出虚假的 Span 级代码归因 |
| 指标重置、单位差异、P99 跨实例聚合 | 正确处理或拒绝无效比较 |
| 查询源超时/无权限、分页截断、源数据不全 | 返回部分结果与原因，不冒充完整无异常 |
| Collector 或实体服务不可用 | 原有监控继续运行；新增能力按设计降级 |

这些都是待执行用例。应用上线判断应基于执行记录、失败修复及回归结果，而不是用例表是否齐全。


## 附录 C：查询、采样与成本控制

### C.1 联合查询应先缩小范围再取明细

建议按“业务症状→实体与时间→部署/拓扑→明细证据”逐步查询，给每类源设置并发、扫描窗口、返回量和超时预算。以用户可感知的业务症状作为排查入口、再向下钻取，与 Google SRE 对分布式系统监控的经典建议一致。[S21：以症状为入口的监控](https://sre.google/sre-book/monitoring-distributed-systems/)。避免在每次告警时跨所有信号执行全量 Join。缓存与结果持久化应包含租户、授权范围、映射版本和时间范围，防止命中旧身份或越权结果。

时间对齐应保留事件时间、采集观察时间和入库时间。OTel 日志模型区分 `Timestamp` 与 `ObservedTimestamp`；它们本身不保证时钟同步。[S8：Logs Data Model](https://opentelemetry.io/docs/specs/otel/logs/data-model/)。

但仅靠“把窗口放宽 ±N 秒”并不足够。当日志、Trace、Profile 和变更事件来自不同时间源时，**时间质量本身应作为关联契约的一部分**，随结果一起返回：

```text
event_time             事件实际发生时间
observed_time          采集端观察到的时间
ingest_time            入库时间
clock_offset_estimate  该来源相对基准的时钟偏差估计
time_uncertainty       综合误差边界
```

据此，结果中应显式给出时间误差边界，而不是在扩大窗口后直接宣称精确关联。误差过大时应降级为 `CANDIDATE` 或 `AMBIGUOUS`。

同样，查询预算不应只是各适配器的内部参数，而应形成统一契约（`max_time_range`、`max_entities`、`max_sources`、`max_scan_bytes`、`max_results`、`timeout`、`max_recursive_depth`）。智能体尤其需要限制自动扩大查询范围和递归深度，避免一次告警触发跨四个平台的大范围扫描。字段定义见配套分册。

### C.2 采样与保留策略要为联合分析设计

建议为错误、慢请求和关键版本配置合适的采样与保留策略，并明确日志、Trace、Profile 的关联可用窗口。头部采样、尾部采样的机制不同；尾部采样只能在收到的数据上作决定，不能补回先前未记录的 Span。[S22：OTel Sampling](https://opentelemetry.io/docs/concepts/sampling/)。

由已采样 Trace 计算错误率或延迟分布，需要明确采样位置与概率，尤其不能直接将偏向错误/慢请求的尾部采样数据当作总体分布。建议把业务总体指标与诊断抽样各自标明来源和统计人口。

### C.3 用可填参数的成本模型代替无依据预算

建议按以下项目测算增量成本，不预设金额或人月：

```text
实施成本 = 实体与语义治理 + 数据源适配 + 应用/网关改造
         + 查询工作台 + 联调验收 + 迁移与培训

持续成本 = 新增采集 CPU/内存 + 网络传输 + 存储与索引
         + 查询计算 + 历史关系服务 + 规则/适配器维护

粗略存储量 = 每日新增保留数据量 × 保留天数 × 副本系数
           + 索引/元数据开销
```

数据压缩、冷热分层、托管计费和日志索引策略会改变测算，需要用试点实测数据替换参数。已有四类平台并不意味着整合免费，但也不意味着四套存储都需要重新采购。

成本模型中必须单列一项高基数风险：**Trace ID、Span ID、Profile 标签等请求级高基数字段，若错误进入普通指标标签，或不加控制地进入日志与 Trace 索引，会显著放大存储与查询成本**。这与 3.2 节“不要给每条普通指标序列加请求 ID”是同一条原则的成本侧表述；6.3 节的请求级 ID 映射同样属于高基数数据，须单独测算并设定 TTL。

对涉及多团队的工作，按“工作项×责任角色×投入时间”核算供给。共享标签治理、服务目录维护和源端上下文改造必须有明确负责人，否则统一模型会持续退化。


## 参考资料与使用说明

以下均为一手规范或官方文档。S1—S22 的访问日期为 2026-09-17，S23—S26 为 v1.2 修订时新增、S27 为 v1.3 修订时新增，访问日期为 2026-09-19；S7、S10、S26 三条的稳定性状态于 2026-09-19 复核。`latest` 与滚动规范页面可能更新，版本状态以访问时内容为依据；实际实施应另行冻结所选版本。

“检索时状态”列仅在来源页面自身明确标注稳定性时填写。**未标注不等于稳定**，只表示该页面未给出状态声明，冻结版本时须逐条确认。文中架构、阶段划分、阈值与诊断分类属于本文建议，不应视为这些来源发布的统一标准。

| 编号 | 官方来源 | 本文使用范围 | 检索时状态 |
| --- | --- | --- | --- |
| S1 | [Grafana — Correlations](https://grafana.com/docs/grafana/latest/administration/correlations/) | 跨数据源查询与跳转 | 页面未标注 |
| S2 | [Datadog — Unified Service Tagging](https://docs.datadoghq.com/getting_started/tagging/unified_service_tagging/) | 服务、环境、版本标签 | 页面未标注 |
| S3 | [Dynatrace — Grail Architecture](https://docs.dynatrace.com/docs/platform/grail/dynatrace-grail/architecture) | 多来源、语义与拓扑处理 | 页面未标注 |
| S4 | [Elastic — ECS Tracing Fields](https://www.elastic.co/docs/reference/ecs/ecs-tracing) | ECS 追踪字段及 OTel 映射 | 页面未标注 |
| S5 | [OpenTelemetry — Collector](https://opentelemetry.io/docs/collector/) | 异构接入、组件成熟度 | 页面未标注 |
| S6 | [OpenTelemetry — Trace Context in non-OTLP Log Formats](https://opentelemetry.io/docs/specs/otel/compatibility/logging_trace_context/) | 非 OTLP 日志关联 | 页面未标注 |
| S7 | [OpenTelemetry — Entity Data Model](https://opentelemetry.io/docs/specs/otel/entities/data-model/) | 身份模型及 Development 状态 | Development（2026-09-19 复核） |
| S8 | [OpenTelemetry — Logs Data Model](https://opentelemetry.io/docs/specs/otel/logs/data-model/) | 时间、Trace/Span 字段 | 页面未标注 |
| S9 | [OpenTelemetry — Metrics Data Model](https://opentelemetry.io/docs/specs/otel/metrics/data-model/) | Exemplar 关联机制 | 页面未标注 |
| S10 | [OpenTelemetry — Profiles](https://opentelemetry.io/docs/specs/otel/profiles/) | Profile 上下文与 Alpha 状态 | Alpha（2026-03-26 进入 public Alpha；2026-09-19 复核） |
| S11 | [W3C — Trace Context](https://www.w3.org/TR/trace-context/) | 跨系统追踪上下文 | 页面未标注 |
| S12 | [OpenTelemetry — Overview](https://opentelemetry.io/docs/specs/otel/overview/) | 信号、语义与 Span Links | 页面未标注 |
| S13 | [Datadog — Correlate Logs and Traces](https://docs.datadoghq.com/tracing/other_telemetry/connect_logs_and_traces/) | 日志注入与独立采样限制 | 页面未标注 |
| S14 | [Grafana Pyroscope — Profiling Types](https://grafana.com/docs/pyroscope/latest/introduction/profiling-types/) | CPU、分配、锁、阻塞等类型 | 页面未标注 |
| S15 | [OpenTelemetry — Service Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/resource/service/) | 服务和实例身份 | 页面未标注 |
| S16 | [OpenTelemetry — Prometheus and OpenMetrics Compatibility](https://opentelemetry.io/docs/specs/otel/compatibility/prometheus_and_openmetrics/) | 格式与语义映射 | 页面未标注 |
| S17 | [Prometheus — Histograms and Summaries](https://prometheus.io/docs/practices/histograms/) | 分布与聚合边界 | 页面未标注 |
| S18 | [Prometheus — Metric and Label Naming](https://prometheus.io/docs/practices/naming/) | 指标与标签命名规范 | 页面未标注 |
| S19 | [Grafana Pyroscope — Span Profiles](https://grafana.com/docs/pyroscope/latest/configure-client/trace-span-profiles/) | 请求级 Profile 机制与采样限制 | 页面未标注 |
| S20 | [OpenTelemetry — Transforming Telemetry](https://opentelemetry.io/docs/collector/transforming-telemetry/) | 转换、补充元数据及性能影响 | 页面未标注 |
| S21 | [Google SRE — Monitoring Distributed Systems](https://sre.google/sre-book/monitoring-distributed-systems/) | 面向业务症状的监控入口 | 页面未标注 |
| S22 | [OpenTelemetry — Sampling](https://opentelemetry.io/docs/concepts/sampling/) | 采样机制及权衡 | 页面未标注 |
| S23 | [Prometheus — Instrumentation（Do not overuse labels）](https://prometheus.io/docs/practices/instrumentation/) | 标签基数的具体经验阈值 | 页面未标注 |
| S24 | [OpenTelemetry — Trace API（Link）](https://opentelemetry.io/docs/specs/otel/trace/api/) | Span Links 的规范性定义 | Stable |
| S25 | [Prometheus — Feature flags（exemplar-storage）](https://prometheus.io/docs/prometheus/latest/feature_flags/) | Exemplar 的开关、存储实现与留存边界 | 实验特性（默认关闭） |
| S26 | [OpenTelemetry — OTLP Specification](https://opentelemetry.io/docs/specs/otlp/) | OTLP 各信号的稳定性状态 | trace/metric/log 为 Stable；profiles signal 为 Development（2026-09-19 复核） |
| S27 | [OpenTelemetry — Profiles 概念与关联模型](https://opentelemetry.io/docs/concepts/signals/profiles/) | Profile 与 Trace/Span、Resource 的双向关联描述 | 概念页，随信号成熟度更新 |
