目 录CONTENT

文章目录

指标、日志、链路三层可观测:一次可关联排障能力建设复盘

zhanjie.me
2026-02-07 / 0 评论 / 0 点赞 / 10 阅读 / 0 字

这是“微服务治理与平台化演进”系列的第四篇。
前三篇分别解决了“服务怎么跑、跑在哪、怎么进来”,这一篇继续回答:服务出了问题以后,平台怎么把异常发现、路径还原和证据回溯真正串起来。

摘要

这次三层可观测平台建设表面上看,是把 Prometheus、ELK、OpenTelemetry 这些能力分别搭起来;但如果只把它理解成“三套工具落地”,意义其实被低估了。真正完成的,并不是把指标、日志、链路各自采上来,而是让它们在故障场景下形成一条能走通的定位链路:指标负责发现异常,链路负责还原请求传播路径,日志负责补足实例级证据,而 trace id / span id 则把日志与链路真正串了起来。本文会从运维实践和架构演进两个角度,复盘三层信号为什么必须同时建设、分别承担什么职责,以及它们如何被组织成一次真正能落地的排障闭环。

系列导航

0. 导语:可观测平台不是三套工具,而是一条定位链路

很多团队做可观测建设时,最容易按组件来理解这件事:

  • Prometheus 是指标
  • ELK 是日志
  • OpenTelemetry / Tempo 是链路

这种拆法没有问题,但如果文章也照着这个顺序去写,很容易把“可观测平台建设”写成“三套工具分别搭起来了什么样子”。

可对微服务平台来说,真正重要的并不是“我们有三套系统”,而是:

当故障发生时,能不能从一个异常信号出发,一路走到具体证据。

服务 RT 抖了,指标会先告诉你。
某个请求超时了,链路会把经过了哪些服务、卡在哪一跳告诉你。
而真正落到具体实例级报错细节时,你又往往还得回到日志里看:

  • 是数据库连接异常
  • 是某个 SQL 超时
  • 是下游返回错误
  • 还是某个线程池已经打满

也就是说,指标、日志、链路并不是三套互相替代的能力,而是三种不同粒度的观测信号。

在我们的平台实践里,这三层也并不是彼此独立地存在。尤其是由于微服务里通过 Java Agent 注入了链路上下文,日志框架可以在输出时带上 trace idspan id,这意味着日志和链路不再是两套平行系统,而是第一次拥有了稳定的关联键。

因为一旦链路中的一次调用和实例上的一条日志可以被稳定关联,那么排障过程就不再是“三套系统里各自翻一遍”,而开始变成一条真正能走通的定位路径:

  1. 先通过指标发现异常
  2. 再通过链路还原请求传播路径
  3. 最后通过 trace id / span id 回到日志,拿到实例上的具体证据

从这个角度看,可观测平台建设真正要解决的,从来不是“信号有没有采到”,而是这些信号能不能在故障场景下串成一条定位链路。

图 1:指标负责发现异常,链路负责还原路径,日志负责补足证据,而 trace id / span id 则把日志与链路连接起来。

flowchart LR A["异常指标"] --> B["异常 Trace"] B --> C["trace id / span id"] C --> D["日志检索"] D --> E["实例级错误细节"]

1. 为什么微服务不能只做指标,或者只做日志

在很多系统里,可观测建设往往是从一个最痛的问题先开始补的。

有的团队先补指标,因为最先感受到的是接口 RT、错误率、资源使用率缺少统一视图;有的团队先补日志,因为排障时最直接的需求就是查错误堆栈和实例行为;也有一些团队会优先做链路,因为微服务之间调用太多,光看单点日志已经不知道问题是在哪一跳开始放大的。

这些选择都很自然。
但如果把时间拉长,就会发现:只做其中一层,最终都不够。

1.1 只有指标,你知道“哪里不对”,但不知道“发生了什么”

指标层最大的价值,是它提供了一个系统健康面的宏观视角。

你可以很快看到:

  • 某个接口 RT 是否突然升高
  • 某个服务错误率是否开始波动
  • 某个 Pod 或节点的 CPU / 内存是否异常
  • 某段时间内流量是否发生明显变化

