这是“微服务治理与平台化演进”系列的第四篇。
前三篇分别解决了“服务怎么跑、跑在哪、怎么进来”,这一篇继续回答:服务出了问题以后,平台怎么把异常发现、路径还原和证据回溯真正串起来。
摘要
这次三层可观测平台建设表面上看,是把 Prometheus、ELK、OpenTelemetry 这些能力分别搭起来;但如果只把它理解成“三套工具落地”,意义其实被低估了。真正完成的,并不是把指标、日志、链路各自采上来,而是让它们在故障场景下形成一条能走通的定位链路:指标负责发现异常,链路负责还原请求传播路径,日志负责补足实例级证据,而 trace id / span id 则把日志与链路真正串了起来。本文会从运维实践和架构演进两个角度,复盘三层信号为什么必须同时建设、分别承担什么职责,以及它们如何被组织成一次真正能落地的排障闭环。
系列导航
- 微服务治理:从 Git 托管 YAML 到 Nacos
- 容器平台:ACK 微服务容器平台建设
- 网关与流量治理:APISIX 与蓝绿灰度发布
- 可观测平台:指标、日志、链路三层建设
- 交付平台:从 Jenkins 到 GitOps
- AIOps / AI RCA:从可观测到智能故障分析
0. 导语:可观测平台不是三套工具,而是一条定位链路
很多团队做可观测建设时,最容易按组件来理解这件事:
- Prometheus 是指标
- ELK 是日志
- OpenTelemetry / Tempo 是链路
这种拆法没有问题,但如果文章也照着这个顺序去写,很容易把“可观测平台建设”写成“三套工具分别搭起来了什么样子”。
可对微服务平台来说,真正重要的并不是“我们有三套系统”,而是:
当故障发生时,能不能从一个异常信号出发,一路走到具体证据。
服务 RT 抖了,指标会先告诉你。
某个请求超时了,链路会把经过了哪些服务、卡在哪一跳告诉你。
而真正落到具体实例级报错细节时,你又往往还得回到日志里看:
- 是数据库连接异常
- 是某个 SQL 超时
- 是下游返回错误
- 还是某个线程池已经打满
也就是说,指标、日志、链路并不是三套互相替代的能力,而是三种不同粒度的观测信号。
在我们的平台实践里,这三层也并不是彼此独立地存在。尤其是由于微服务里通过 Java Agent 注入了链路上下文,日志框架可以在输出时带上 trace id 和 span id,这意味着日志和链路不再是两套平行系统,而是第一次拥有了稳定的关联键。
因为一旦链路中的一次调用和实例上的一条日志可以被稳定关联,那么排障过程就不再是“三套系统里各自翻一遍”,而开始变成一条真正能走通的定位路径:
- 先通过指标发现异常
- 再通过链路还原请求传播路径
- 最后通过
trace id / span id回到日志,拿到实例上的具体证据
从这个角度看,可观测平台建设真正要解决的,从来不是“信号有没有采到”,而是这些信号能不能在故障场景下串成一条定位链路。
图 1:指标负责发现异常,链路负责还原路径,日志负责补足证据,而 trace id / span id 则把日志与链路连接起来。
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 id 和 span 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 日志采集如何贴近工作负载组织
在我们的设计里,业务日志首先会落到宿主机目录,然后由 Filebeat 以 DaemonSet 的方式部署在集群中,贴近节点侧进行采集。之后日志会跨集群进入 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 id 和 span id 这类统一标识,链路和日志虽然都能看到很多信息,但它们之间其实很难建立稳定映射。
比如你在链路系统里看到一条异常 trace,知道它经过了哪些服务、在哪一跳失败了;与此同时,你也能在日志系统里搜到某些服务实例在相近时间点打出了错误。
但这时一个问题会立刻出现:
- 这条日志,真的是那次 trace 对应的日志吗?
- 还是只是同一时间窗口里另一个实例输出的类似错误?
- 看到的这个异常,和链路中的那个 span 到底是不是一回事?
6.2 Java Agent 注入上下文后,日志就不再是孤立文本
而在我们的实践里,由于微服务运行时注入了 Java Agent,链路上下文可以自然进入业务进程。这意味着日志框架在输出日志时,可以把 trace id 和 span id 一起写出来。
一旦这一步成立,日志和链路之间的关系就彻底变了。
因为这时:
- 链路中的一次调用
- 和实例中的一条日志
- 终于拥有了同一个稳定的关联键
6.3 这件事真正改变了什么
这个设计最重要的价值,并不是“日志里多了两个字段”,而是它改变了排障方式本身。
以前更常见的做法是:
- 指标看一下
- 链路翻一下
- 日志再搜一下
- 三套视图之间主要靠人自己脑子里做拼接
而现在,排障路径开始变得更明确:
- 先在指标里看到某个服务、某条接口或某个时间窗口开始异常
- 再在链路里找到对应的异常 trace,判断问题在哪一跳开始放大
- 最后拿着
trace id或span id回到日志,直接找到该次请求对应实例上的具体证据
6.4 为什么这比“单独把日志做好”更有价值
很多团队会把日志建设做得很好,比如日志采集稳定、检索能力强、字段丰富、保留周期长。但如果日志仍然只是孤立文本,它的价值通常仍然停留在“出了事能翻到一些信息”。
而当它通过 trace id / span id 和链路真正接起来之后,日志的角色就发生了变化:
- 它不再只是“一个单独的搜索系统”
- 它开始成为“请求上下文中的证据落点”
6.5 日志与链路真正连起来之后,排障闭环才成立
而在这条链路里,trace id / span id 恰恰就是把“路径”与“证据”连起来的关键节点。
也就是说:
- 没有这个关联键,链路和日志是两套并列视图
- 有了这个关联键,链路和日志才真正变成一次故障定位过程中的前后两步
在我们的实践里,日志和链路并不是两套彼此独立的系统。由于 Java Agent 在微服务运行时注入了链路上下文,日志框架可以在输出时携带
trace id和span 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 提供基础
评论区