跳到主要内容

第 25 章 可观测性:挂了或慢了,怎么知道是哪里

副题:三件套——日志(当时发生了什么)、监控(现在是否正常)、链路追踪(一次请求内部发生了什么);可观测性是投资,不是装饰

高可用建好了:冗余、限流、熔断降级、值班表也排上了。但值班的第一晚,你就撞上最难的问题:用户说"打不开",监控显示一切正常——问题到底出在哪?你能看见"系统活着",却看不见"一次请求内部发生了什么"。本章回答的就是这个问题:让系统"看得见"。

读完本章,要建立三条认知——它们合起来回答一个问题:什么叫"看得见",以及怎么做到。

  1. 可观测性三件套:日志回答"当时发生了什么",监控回答"现在是否正常",链路追踪回答"一次请求内部发生了什么"——三者各管一个问题,缺一不可;
  2. 可观测性的第一原则是:排查从被记录开始——没被记录的问题,等于不存在;
  3. 告警是把"看着"交给机器:阈值定多高、告警发给谁,是一次正经的决策——告警太多等于没有告警,狼来了的代价是致命的。

25.1 线上是黑盒:三问与三件套​

线上排障的第一课,从一只黑盒讲起。

场景是第一次值班的凌晨一点。老周发来消息:"首页打不开了。"你打开电脑——网站是好的:首页能打开,接口有响应,服务器负载正常。你回老周:"我这边正常啊。"老周发来截图:他那边就是转圈。这就是线上排障的起点:你和用户看到的不是同一个世界。上一章值班那段说的"不知道挂在哪、排障靠猜",本章正面回答它。

先看看老周的视角。他不会说"P95 超时"——他说"转圈"。你问他什么页面,他说"就首页";问他什么时候开始,他说"刚发现"。用户提供的信息天然残缺,因为他们不是排障者——这正是用户的常态:只会说"打不开"。所以排障者的工作是:从残缺的信息出发,用系统的记录补全。"转圈"是症状,先问出时间点,这是触发条件;再查那个时间点的日志和指标,这是证据。可观测性的第一个用处,就是把"用户的一句话"变成"系统的一堆证据"。

那为什么"打不开"这么难查?因为同一个症状,背后是三个可能的方向:前端问题——JS 报错、资源挂了,白屏的那种;网络问题——用户自己的网络,或者 CDN 边缘节点出了状况;服务端问题——接口慢、数据库慢。没有工具,只能猜;猜的代价是时间,而时间是事故的放大器——恢复优先,猜就是在耽误恢复。

没有三件套的世界什么样?对照着感受一下:用户报"打不开",先问用户,说不清;自己试,我这边好的;重启,碰运气;加几行日志,等下一次——二十四小时后,事故又来一遍。反复几轮,用户流失。黑盒排障的本质,是拿用户的耐心做实验;三件套的本质,是把实验挪到系统内部做——记录一切,出事回放。

工具从问题里长出来,所以先立三问。排障时永远先问三个:

  1. 现在挂没挂?——系统整体是否正常;
  2. 哪里挂的?——哪个环节出了问题;
  3. 当时发生了什么?——出问题那一刻的细节。

三个问题,三件工具,一件回答一个。这就是可观测性三件套:

  • 日志:记录发生过的事——回答"当时发生了什么";
  • 监控:持续测量系统状态——回答"现在是否正常";
  • 链路追踪:把一次请求的完整路径串起来——回答"一次请求内部发生了什么"。
图25-1 一级#7首现 图稿占位
可观测性三件套
日志/监控/链路追踪各答一个问题

三件套的分工不重叠,这解释了为什么三件都要。日志回答"发生了什么事",历史、具体;监控回答"现在正不正常",现状、整体;链路回答"这次请求内部怎么样",细节、结构。少一件,就有一块盲区。少日志:监控说错误率 10%,但不知道为什么,查不了。少监控:日志都在,可没人看——那场雪崩事故里"告警半小时后才响",就是缺了这一件。少链路:知道挂了、也有日志,但"慢在哪一站"要靠手工对时间戳去拼——慢,而且容易错。各自补一块盲区,缺一块就有一类问题查不动。

由此立起可观测性的第一原则:没被记录的问题,等于不存在。用户报"打不开"时,如果系统里没有任何记录——日志、指标、链路,一样都没有——这次事故就是不可知的:猜、试、重启,全凭运气。三件套的全部工作,就是让"未知的问题"变成"已知的问题"。上线从来不是结束,是问题从已知变成未知的开始;三件套负责把未知变回已知。