但指标层也有很明确的边界:它通常更擅长告诉你“哪里开始不对了”,却不擅长直接告诉你“到底发生了什么”。

1.2 只有日志,你看到“发生了什么”,但很难建立整体视角

日志特别适合回答“具体发生了什么”这类问题。它能看到实例行为细节,能看到错误堆栈,能看到数据库、中间件、业务代码在某个时刻真实输出了什么信息。

但当系统进入微服务阶段之后,日志层的问题也会开始显现:

  • 日志天然是离散的
  • 它依附在实例或 Pod 上
  • 它很难天然提供全局健康面的概览
  • 它也不擅长直接回答“问题是从哪里开始扩散的”

1.3 只有链路,你能看到请求路径,但很多细节仍然缺失

链路层能告诉你:

  • 一次请求经过了哪些服务
  • 每一跳耗时如何变化
  • 哪一跳开始出错
  • 问题是入口触发、消费者扩散还是 provider 放大

但链路层也不是万能的。它很擅长还原调用路径,却不一定能完整替代实例级证据。链路更像是“把问题带到你面前”,而日志则更像是“把发生过什么具体写下来”。

1.4 微服务可观测真正需要的是三层分工,而不是单层做大

回头看这三层的边界,其实很清楚:

  • 指标负责发现异常
  • 链路负责还原路径
  • 日志负责补足证据

这意味着它们最大的价值,不是互相替代,而是各自承接不同粒度的观测问题。

2. 三层信号如何分工:健康、路径与证据

如果继续往下问,一个很自然的问题就是:指标、日志、链路这三层,到底应该分别承担什么职责?

在我们的实践里,更合适的理解方式是:这三层并不是三套平行工具,而是三种不同粒度、不同语义的观测信号。

2.1 指标层:系统健康面的宏观信号

指标层最适合回答的问题,是“哪里开始不对了”。

它面向的是健康面,而不是单次请求,也不是某一条具体错误日志。它关心的是:

  • 服务整体 RT 是否升高
  • 错误率是否开始波动
  • QPS 是否发生异常变化
  • 节点和 Pod 的资源使用是否进入危险区间
  • 某个工作负载是否正在变得不稳定

2.2 链路层:请求传播路径的上下文信号

链路层解决的是另一类问题:当异常已经发生之后,它到底是沿着哪条调用路径传播和放大的。

它回答的问题通常是:

  • 一个请求经过了哪些服务
  • 哪一跳开始耗时变长
  • 哪一跳开始出现错误
  • 是入口层触发,还是下游依赖放大
  • 是局部调用抖动,还是整条链路已经受影响

2.3 日志层:实例行为与错误细节的证据信号

日志层负责的则是第三类问题:具体实例上,到底发生了什么。

它能看到:

  • 哪条 SQL 失败了
  • 哪个中间件连接超时
  • 哪个业务异常被抛出
  • 哪个线程池已经打满
  • 某条请求在实例内部执行到了哪里

2.4 日志与链路并不是割裂的两层

在我们的实践里,日志层和链路层并不是完全独立的。因为微服务通过 Java Agent 注入了链路上下文,而日志框架又可以在输出时把 trace idspan id 写进日志。

这意味着:

  • 链路中的一次调用
  • 和实例上的一条日志
  • 并不是两个互不相干的对象

它们第一次拥有了一个稳定的关联键。

2.5 三层不是并列罗列,而是一次定位过程中的不同视角

从这个角度看,三层信号之间更像是一次定位过程中的三种视角:

  • 指标视角:先发现哪里异常
  • 链路视角:再看异常是怎么传播的
  • 日志视角:最后补齐具体实例证据

trace id / span id 则让链路视角和日志视角之间,第一次拥有了稳定的连接点。

指标负责发现异常,链路负责还原路径,日志负责补足证据,而 trace id / span id 则把日志与链路真正连成了一条线。

3. 指标层:平台健康面的异常发现入口

