系统化调试
用证据定位根因,但让调查深度与问题复杂度匹配。简单问题快速验证;复杂问题逐步缩小;无法立即复现的线上问题先检查可获得的观测证据。
从当前请求判定授权范围
- 只诊断:输出根因、证据与建议,不实施永久修复。
- 诊断并修复:原始修复请求即为范围内修复授权,确认根因后继续实施并验证,不重复询问。
- 请求与已有上下文仍无法判断范围时才询问;修复授权不自动包含生产变更。
- 线上事故的回滚、关闭功能开关、降级或隔离流量属于临时缓解;只有用户已授权事故处置,或已有授权采用的运行手册覆盖该操作时才执行。可逆不等于已授权,只诊断请求不能转成生产操作;恢复后仍须定位根因。
1. 明确问题
从已有上下文确认:
- 实际发生了什么,预期是什么?
- 在什么环境、输入和条件下出现?
- 用户观察到的准确症状是什么?
- 已有哪些错误信息、日志、截图、性能数据或复现步骤?
信息充分时直接调查,不为走流程重复提问。证据不足且答案会改变调查方向时,再向用户确认。
2. 建立证据路径
可以立即复现
永久修复前,先建立一条可重复的问题验证路径。它可以是测试、命令、接口请求、浏览器操作或其他可执行步骤,但应当:
- 捕获用户报告的真实症状,而不是附近的另一个错误;
- 能重复运行,并明确判断问题是否仍然存在;
- 尽可能稳定、快速,并可由代理独立执行;
- 记录实际运行结果,不把“理论上能复现”当作证据。
初始路径不必是最小复现。先获得可靠信号,再按需收紧。
无法立即复现
低频、环境相关或只在线上出现的问题,改为建立证据采集路径:
- 记录现象、影响范围、已知条件和已经排除的原因。
- 明确当前要区分的假设,以及什么信号能支持或否定它们。
- 优先读取已有日志、指标、链路、监控或报警;仍不足时,在授权范围内准备可检查的观测改动,放在最能区分假设的位置。部署或启用继续遵循目标环境权限,不因需要证据自行修改生产。
- 说明真实观察窗口、等待信号和收到证据后的下一步。需要异步跟进时使用当前可用且已授权的调度能力;只有实际创建成功才称已安排,否则说明仍需如何跟进。
此时明确写出“根因尚未确认”;已建立采集路径不等于问题已修复,不为继续流程猜测根因。
3. 按证据选择诊断方法
下面是工具箱,不是固定检查清单。选择成本最低、最能区分当前假设的方法:
- 阅读现有证据:完整阅读错误、调用栈、相关日志和性能数据。
- 检查近期变化:查看相关提交、依赖、配置和环境差异。
- 针对性临时日志:只记录能区分假设的数据流和组件边界,使用统一可搜索标识。
- 断点或交互式调试:直接观察运行时状态,避免用大量日志间接猜测。
- Git 二分定位:已知正常和异常版本时,用
git bisect定位首次引入问题的提交。 - 差异对比:让相同输入经过正常版本与异常版本,比较输出、状态或性能。
- 请求或事件重放:保存真实输入,在隔离环境中重放问题路径。
- 性能诊断:先建立基线,再使用性能分析、时间测量或查询计划定位退化。
- 最小复现:完整场景过慢、不稳定或变量过多时,逐步删除无关条件。
临时日志、监控和报警不得记录密钥、令牌、个人信息或完整敏感载荷。报警应对应可执行动作,避免形成长期噪声。
4. 形成并验证根因假设
- 假设必须能够被证据推翻,并说明它预测会观察到什么。
- 优先验证最能区分多个可能原因的信号。
- 一次只改变一个关键变量;失败后根据新证据更新判断,不叠加未经验证的修复。
- 沿错误数据和调用关系追到最初产生异常的位置,区分症状、直接原因与根因。
- 多次尝试持续暴露不同位置的共享状态或耦合时,暂停补丁式尝试,与用户讨论是否属于架构问题;不使用固定次数代替判断。
5. 修复与验证
仅在用户授权修复且根因已经确认后执行;进入永久修复实现时调用 test-driven-development 判断证据寿命,选择当前证据以及是否需要永久保护:
- 只有永久保护门槛成立且存在能够覆盖真实问题的正确测试接缝时,才把问题固化为失败测试。
- 不满足永久保护门槛或没有正确接缝时,记录这一限制,使用可重复的问题验证路径,不添加无法捕获真实问题的浅层测试。
- 修复已经确认的根因,不混入无关重构。
- 重新运行最初的问题验证路径,确认用户报告的原始症状消失。
- 运行受影响范围的回归检查。
- 删除临时日志、脚本和诊断代码;确有长期价值的观测项应明确保留理由。
输出
简单缺陷用现象、根因、修改和验证的短叙述即可。复杂调查按需补充问题验证或证据采集路径、关键证据链、未知项、剩余风险和等待条件,不强制固定章节。
只诊断时报告证据与建议,不把建议方案写成已完成修改;临时缓解、已建立采集路径和根因修复分别说明实际状态。