放到运行期的全景里,本章的位置也清楚了。部署管"放上去",高可用管"挂不了",本章管"看得见"——三章合起来,"跑起来、挂不了、看得见",运行期的闭环至此完整。

最后交代起点:可观测性不是从零开始。打从分层那一章起,每一层已经在打日志;中间件管道里有贯穿全链路的请求 ID;数据库那一章立过慢查询日志;性能那一章跑过 P95 曲线;构建那一章落了健康检查接口。本章做的事,是把这一路的散点串成体系。

25.2 日志:当时发生了什么​

三件套的第一件是日志——把发生过的事记下来。它是可观测性的地基:监控是日志的汇总,链路是日志的串联——先有日志,才有其他。请求日志这个角色,从管道那一章起就站在可观测性的起点上了。

日志记多细?用级别分层。DEBUG,开发调试的细节,生产环境不记——调试信息不上生产,这条规矩在部署章就立过了。INFO,正常事件:请求完成、下单成功,业务日志的主场。WARN,异常但可恢复:重试、降级,熔断触发那一刻该记的就是它。ERROR,错误:未捕获异常、数据库不可用——值班的人先看 ERROR,再想别的,ERROR 是排障的第一入口。级别是日志的过滤器:级别定得对,排障就不被 INFO 淹没。

一条好日志长什么样?四条缺一不可:时间,精确到毫秒;级别,INFO、ERROR、WARN 之一;request_id,中间件管道里那条贯穿全链路的排障 ID,这条日志属于哪次请求,靠它认;上下文,谁、什么操作、结果如何——和表里每个字段都要能回答"这条数据哪来的"是同一个思想,每条日志都要能回答"这条记录哪来的"。规范的意义一句话:日志是给未来的排障者读的,原料不齐,排障就慢。顺带说一句,打日志的习惯管道那一章就立了——中间件记请求进来,业务层记决策结果,数据层记查询耗时。本章升级的只是格式:补上 request_id、补上级别、补上结构。习惯早有了,规范的只是格式——日志文化的建设成本,大头是习惯不是工具。

格式升级到什么形态?结构化。日志别用自然语言,用结构化格式——JSON,一行一条。人看自然语言舒服,机器看 JSON 舒服;而日志量大了以后,检索靠机器:按 request_id 过滤、按级别统计。所以记住那句话:日志是数据,不是文字。部署那一章讲过"日志是事件流",事件流的含义正是这个——一条一条结构化的事件,交给平台统一收集。

说"统一收集",就得交代部署侧。规矩是:应用把日志打到标准输出,平台统一收集,应用自己不许写文件。为什么?多实例时代,每个实例一个日志文件,查日志就得 SSH 到每台机器——容器化之后这问题更明显。日志收集器把每台机器的日志汇总到一个日志平台,按 request_id 一查到底。制品要"所有实例同一个",日志要"所有实例汇到同一处"——集中,是多实例部署对日志的必然要求。

日志回答三问里的哪一问?主要是第三问,"当时发生了什么"。但前两问也要它配合:监控说错误率 10%,这是第一问的答案;是哪些请求错了?翻 ERROR 日志,这是第二问的入口。所以日志的角色是取证层——监控给方向,日志给证据,至于结构,那是链路追踪的活,后面讲。

实操怎么用它?三个基本动作。按 request_id 捞:用户报障,拿到请求 ID,日志平台一查,那次请求的全部日志按时间排开——联调时就这么干过,这里是生产版。按 ERROR 扫:每天扫一眼今天的 ERROR——没告警不代表没错误,错误率 1% 的常态错误,要靠扫才能看见。按慢查询看:数据库那章的每周三件事,到本章全部自动化。三个动作归结起来,日志平台的基本操作只有两个:过滤,按 ID、按级别;时间线,按时间排开。会用这两个操作,排障就入门了——开发者工具里的 Network 和 Performance 两个面板,也是同样的两个操作。

还有一条规矩贯穿始终:错误详情只进日志,不进响应。堆栈、表结构、内部参数,这些细节留在日志里,响应体只给通用格式——一个错误码、一句用户看得懂的话、一个 request_id。原因有两层:安全上,错误详情是攻击者的免费地图;排障上,详情在日志里,排障者拿着响应里的 request_id 就能找到它。一句话:对外最小化,对内最大化——响应里最少的细节,日志里最多的细节。