在三层可观测里,指标层往往是最先被看到的一层。因为在真实故障场景中,团队通常不是先从日志开始发现问题,也不是先从链路开始发现问题,而是先在某个健康面上看到异常:

  • 某个接口 RT 开始升高
  • 某个服务错误率开始抖动
  • 某些 Pod 重启或资源占用突然异常
  • 整个系统某个时段的吞吐开始波动

也就是说,指标层本质上是平台的异常发现入口。

3.1 指标采集为什么要贴近运行面

在我们的平台设计里,Prometheus 放在 Kubernetes 集群内部,直接承接集群和工作负载相关指标采集。

它可以更直接地看到:

  • 节点状态
  • Pod 状态
  • Service 与工作负载状态
  • 容器资源使用情况
  • 微服务自身暴露出来的业务指标

所以在我们的设计里,Prometheus 更像是“贴着平台跑”的指标入口层,而不是一个远离运行面的旁路系统。

3.2 指标汇聚为什么又不应该和业务运行面强耦合

虽然指标采集贴近业务运行面,但指标的长期存储与统一汇聚,我们并没有继续强绑在业务集群里,而是通过 remote_write 写到跨集群部署的 VictoriaMetrics,再由 Grafana 做统一展示。

整体结构大致是:

Prometheus(k8s 内部) -> remote_write -> VictoriaMetrics -> Grafana

这背后的思路是:

  • 采集层尽量贴近运行面
  • 汇聚层与存储层适度脱离运行面
  • 展示与分析层可以继续统一承接

3.3 指标层到底解决了什么问题

如果回到故障定位视角,指标层最核心的价值其实很明确:它让系统异常第一次拥有了一个可快速发现的入口。

比如当问题出现时,指标层能最快帮助回答这些问题:

  • 异常从什么时候开始
  • 是哪一个服务先开始波动
  • 是 RT 异常、错误率异常,还是资源异常
  • 是个别实例问题,还是整个服务面问题
  • 影响范围是在入口服务、消费者,还是某类 provider

3.4 指标层的价值不在“图画得多”,而在“异常被看见得早”

很多团队做指标建设,最后容易变成 Grafana Dashboard 数量竞赛。图当然重要,但图的数量并不等于平台的异常发现能力。

对我们来说,指标层真正重要的并不是“有多少面板”,而是它能不能让平台尽早发现下面这类变化:

  • 某类服务开始抖动
  • 某组实例资源逼近边界
  • 某次发布之后错误率突然升高
  • 某条链路上的入口服务延迟开始异常

指标层最重要的价值,不是画了多少图,而是让平台第一次拥有了一个能快速发现异常的健康视角。

4. 日志层:实例行为与错误证据的落点

如果说指标层解决的是“哪里开始不对了”,那么日志层解决的就是另一个同样关键的问题:具体实例上,到底发生了什么。

4.1 日志在平台里不是“文件”,而是实例证据

在传统部署阶段,日志很容易被理解成“应用在某台机器上写出来的文件”。但到了容器平台阶段,这种理解已经不够了。因为实例本身变成了动态工作负载,Pod 会漂移、会重建、会扩缩容,如果日志仍然只按主机视角管理,那么它很快就会失去和运行实例之间的稳定关联。

在我们的平台里,日志更适合被理解成一种“实例证据”。也就是说,它不是简单的文本输出,而是某个具体运行实例在某个具体时间窗口里的行为记录。

4.2 日志采集如何贴近工作负载组织

在我们的设计里,业务日志首先会落到宿主机目录,然后由 FilebeatDaemonSet 的方式部署在集群中,贴近节点侧进行采集。之后日志会跨集群进入 Kafka,再由 Logstash 进行处理,最终写入 Elasticsearch,通过 Kibana 完成检索与分析。

整个链路大致是:

Filebeat(DaemonSet) -> Kafka -> Logstash -> Elasticsearch -> Kibana

比如在落盘路径上,我们会通过 hostPath + subPathExpr 让日志目录按 namespace_podname 的维度进行组织。这样带来的好处很直接:

  • 日志归属和运行实例天然绑定
  • 一个实例对应一组相对清晰的日志空间
  • 排障时更容易定位到具体 workload
  • 节点级采集和实例级定位之间更容易衔接

4.3 日志层真正负责的,不是“收集文本”,而是保留证据

