后端可观测性排障:先问用户受影响了吗
后端可观测性排障:先问用户受影响了吗
一、排障不要一上来翻日志
线上报警一响,很多人第一反应是 ssh 上去翻日志。经验告诉我,先别急。第一问应该是:用户是否受影响,影响范围多大,核心链路是否不可用?如果只是某个非核心指标抖动,处理节奏和全站不可用完全不同。
大概是去年下半年,我们一个推荐服务突然告警说"成功率下降 5%"。值班同学冲上去开始翻日志、看调用栈,20 分钟后还没找到问题。我拉了个语音问了一句:"用户能看到推荐吗?"答:"能。""那页面打开速度呢?""正常。""那 5% 是对什么服务的成功率?""是对一个 A/B 实验的模型版本,正式用户用的不是这个。"——问题解决了。一个无关用户的可控失败的实验版本,在成功率和恐慌程度上画了一笔误导性的红线。
可观测性不是为了让面板好看,而是帮助团队快速判断影响、定位原因、执行止损。日志、指标、链路追踪要围绕这个目标组织。排障的第一个输入不应该是数据,而应该是问题:影响谁了、多严重、急不急。
二、排障链路:影响优先
flowchart TD A[报警触发] --> B[确认用户影响] B -->|无影响| B1[静默处理/调整阈值] B -->|有影响| C[定位核心指标] C --> D[查看变更记录] D --> E[缩小故障范围] E --> F[止损] F --> G[复盘] G --> H[改进监控/流程/代码]很多事故和变更有关。发布、配置、扩容、数据迁移、依赖升级,都可能触发问题。排障时先看最近变更,往往比盲目翻日志快。
变更检查有个实用清单:最近 24 小时内——有没有代码发布?有没有配置变更?有没有依赖升级?有没有数据迁移?有没有扩容/缩容操作?如果这五项都没变但故障发生了,那可能是外部依赖、第三方 API 或基础设施的问题,排查方向就不同了。
三、代码示例:日志里必须有 request_id
package logx import "context" type Fields map[string]any // Error 记录带 request_id 的结构化错误日志 func Error(ctx context.Context, msg string, fields Fields) { // 从 context 中取 request_id,如果没有则生成一个 rid, _ := ctx.Value("request_id").(string) if rid == "" { rid = "unknown" } fields["request_id"] = rid fields["level"] = "error" fields["msg"] = msg // 加上调用方法名,方便定位 if method, ok := ctx.Value("method").(string); ok { fields["method"] = method } // send fields to logger } // 中间件:在入口处注入 request_id func RequestIDMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { rid := r.Header.Get("X-Request-ID") if rid == "" { rid = generateRequestID() } ctx := context.WithValue(r.Context(), "request_id", rid) // 传给下游时也带上 r = r.WithContext(ctx) r.Header.Set("X-Request-ID", rid) next.ServeHTTP(w, r) }) }request_id 是串起日志、链路和用户反馈的钥匙。没有它,排查一次请求会变成全文搜索。结构化日志比大段字符串更适合生产排障。
两点容易被忽视的细节:第一,request_id 要从入口传到底、传给下游、传给数据库和缓存里——如果只在第一层有 request_id、后续调用用的是没有上下文的日志,串链路还是串不起来;第二,request_id 格式最好包含服务名、时间戳和随机数,这样即使两个服务生成了相同的随机串(概率很小但存在),也不会冲突。
四、工程边界:指标要能指导动作
指标不是越多越好。核心服务至少要有请求量、错误率、延迟、饱和度、下游错误、队列长度和资源使用。更重要的是每个报警要有处理建议:看哪个面板,找哪个负责人,是否可以回滚,是否有降级开关。
取舍方面,报警阈值太敏感会扰民,太迟钝会错过故障。可以按用户影响分级:错误率轻微上升先记录,核心接口 P95 超阈值通知值班,支付或登录不可用立即升级。告警不是展示系统有多复杂,而是让人能行动。
复盘要产出系统改进。不要只写"加强监控""提高意识"。要明确加哪个指标、改哪个超时、补哪个限流、修哪个发布流程。没有动作的复盘,只是事故作文。
排障时还要分清症状和原因。CPU 高是症状,可能原因是流量涨、死循环、GC、日志暴增或下游慢导致堆积。错误率高也是症状,可能是发布、依赖、数据或配置。可观测性的价值,是让团队从症状一步步缩小范围,而不是看到一个红色面板就下结论。
变更记录要自动化。谁在什么时候发布了哪个版本、改了哪个配置、执行了哪个数据任务,最好能在排障面板里直接看到。很多事故恢复快,不是因为人更聪明,而是证据更集中。
值班手册要跟着系统更新。服务新增了降级开关,手册里就要写;报警策略改了,手册也要改。过期手册会在事故里害人。
可观测性还要覆盖业务指标。接口 200 不代表业务成功,订单是否创建、消息是否送达、AI 回答是否被采纳,都可能才是关键结果。技术指标告诉你系统是否运行,业务指标告诉你用户是否真的完成目标。
排障结束后,要把本次最有用的指标和最没用的指标记下来。下次优化面板时,删掉噪声,突出证据。面板也需要迭代。
别把可观测性做成少数人的特权。研发、测试、运维和业务值班都应该能看懂核心状态。面板命名、指标解释和处理入口都要尽量直白。看不懂的监控,在事故里等于不存在。把复杂性藏在系统里,把关键信号摆在值班人员眼前,这才是可观测性真正的价值。
五、总结
后端可观测性排障,先确认用户影响,再看核心指标、变更记录和链路证据。日志、指标和追踪要服务行动,不能只是堆数据。排障不是为了找原因而找原因,是为了让用户少受影响。第一问永远是:用户现在还能用吗?