但日志自己也有暗面:它会无意间记下不该记的东西。登录日志里打了密码,token 和密钥混进业务日志,个人数据散落各处——凭证是命门,密钥进日志就是密钥泄露。所以日志规范里除了"打什么",还要有"不打什么":密码、密钥、token,一律不打,这是脱敏清单。日志是泄密高发地,排障便利和安全要同时管:日志平台要做访问控制,谁能看到 ERROR 详情,按最小权限来。

安全管理还有一笔账要记在日志头上:审计。安全事件要有日志——登录失败、权限变更、支付操作——而且日志要留得住:攻击者清理日志是常见手法,所以日志要写到攻击者删不到的地方——独立的日志平台加权限控制。审计日志回答的问题是"被攻击了怎么知道"——安全那章埋的第四个发现路径"主动发现",落地就在这里。

往深处看,日志的底层哲学是留痕:系统的每个动作都该留痕——请求来了、决策做了、错误发生了。留痕让系统成为一段可回放的历史:出事时把时间线拉出来,那一刻发生了什么一清二楚,不用复现——很多 bug 根本复现不了,测试那章就说过;直接看记录,才是排障的正道。回放能力,是可观测性给排障的最大礼物。

日志还分两类,边界要清。系统日志——请求、错误、慢查询,本章的主角;业务日志——下单成功的订单号、支付回调、用户关键操作,第 18 章立过"关键路径要有日志"(下单成功必须有订单号日志),安全那章的审计日志也属于这类。业务日志给业务排障用:"用户说订单没到",按订单号查业务日志,交易那章的异步排查靠的就是它。系统日志给系统排障用:"接口慢了",按 request_id 查。两类日志,一套体系:都结构化、都带 ID、都集中收集。

代价最后认账。日志量,阶段 4 每天几个 GB——检索变慢、存储变贵。对策两个:级别过滤,DEBUG 只在开发环境,生产只记 INFO 以上;保留期,日志留 30 天,按月归档——排障要的是"最近",历史审计按月查。所以日志不是越多越好,是"够排障、够审计"——日志量也是复杂度预算的一项。

25.3 监控指标:现在是否正常​

三件套的第二件是监控——持续测量系统的状态。它回答三问里的第一问,"现在挂没挂",同时它还是告警的原料:没有监控,告警就是无米之炊。

先澄清一个术语。监控和可观测性不是一回事:监控是"我们已经知道要问什么"——预设好的指标,延迟、错误率,问的是已知的问题;可观测性大一圈,是"出事之后能问出问题"——靠日志和链路,回答未知的问题。监控加日志加链路,才是完整的可观测性:已知的自动化交给监控,未知的留证据交给日志和链路。本章标题用大词,因为它管的是这一整圈。

那监控什么?四类指标,业界有现成的分法,通俗版够用。延迟,接口响应多快,P95 是主角;错误率,请求失败的比例,5xx 和 4xx 分开看;流量,每秒请求数,容量告警的依据;饱和度,资源用到几成,CPU、内存、连接池——"快满"的信号。四类指标对应四类问题:慢、错、多、满。系统出问题,永远落在这四类里——这个清单的价值在于兜底:不知道监控什么时,从这四类各挑一个,就不会漏大方向。

每类先抓一个主角,都是故事里死过的。延迟的 P95:预算指标一路用到现在,预算早定了、故事够多了、阈值有现成的依据——它是最早该上监控的指标。饱和度的连接池:雪崩事故的元凶,连接池使用率是该死的第一个饱和度指标——它红了,事故已经在路上。再加上错误率和健康检查接口,四选四,一张起步清单就有了。

指标从哪来?三个来源。应用自己上报:每层代码里打点,这个接口耗时多少、这层调用耗时多少——分层结构的好处在这里兑现,每层天然是一个计时点。运行时采集:进程指标,内存、事件循环延迟,不写代码也自动有。基础设施探针:数据库、缓存、CDN 的指标,由平台和工具采集。三个来源,汇到一个平台,画在同一张图上——监控系统的本质就这句话:所有来源的指标,一个地方看。排障不用 SSH 到每台机器敲命令——日志集中的同款逻辑,指标版。

指标和预算的关系,到此闭环。预算思想走完了全程:定预算——接口 P95 多少毫秒;拆预算——中间件 10 毫秒、控制器 5 毫秒、数据层 150 毫秒;量预算——本章的监控,每个指标对着预算看;超预算告警——下一节的事。先度量再优化,度量不停,优化不停。

监控还要铺到每一层。应用指标:接口延迟、错误率。数据库指标:连接数、慢查询、磁盘——数据层的健康不是不出事,是出事之前有信号。CDN 指标:命中率、回源率、节点延迟——缓存健康状况的体温计。服务存活:健康检查接口,一个接口三个用途,构建校验、高可用探活、监控存活,到此全部上岗。监控不是一个系统,是每一层的体温计——分层是排障的地图,监控按同一张地图布点。