如果回到排障场景,日志系统最重要的职责其实非常明确:

  • 保留实例上的行为痕迹
  • 记录错误发生时的上下文
  • 承接那些只有实例自己才知道的细节

比如在真实问题里,日志层最常提供的是这类信息:

  • 某次 SQL 执行异常
  • 某个中间件连接超时
  • 某条接口抛出的业务错误
  • 某个线程池拒绝请求
  • 某次重试触发或熔断生效
  • 某个依赖返回了什么错误码

4.4 日志层的边界:它不能独立完成全局定位

日志非常擅长提供细节,但它不擅长提供全局健康视角。

如果平台只有日志,那么排障过程很容易变成:

  • 先猜一个可能有问题的服务
  • 去搜这个服务的日志
  • 再翻很多实例级输出
  • 过程中不断修正自己的判断

所以日志层最合适的角色,从来不是单独承担全部观测能力,而是作为最终证据层存在。

日志层真正负责的,不是“把文本收上来”,而是给每一次异常提供可追溯的实例级证据。

5. 链路层:请求传播路径的上下文还原

如果说日志层让我们看到了实例上的细节,那么链路层解决的,就是另外一个在微服务场景下同样关键的问题:一个异常请求,到底是沿着哪条路径在系统里传播的。

5.1 链路层不是“调用图可视化”,而是请求传播上下文

在我们的平台里,链路数据主要通过 OpenTelemetry Java Agent 注入到微服务运行时,再由 Pod 内的应用把 trace 信息发送到 K8s 内的 OTel Collector,随后跨集群进入 Kafka,再由后续 Collector 和存储组件处理,最终落到 ClickHouse / Tempo 并通过 Grafana 查询。

整体链路大致是:

OpenTelemetry Java Agent -> OTel Collector(k8s 内) -> Kafka -> OTel Collector -> ClickHouse / Tempo -> Grafana

对我们来说,更重要的是它提供的治理价值:它让一次请求在系统里如何传播、在哪一跳开始变慢、在哪一跳开始失败,第一次拥有了统一上下文。

5.2 链路层最擅长回答什么问题

在排障场景里,链路层特别擅长回答这类问题:

  • 一个异常请求经过了哪些服务
  • 哪一跳开始耗时明显放大
  • 哪一跳开始报错
  • 是入口层先抖,还是下游 provider 先抖
  • 是局部依赖放大,还是整条调用路径都已经被拖慢

5.3 为什么链路层不能只停留在“可视化很酷”

链路平台很容易被误解成一种“看起来很酷”的能力。真正重要的是,链路层能不能帮助你缩短下面这条路径:

  • 从“我知道系统有问题”
  • 到“我知道是哪一跳开始出问题”
  • 再到“我知道要去哪个服务、哪个实例、哪个时间窗口里找证据”

5.4 Java Agent 不进入业务镜像,本质上是可观测治理边界设计

在我们的平台里,OpenTelemetry Java Agent 并没有直接封进业务镜像,而是通过 init 容器把 agent 包放进共享卷,再通过环境变量和 JAVA_OPTS 注入到业务进程中。

这背后的含义并不是“实现方式换了一种”,而是:

  • 运维侧可观测治理线与业务镜像迭代线解耦
  • 不同环境可以按需决定是否启用链路
  • OTel 升级不必绑定业务镜像重建
  • 可观测能力开始作为平台运行时能力存在,而不是业务镜像附属物

链路层真正提供的,不是“调用图好看”,而是一次异常请求在系统里是如何传播和放大的上下文。

6. trace id / span id 如何把日志与链路真正串起来

如果说前面两层分别解决的是“证据”与“路径”,那么这一节真正要回答的问题就是:日志和链路,怎么从两套系统变成一条线?

这是整篇文章里最关键的一层,也是我们这套可观测平台最有价值的一个设计点。因为如果日志和链路只是各自独立存在,那么排障过程仍然会停留在“两边都看一下”的状态;只有当它们拥有稳定的关联键时,定位路径才真正会从“并列搜索”变成“顺线追踪”。

6.1 没有关联键时,日志和链路只是两套平行视图

如果没有 trace idspan id 这类统一标识,链路和日志虽然都能看到很多信息,但它们之间其实很难建立稳定映射。

比如你在链路系统里看到一条异常 trace,知道它经过了哪些服务、在哪一跳失败了;与此同时,你也能在日志系统里搜到某些服务实例在相近时间点打出了错误。

但这时一个问题会立刻出现:

  • 这条日志,真的是那次 trace 对应的日志吗?
  • 还是只是同一时间窗口里另一个实例输出的类似错误?
  • 看到的这个异常,和链路中的那个 span 到底是不是一回事?

6.2 Java Agent 注入上下文后,日志就不再是孤立文本

而在我们的实践里,由于微服务运行时注入了 Java Agent,链路上下文可以自然进入业务进程。这意味着日志框架在输出日志时,可以把 trace idspan id 一起写出来。

一旦这一步成立,日志和链路之间的关系就彻底变了。

因为这时:

  • 链路中的一次调用
  • 和实例中的一条日志
  • 终于拥有了同一个稳定的关联键

6.3 这件事真正改变了什么

这个设计最重要的价值,并不是“日志里多了两个字段”,而是它改变了排障方式本身。

以前更常见的做法是:

  • 指标看一下
  • 链路翻一下
  • 日志再搜一下
  • 三套视图之间主要靠人自己脑子里做拼接

而现在,排障路径开始变得更明确:

  1. 先在指标里看到某个服务、某条接口或某个时间窗口开始异常
  2. 再在链路里找到对应的异常 trace,判断问题在哪一跳开始放大
  3. 最后拿着 trace idspan id 回到日志,直接找到该次请求对应实例上的具体证据

6.4 为什么这比“单独把日志做好”更有价值

很多团队会把日志建设做得很好,比如日志采集稳定、检索能力强、字段丰富、保留周期长。但如果日志仍然只是孤立文本,它的价值通常仍然停留在“出了事能翻到一些信息”。

而当它通过 trace id / span id 和链路真正接起来之后,日志的角色就发生了变化:

  • 它不再只是“一个单独的搜索系统”
  • 它开始成为“请求上下文中的证据落点”

6.5 日志与链路真正连起来之后,排障闭环才成立

而在这条链路里,trace id / span id 恰恰就是把“路径”与“证据”连起来的关键节点。

也就是说:

  • 没有这个关联键,链路和日志是两套并列视图
  • 有了这个关联键,链路和日志才真正变成一次故障定位过程中的前后两步

在我们的实践里,日志和链路并不是两套彼此独立的系统。由于 Java Agent 在微服务运行时注入了链路上下文,日志框架可以在输出时携带 trace idspan id,这使得“链路中的一次调用”与“实例上的一条日志”第一次拥有了稳定的关联键。

7. 为什么采集贴近业务,汇聚与存储适度脱离业务

当三层信号的职责逐渐清晰之后,另一个很自然的问题也会出现:这些可观测能力,为什么不是全都直接部署在业务集群里,或者全都统一扔到一套外部系统里?

在我们的实践里,更合适的方式并不是“全贴着业务”或“全脱离业务”,而是:

  • 采集层尽量贴近业务运行面
  • 汇聚、存储和分析层适度脱离业务运行面

7.1 采集层为什么必须贴近业务运行面

这一点在三层里都很明显。

指标层:

  • Prometheus 放在 K8s 内部
  • 更容易看到节点、Pod、工作负载和业务指标

日志层:

  • Filebeat 以 DaemonSet 的方式贴近节点
  • 更容易稳定采集实例落盘日志

链路层:

  • OTel Collector 先在 K8s 内接住 trace
  • 更适合在靠近工作负载的位置承接链路信号

7.2 为什么汇聚与存储又不应该强绑在业务运行面里

虽然采集层贴近业务运行面很合理,但汇聚与存储层如果继续完全绑定在业务集群里,就会带来另一个问题:观测平台本身会开始和业务平台互相挤占资源、互相放大故障影响。