指标存在哪、怎么查?指标是时间序列——每个点带着时间戳和标签——存进专门的时序存储,它按时间聚合、降采样,比通用数据库更合这个场景;数据库要选对场景,指标的存储同理。查询三个动作:看曲线,看趋势;对比,今天对昨天;下钻,从总接口钻到单个接口,再钻到单个实例——从粗到细,先看整体哪个接口红了,再看哪台机器。这就是分层排障在指标上的走法。

而曲线比单点重要得多。平稳、爬升、陡增,三种形状三种故事:性能那章说过,P95 三个月从达标爬到超标——爬升期就该被看见,等单点报警就晚了;陡增就是雪崩的形状。所以看监控不是看现在,是看趋势。当年巡检的每周三件事——命中率、慢查询、预算——到本章全部变成曲线,自动化地摆在眼前。

监控还有一个层级上的妙处:它是事故的提前量。饱和度先红——连接池八成;延迟后红——P95 涨了,排队开始;错误率再红——请求开始失败;最后用户才感知。在用户感知之前看见,才是监控的意义——告警策略要做的,就是把这个提前量变成机制。

最后两笔,把监控接到"人"那头。第一笔是业务对齐:指标要回答业务的问题——用户体验变差了吗,看 P95 和错误率;业务增长了吗,看订单量、注册量;成本失控了吗,看资源使用率。技术指标加业务指标,两层都要有,而且它们有上下游关系:订单量涨,QPS 涨,饱和度涨——业务指标先变,技术指标后变。告警响在技术层,解释要回到业务层:排障先看业务指标,"订单量暴跌"和"QPS 暴涨"是两种完全不同的事故。第二笔是翻译:值班的人要会把用户语言翻成指标语言——用户说"卡",指标是 P95 涨;用户说"打不开",指标是 5xx 涨;用户说"图片加载慢",指标是 CDN 命中率掉。用户感知是症状,监控数据是诊断,值班清单里"再判断"那一步,靠的就是这门翻译。

25.4 告警:把"看着"交给机器​

监控有了,但人不能 24 小时盯着曲线。告警做的事,就是把"看着"交给机器:指标异常时,机器自动通知值班的人——值班的人终于有了眼睛。而眼睛怎么配,是本章的决策点。

决策的起因,还是那场雪崩事故。事故里告警半小时才响,复盘挖出三个病根:阈值设得太松——P95 5 秒才告警,是按"还扛得住"设的,不是按预算设的;告警没分级——半夜全打给同一个人;没有持续时间判断——瞬时抖动也打扰人。本章的告警策略,就是那场事故的修复。事故是演进之母,老规律又一次应验。

正经走一遍决策流程。先立一条连贯性:可用性目标定了 99.9%——目标兑现的前提是"知道挂了",所以这次的决策是上次决策的执行层——目标管"承诺多高",告警管"怎么知道没兑现"。决策记录不是孤立的行,是一层层执行的链。

① 目标:出问题时第一时间知道,且不被噪音淹没。② 约束:阶段 4——8 人团队,值班刚引入,监控刚体系化。③ 问题:先监控什么?告警阈值怎么定?④ 候选:A,全量监控,所有指标都盯都告警;B,核心链路优先,阈值按预算定;C,只监控已经出过事的,被动响应。⑤ 维度:覆盖度——漏报的风险;噪音——误报的风险;维护成本——指标也是要养的。⑥ 打分:A 的致命伤是噪音会淹没信号——告警太多,值班的人就开始忽略,狼来了;C 的盲区是未知的问题——没被监控的问题等于不存在,这是可观测性的第一原则。B 均衡:核心链路优先——交易相关的接口永远第一优先级,钱的链路不能盲;阈值按预算定,不拍脑袋。⑦ 选择:B。⑧ 收益:该知道的都知道,核心链路全覆盖;噪音可控,阈值有依据。⑨ 代价与演进:非核心链路有盲区,可接受——它们挂了的损失小。演进条件:非核心链路出过事,再纳入监控。事故驱动扩容,安全那章"后置的防御要有触发开关",同一个逻辑。

阈值怎么算?给一个"不拍脑袋"的配方:预算乘以 2 到 3 倍,就是告警线。P95 预算 200 毫秒,告警线设 500 毫秒——给正常波动留余量,和限流阈值 2.5 倍余量是同一个逻辑。再配两个条件:持续 N 分钟——5 分钟,单次抖动不告警,持续异常才告警;对比基准——和昨天同一时段比,"今天比昨天慢 3 倍"比绝对值更灵敏,趋势告警是进阶玩法,阶段 4 先用阈值,知道它存在。还有一点别忘了:告警阈值是行为类配置,不发布也能改——但调阈值要走评审,保护机制的参数要被管理,这条规矩在前面立过。

一条合格的告警长什么样?三要素:指标——什么异常了;阈值与持续时间——P95 超过 500 毫秒、持续 5 分钟,持续 N 分钟是关键设计,瞬时抖动不打扰人;负责人——告警给谁,值班表说了算。一句话:告警发的是对的通知、给对的人,不是随手发通知。

给对的人,还要分级。P0:全站不可用、交易不可用——半夜叫醒值班的人和负责人。P1:单接口异常、部分用户受影响——值班的人处理,不惊动别人。P2:趋势异常、非核心链路——上班再处理,记下来就行。分级的意义在于:告警本质上是"要不要叫醒人"的决策,叫醒有成本,事故也有成本,两边要称。什么算 P0?给一个量化:错误率超过 5%、持续 5 分钟——先于用户感知到之前,告警就得响。

告警响了之后呢?流程要接上:值班的人先判断是不是 P0——是,先恢复再查因,值班清单的老规矩;再确认是真事故还是抖动——持续 5 分钟以上的大概率是真的,这正是阈值设计里"持续 N 分钟"的意义;然后处理或升级,30 分钟处理不了叫醒负责人;最后处理完关掉告警,复盘为什么响、阈值要不要调。所以告警不是终点,是流程的起点——排障时间线,从告警这一刻开始画。

但告警有一个会慢慢失效的隐患:噪音。告警太多,值班的人开始忽略,真事故也被忽略——雪崩事故的告警半小时才响,有一半原因就是值班的人已经被吵麻了。治理三招:阈值有依据,预算乘系数,不是随手填;合并,同一时间同一条链路的多个告警并成一条;分级,P0 半夜叫醒、P1 上班处理、P2 记下来,和值班清单配套。告警的黄金标准一句话:每条告警都必须是"需要人做点什么"——不需要行动的告警,就是噪音。

两笔跨章的衔接,收在这里。一笔给值班:上一章说过,没有告警,值班等于等用户投诉;告警太多,值班等于狼来了。本章把眼睛建好,分工就完整了——告警负责"知道",人负责"恢复";一章管挂了怎么办,一章管挂了怎么知道,两章合起来才是完整的事故应对。另一笔给保护机制:上一章说限流拦了多少请求要数得出来,本章落地成指标——429 的计数,正常是 0,暴涨说明流量异常或阈值太紧;熔断打开次数也要计,每次打开都该被看见。机制的状态也要被观测,这句话在这里兑现。最后还有一笔给安全:安全那章把验证码之类的防御后置,理由是"挂了监控等信号"——信号清单就是告警规则:同一 IP 大量登录失败、注册频率异常、高频小金额下单,各配一条告警。当年记下的演进条件,到这里变成了监控里的配置项——决策记录里"当时为什么不上、什么时候该上",在这里有了可执行的形态。

25.5 链路追踪:一次请求内部发生了什么​

三件套的最后一件,回答的是"一次请求内部发生了什么"。前两件套看整体,这一件看单次:日志是一条一条的,链路追踪做的事,就是把属于同一次请求的日志串成一条线。分层那一章说过,链路追踪是日志链的自动化版——这里兑现。

先澄清两个 id,它们很容易被混。request_id 是一次 HTTP 请求的 id,前端报错时带上它;trace_id 是整条业务链的 id——一次用户操作,比如下单,可能触发多次请求加多个异步任务。关系是:request_id 是 trace_id 的一部分,链上每个请求的 span 都带着同一个 trace_id。用法也顺:前端报 request_id,后端按它找到 trace_id,再展开整棵树。联调时两边对着同一个 id 说话,吵不起来;本章让这个 id 能展开成一棵树。

树是怎么长出来的?跟着一次请求走一遍。请求进来,中间件在鉴权之后生成 trace_id;每进一层,记一个 span,父 span 连子 span;数据库查询也是一站,数据层同样记一个 span;响应返回,树长完。一次请求就是一棵树。请求的旅程这个概念——设计时一站一站地走,运行时一站一站地记——旅程思想在这里从"设计时的预算"变成"运行时的 span"。