这也是为什么在我们的平台里:

  • 指标通过 remote_write 汇聚到跨集群部署的 VictoriaMetrics
  • 日志通过 Kafka、Logstash 进入 Elasticsearch
  • 链路通过 Kafka 和后续 Collector 进入 ClickHouse / Tempo
  • 这些更偏平台型的能力,更多放在运维支撑集群承接

7.3 这其实和前面几篇的思路完全一致

如果把这一步放回整个系列里看,其实会发现它和前面几篇的治理思路是同一条线:

  • Nacos 那篇强调配置与注册边界要清晰
  • ACK 那篇强调工作负载和运行平台边界要清晰
  • APISIX 那篇强调入口层与内部服务发现边界要清晰
  • 到这一篇,可观测平台解决的也是同一类问题:
    • 采集边界要贴近运行面
    • 汇聚与分析边界不能和业务运行面过度耦合

观测信号的采集应该尽量贴近运行面,而汇聚、存储和分析能力则更适合适度脱离业务运行面。

8. 为什么日志和链路都要经过 Kafka

当日志和链路的整体架构写到这里时,一个很自然的问题就会出现:为什么日志和链路都要经过 Kafka?这是不是又多堆了一层中间件?

这个问题非常值得正面回答。因为如果只是从“组件数量”去看,Kafka 确实容易被误解成一层额外复杂度;但如果放回平台治理视角,它在这里承接的,其实不是“消息队列能力本身”,而是观测信号在跨集群、跨层次流动过程中的解耦与缓冲能力。

8.1 Kafka 在这里首先解决的是采集端与存储端解耦

无论是日志还是链路,采集端都尽量贴近业务运行面。

这意味着:

  • Filebeat 贴近节点与实例日志
  • OTel Collector 贴近工作负载接收 trace
  • 它们面对的是高频、连续、实时产生的运行信号

而日志和链路的下游存储侧则是另一种特征:

  • Elasticsearch 更重索引与检索
  • ClickHouse / Tempo 更重链路存储与查询
  • 下游存储本身也可能会有波动、扩容、维护和负载变化

如果让采集端直接强耦合到这些存储系统上,一旦下游发生抖动,最先被放大的通常不是存储问题,而是采集链路问题。

8.2 Kafka 也解决了跨集群传输时的缓冲问题

在我们的平台设计里,日志和链路都不是只在单个业务集群内部闭环完成,而是需要跨集群进入运维支撑能力侧:

  • 日志跨集群进入 Kafka,再到 Logstash / Elasticsearch
  • 链路跨集群进入 Kafka,再到后续 Collector / Tempo / ClickHouse

在这种场景下,Kafka 的价值就更明显了。它不仅仅是一层缓冲,而是一层跨集群信号中转能力。

8.3 为什么日志和链路都适合走 Kafka

日志和链路虽然语义不同,但它们在平台里的传输特征其实很像:

  • 都是高频连续产生
  • 都来自业务运行面
  • 都需要跨集群汇聚
  • 都不适合直接与后端存储强耦合
  • 都希望在下游波动时保留缓冲能力

从这个角度看,它们共享 Kafka 这样的缓冲层是很自然的。

8.4 Kafka 的价值,不是“消息队列”,而是观测信号流动边界

如果要更抽象一点去理解,这里的 Kafka 承接的是一种边界能力:

  • 采集边界和存储边界之间的解耦
  • 业务运行面和运维支撑面之间的过渡
  • 实时信号和后端消费之间的缓冲
  • 平台采集层和平台分析层之间的连接

Kafka 在这里解决的,不是“消息队列需求”,而是观测信号在跨集群与跨层次流动过程中的解耦与缓冲问题。

9. 一个典型故障场景中,三层如何联动定位

如果前面几节更多是在讲平台结构和信号分工,那么这一节更想回答一个非常实际的问题:当系统真的出问题时,这三层信号到底是怎么一起工作的?

因为三层可观测真正的价值,从来不在于“指标、日志、链路都搭出来了”,而在于问题发生时,你能不能顺着一条线,从异常现象一路走到具体证据。

为了说明这一点,可以假设一个比较典型的微服务故障场景:

  • 某个入口接口 RT 明显升高
  • 部分请求开始报错
  • 入口服务和下游 Dubbo provider 都在调用链中
  • 故障不是全系统崩溃,而是某一类请求开始异常放大

9.1 第一步:先在指标里发现异常

通常最先发出信号的,还是指标层。

比如在 Grafana 中,团队可能先看到:

  • 某个接口 RT 在某个时间点之后明显升高
  • 某个服务错误率开始抖动
  • 某组 Pod 的资源使用出现波动
  • 某个下游服务的调用耗时统计开始偏离平时基线

这个阶段,指标层提供的还不是最终原因,而是一个快速收敛问题范围的入口。

9.2 第二步:再在链路里还原请求路径

当异常窗口和问题服务大致被圈出来之后,下一步就应该进入链路层。

在链路里,最有价值的是找到异常 trace,然后沿着请求路径去看:

  • 这次请求经过了哪些服务
  • 哪一跳开始耗时异常
  • 哪一跳开始报错
  • 问题是在入口、consumer 还是 provider

9.3 第三步:最后借助 trace id / span id 回到日志

但很多时候,链路虽然已经能告诉你问题大概率在某一跳,却仍然没有给出最终证据。

比如你已经知道某个 provider 调用在链路里报错了,可真正的问题仍然可能有很多种:

  • 某条 SQL 超时
  • 某个 Redis 请求失败
  • 某个线程池拒绝执行
  • 某个参数校验异常
  • 某个第三方依赖返回错误

这时,就该回到日志层。

而在我们的实践里,这一步并不是“再去大海捞针式搜日志”,而是可以借助 trace id / span id 直接检索到与该次异常请求对应的日志记录。

9.4 三层联动真正改变的,是排障方式

如果把这整个过程放在一起看,就会发现三层联动真正改变的,并不是“多了几套系统”,而是排障方式本身。

而三层联动之后,路径会变得非常明确:

  • 指标负责发现异常窗口
  • 链路负责还原请求传播路径
  • 日志负责补足实例级证据
  • trace id / span id 负责把“路径”和“证据”连起来

三层可观测真正的价值,不是同时看到三种数据,而是可以顺着一条线,从异常信号一路走到具体证据。

10. 结语:可观测的真正价值不在“看见”,而在“找通”

回头看这次三层可观测平台建设,我们越来越明确地意识到:真正完成的,并不是把指标、日志、链路三套能力分别搭起来,而是让它们第一次在故障场景下形成了一条能走通的定位路径。

而在我们的实践里,这三层之所以真正开始形成闭环,关键就在于它们的职责被明确分开了:

  • 指标负责发现异常
  • 链路负责还原路径
  • 日志负责补足证据

更重要的是,日志和链路之间并不是孤立关系。由于 Java Agent 在运行时注入了链路上下文,而日志又通过 trace id / span id 承接了这份上下文,链路中的一次请求终于可以和实例上的一条日志稳定关联起来。

这意味着平台真正获得的,不再只是“更多信号”,而是“信号之间的连接关系”。

如果说前面几篇文章一直在解决运行时边界问题:

  • Nacos 解决配置和注册边界
  • ACK 解决运行时承载边界
  • APISIX 解决入口流量边界

那么这一篇可观测平台真正解决的,就是:当边界中的某一环出现异常时,平台如何把异常发现、路径还原和证据回溯真正串起来。

所以如果要用一句话来总结这次建设,我更愿意这样说:

对微服务平台来说,可观测建设真正完成的,不是把信号采上来,而是让异常发现、路径还原和证据回溯之间第一次真正走通。

小结

这次三层可观测平台建设最值得复盘的,不是“我们搭了 Prometheus、ELK 和 OTel”,而是定位链路终于开始成立:

  • 指标层发现异常
  • 链路层还原传播路径
  • 日志层补足实例证据
  • trace id / span id 把日志与链路真正串起来
  • Kafka 作为跨集群汇聚与缓冲层,让整套平台更可治理

延伸阅读

  • 下一篇会继续写从 Jenkins 到 GitOps 的交付体系演进
  • 可观测信号为什么必须和运行时平台一起设计
  • 三层信号如何继续为后续 AI RCA 提供基础
0

评论区