这棵树和当年那笔手工账是什么关系?就是那笔账的自动化。分层那章算过:中间件 10 毫秒、控制器 5 毫秒、业务层 20 毫秒、数据层 150 毫秒——那是设计时的预算账,靠手工拼日志拼出来。链路追踪把同一笔账自动画出来,每个 span 的耗时就是那笔账的运行时版本。分层结构从代码结构升级为观测结构:每层打日志的习惯,到这里收成为每层一个 span。分层不只是代码结构,还是排障地图——这句话的观测版,就是这张图。

图25-2 新 图稿占位
链路追踪
一次请求跨服务的 trace/span

异步是这棵树最该出现的理由。交易那章说过,异步化的代价清单里,排查复杂度最容易被低估——本章的链路,就是那笔代价的偿付。一次下单拆成好几个环节,任务表加消息队列,环节各跑各的:没有 trace,排障就是挨个环节猜;有 trace,一次操作一棵树,trace_id 贯穿所有相关任务,一个 id 找到整条链。异步能上,就是因为链路能兜——这句话在这里兑现。

树的价值,落在两个排障场景。慢的请求,慢在哪一站?一次接口慢 800 毫秒,是中间件、业务还是数据库——当年手工拼日志算过,现在自动画出来。异步任务断了,断在哪一环?下单后通知没到:回调没来、任务没跑、消息丢了、worker 挂了——trace_id 一个 id 穿到底。所以链路回答的是"内部细节",是排障从"猜"到"看"的最后一公里。

要不要上链路?给一条判断标准:一次操作跨了几个进程或任务。一个进程内,日志够;跨多个,链路上。单体阶段它不是必需品,手工拼日志就行——但阶段 4 已经露出拆分的苗头,异步任务已经在跑。所以结论:链路从异步出现的那一刻开始必要。这也是复杂度预算的老算法。

全量追踪贵不贵?贵。每个请求都记录加存储,成本高得吓人。对策是采样:只记录一部分请求,阶段 4 的比例,平时 10%,高峰降到 1%。排障要的是代表性样本——问题会重复出现,样本足够定位。采样是用一点点概率换大量成本,缓存随机的哲学在这里又出现了一次。

落地怎么落?工具用现成的:开源体系 Jaeger、Zipkin 一类,语言无关、社区成熟;或者用云厂商的方案。代码改动也不大:中间件生成 trace_id,每个入口加两行 span——分层的结构早就站好了,每个入口都是现成的打点位置。所以链路就是 request_id 的升级版:从一个 id 捞日志,升级为一个 id 画树。

看一个具体的例子,树的模样就立住了。trace_id: abc123——根 span:POST /orders,总耗时 850 毫秒;子 span:鉴权 15 毫秒;子 span:创建订单 200 毫秒,它的子 span:INSERT 150 毫秒;还有一个子 span:发通知,异步的,620 毫秒后才完成。一眼看出:慢在通知任务,不在主流程。这就是价值——850 毫秒的请求,慢在哪一站的下游,不用猜。

最后把三件套的关系说透,这也是全章的收束。链路是索引,日志是内容:trace 告诉你哪一站慢,点开那一站的 span 看日志细节。三种工具连起来是一句话:监控告诉你有没有病,链路告诉你病在哪一站,日志是那一站的检查报告——从上到下,由粗到细,可观测性到此完整。

25.6 排障流程:三问的自动化​

三件套齐了,最后串成流程——排障的完整时间线。先说这次串线的性质:联调那章的三问排查法,是人工版——复现、分层、证据,靠人记得走;本章的告警、监控、日志,是同一套三问的自动化版——靠体系记得看数据。两次形态的区别就一句话:流程从人的习惯,变成了系统的行为。当年说"流程是团队的函数",这里是它的运行期版本:流程是系统的函数。

时间线分四步,正好对应三问加一尾。

① 发现:告警响了,或者用户报障。第一问"现在挂没挂"由监控回答——挂没挂、挂了多久、影响面多大,容量告警也在这里。

② 定位:第二问"哪里挂的"。看监控曲线,哪类指标异常——延迟、错误率、流量、饱和度,四类各看一眼就有方向;查日志,按 request_id 或 trace_id 把那一批请求捞出来;看链路,慢在哪一站。定位从猜,变成查。

③ 恢复:老规矩,先恢复再查因——回滚、重启、切流量。用户先能用,真相慢慢找。

④ 复盘:这一问问的不是事故本身,是体系。三个问题:哪道防线漏了——高可用的机制缺口;哪条告警没响——感知层的缺口;哪个环节没记录——日志的缺口。三问各有一个"补什么"的答案:补机制、补告警、补日志。所以复盘不是写报告,是列一张"下次怎么更快"的清单——事故的终点不是修复,是下一次更快。

流程走一遍,用那场雪崩事故当实景。告警响:P95 超 500 毫秒、持续 5 分钟,P0。值班的人醒来,看监控:错误率 5%,饱和度 80%——不是流量问题就是依赖问题。看链路:数据库的 span 3000 毫秒——慢在数据层。看日志:慢查询,全表扫描——数据库那章的主角登场。恢复:先限流止血,再回滚。复盘:补索引,再加一条"慢查询告警"——下一次,不用等人发现,告警自己会响。流程的每一步都有工具,工具让流程从靠人变成靠体系。

复盘背后还是那个老规律:发现问题、解决问题、新问题——排障复盘是运维版的演进循环。事故是发现问题,修复加补监控是解决问题,而新的监控会暴露新的问题——补了慢查询告警,可能发现连接池饱和得更早。可观测性体系不是建完的,是长出来的:每次事故长一块,动力是事故本身。

排障时还有几样辅助工具,是前面各章埋下的,事故时一起上。代码历史的 blame 和提交信息——排障时第一个看的就是它,"这次改动为什么存在",能回滚,更要知道回滚的是什么。部署状态记录——现在跑的是哪个版本,先确认"是不是这次发布引入的",这是排障的第一排除项。sourcemap——让错误日志里的行号从压缩代码变回可读源码。排障这件事,本质上是团队所有章节积累的工具的集合。

最后给排障速度一个度量,叫 MTTR——平均恢复时间,从发现到恢复。拆开看是三段:发现时间(MTTD),告警快不快,阈值和分级说了算;定位时间(MTTI),三件套全不全说了算;恢复时间,回滚和切换快不快说了算。三段时间的总和,决定了那个 99.9% 的目标能不能兑现——年宕机 8.76 小时除以事故次数,就是每次事故的时间预算。本章压缩的是前两段:发现和定位;恢复靠的是前两章的回滚和切换。所以每一章都是 MTTR 的一部分——而可观测性对业务的价值,一句话:它把 MTTR 从小时压到分钟。还是那场雪崩作对比:当年告警半小时才响、人工排查,40 分钟;本章之后的世界,告警 5 分钟、定位 10 分钟、回滚 5 分钟——MTTR 减半,靠的不是更努力,是工具。

25.7 可观测性的边界与投资​

三件套讲完,还要说清楚它的代价与边界。先立总论:可观测性是投资,不是装饰——这句话是本节全部讨论的地基。

既然是投资,就有起步的顺序,而这个顺序是问题逼出来的。别一上来就搭全家桶——日志平台、监控面板、链路系统一次到位,8 人团队会死在工具上。阶段 4 的走法:日志先规范化,规范日志几乎零成本,变的只是写日志的习惯;然后用现成的基础监控,延迟、错误率、存活;告警只配核心链路,那是本章的决策;链路追踪等到"慢请求排不动"的信号出现再上——异步任务上线之后它才必要。每一步都是被问题逼出来的:雪崩事故逼出了告警,异步化逼出了链路。可观测性体系不是一次建成的,是被事故逼着一节节长的。

投资就有花费,账单列清楚。存储成本:日志、指标、链路都要存,阶段 4 每天几个 GB。埋点成本:每层打日志、每个接口记指标,都是开发时间。维护成本:告警规则要养——阈值要调、噪音要清,不养的告警会烂掉。运维成本:日志平台和监控系统本身要维护——工具有一个共同的教训:工具自己也是复杂度。四项花费,全是复杂度预算的支出。所以可观测性的本质是:花复杂度,买确定性。

观测数据本身也是数据,管理规矩一样不少。安全上,日志里可能有用户信息——密码、token 不打日志的脱敏清单,凭证是命门的原则在这里原样适用;日志是泄密高发地,访问控制按最小权限来。保留策略上,观测数据留 30 天,法律和审计的要求与业务数据同标准。成本上,存储照预算管。观测数据不是免费的副产品,它和业务数据一样要建模、要管理。

投资还有边界:过度可观测也是负债。每个接口埋 50 个指标没人看,每条日志打满上下文检索变慢,200 条告警没人理——判断标准一句话:每条日志、每个指标、每条告警,都要回答"谁会看它、看了做什么"。没人消费的观测数据,就是装饰——需求可砍的思想,在运维领域就是这个判断。

观测体系还有几个开关要管:日志级别、告警阈值、采样率,全是行为类配置——不发版也能调,但调了要记录、要走评审。把 ERROR 级别调成不记,等于把眼睛闭上。可观测性的开关也是生产行为,和限流阈值同级管理:保护机制的参数要被看,感知机制的参数也要被看。

这笔投资的回报,往两头看。一头在拆服务:微服务的前提是"拆之前先建可观测性"——一次用户操作跨多个服务,没有 trace 根本没法排,先有眼睛,再动手术。另一头在业务迭代:监控数据是"新功能好不好"的证据——上线后看新功能的转化率和接口错误率,可观测性不只是排障工具,还是业务决策的数据源。排障时的老经验也在这里兑现:告警响在技术层,解释要回到业务层。

建设节奏还跟着团队走。1 人团队,日志够——打日志的习惯从分层那一章就有;8 人团队,监控加告警,本章的成果;几十人的团队,链路加平台化,拆服务之后必配。可观测性的建设跟着团队规模和系统复杂度走,工具也是预算,花在刀刃上。

放到全书的地图里,本章的位置是收官。工程线回答的问题是"怎么让能跑的代码变成能一直跑的产品"——这条线上,流水线管交付,部署管放置,高可用管保障,本章管感知。四块拼图至此齐了,"能一直跑"的完整含义可以一句话说完:能交付、能放置、挂不了、看得见。

"看得见"还要补完最后一块拼图:前端。前端的观测有三样:浏览器开发者工具,单次排查的神器,前端的可观测性从这里起步;前端监控 SDK,自动采集页面错误率、资源加载失败率、首屏时间、接口失败率——前端也有自己版本的"四类指标";用户反馈,老周的消息最真实也最滞后。而前后端的指标要对着看:前端接口失败率高、后端错误率正常,问题在网络或网关;两个都高,问题在后端。当年联调靠"两边对着同一个 id 说话",现在靠两边对着同一组指标对表诊断——前后端合起来,才是完整的看得见。

"看得见"到此有三层,是全章引导词的总收。第一层,系统看得见——三件套,本章主体。第二层,团队看得见——值班交接、排障手册,人和流程的记忆。第三层,业务看得见——指标回答业务问题,业务与技术两层指标对齐。可观测性的终点:系统、团队、业务,三双眼睛都在看。看得见的产品,才谈得上能一直跑。

最后一笔,给"发现"再添一个主动哨兵。核心路径的端到端测试,可以部署到生产环境定时跑——下单流程每五分钟自动走一遍,挂了立刻告警,用户报障之前,测试先发现。这是测试和监控的合流:测试不只上线前跑,也上线后跑;合成监控是主动的发现,用户报障是被动的发现——而发现永远主动比被动好。

25.8 本章对应表​

业务诉求技术选择为什么代价/取舍
"打不开"说不清哪里三件套(图 25-1)三问各有一件工具:日志/监控/链路三件都要建(顺序:日志→监控→链路)
用户报障查不到那次请求request_id → trace_id对着同一个 id 说话(第 7、15 章)埋点要规范(每层都带)
挂了不知道告警把看着交给机器;on-call 的眼睛噪音=告警失效(阈值按预算×系数)
慢得诡异指标曲线(延迟/错误率/流量/饱和度)趋势比单点重要(P95 爬升)指标要养(阈值/噪音治理)
异步任务断了找不着链路追踪(图 25-2)17 章异步的代价清单:trace 贯穿采样(10%)换成本
被攻击了不知道审计日志19 章:审计是安全的"事后诸葛亮"日志要留得住(30 天)
用户报障前发现挂合成监控18 章:E2E 从开发工具变成运维哨兵核心路径才配得上(预算)

本章小结​

  • 三件套回答三问:日志(发生了什么)、监控(是否正常)、链路(内部细节)——从上到下,越来越细;

  • 第一原则:排查从被记录开始——没被记录的问题等于不存在;

  • 告警是把"看着"交给机器:核心链路优先、阈值=预算×系数——告警太多等于没有告警;

  • 可观测性是投资不是装饰:从少到多(事故逼着长)、没人消费的观测数据是负债;

  • MTTR 是业务价值:发现快(告警)、定位快(三件套)、恢复快(回滚/切换)——可观测性把 MTTR 从小时压到分钟。

下一章​

系统现在"看得见"了:挂了有告警、慢了有曲线、错了有日志。业务也在增长——阶段 5 来了:日活新高、功能膨胀、8 人团队改一个仓库越来越挤、发布互相踩、数据库连接数告急。

第 26 章,架构演进:单体 vs 微服务——什么时候该拆?怎么拆?