2. 故障信息收集

故障发生了,第一反应是什么?别急着动手修。先停下来,把现场「拍张照」。

我见过太多人,一看到告警就冲上去重启服务。结果呢?故障是恢复了,但根因永远成了谜。下次同样的故障再来,还是抓瞎。

这一章,我们就聊聊怎么系统性地收集故障信息。说白了,就是给事故现场做一次完整的「法医取证」。

2.1 信息收集的黄金时间窗口

故障信息有个特点——它会随时间衰减。进程重启了,日志就丢了;内存被回收了,堆栈就没了;用户流量切换了,现场就变了。

我个人习惯,故障发生后的前5分钟,只做一件事:收集。不修复,不重启,不猜测。

黄金5分钟行动清单:

  1. 确认告警时间点与故障现象是否匹配
  2. 冻结当前进程状态(不重启、不kill)
  3. 启动日志与监控的并行采集
  4. 记录操作者的每一步操作(谁、什么时间、做了什么)

注意: 如果故障正在导致数据丢失或资损,优先止损。但止损前,尽量保留一份「快照」。我曾经因为犹豫了30秒没重启,多拿到了一个关键的core dump,直接定位了问题。

2.2 日志采集:不只是tail -f

日志是最直接的证据。但很多人采集日志的方式太粗糙了。

2.2.1 采集哪些日志?

日志类型 采集内容 采集时机
应用日志 ERROR/WARN级别日志、业务关键日志 故障发生前后各30分钟
系统日志 /var/log/messages, dmesg, syslog 故障发生前后各1小时
访问日志 Nginx/Apache access log, 网关日志 故障发生前后各2小时
慢查询日志 MySQL slow query log, Redis slow log 故障发生前后各15分钟
GC日志 JVM GC log, 内存Dump 故障发生时立即采集

2.2.2 采集命令与技巧

别只靠人工去服务器上翻日志。我建议提前准备好脚本或工具包。

# 采集应用日志(带时间戳过滤)
journalctl -u myapp --since "2024-01-15 10:00:00" --until "2024-01-15 11:00:00" > app.log

# 采集系统日志(保留最后5000行)
dmesg -T | tail -5000 > dmesg.log

# 采集Java应用线程栈(连续采集3次,间隔5秒)
for i in 1 2 3; do
  jstack <pid> > thread_dump_$i.txt
  sleep 5
done

# 采集网络连接状态
ss -tpan > ss_output.txt
netstat -s > netstat_stats.txt

我的经验: 线程栈一定要连续采集多次。单次采集只能看到「某一瞬间」的状态,连续采集才能看到线程是否在「卡住」。我曾经靠3次线程栈的对比,发现一个线程每次都在同一个JDBC调用上阻塞,最终定位到数据库连接池泄漏。

2.3 监控数据:从指标到真相

监控数据是故障的「心电图」。它能告诉你系统在故障前后经历了什么。

2.3.1 关键监控指标

  • 基础设施层: CPU使用率、内存使用率、磁盘IO、网络带宽
  • 中间件层: 连接数、队列深度、请求延迟P99/P95、错误率
  • 应用层: QPS、RT、GC频率与耗时、线程池活跃数
  • 业务层: 订单量、支付成功率、用户登录成功率

2.3.2 采集方法

现在的监控系统(Prometheus、Grafana、Datadog)一般都支持数据导出。但要注意,监控数据的粒度很重要。

场景 推荐粒度 采集时长
常规排查 1分钟 故障前后各1小时
高精度排查 10秒 故障前后各10分钟
毛刺分析 1秒 故障前后各2分钟

避坑指南: 我曾经遇到过一个案例,监控面板上CPU使用率只有60%,但服务已经超时严重。后来发现是监控采集周期是1分钟,而CPU毛刺只有几秒钟。换成10秒粒度后,才看到CPU在故障期间频繁冲到100%。

2.4 告警信息:别只看标题

告警是故障的「第一声哨响」。但很多人只看告警标题,忽略了告警里的上下文。

2.4.1 告警信息采集清单

  • 告警标题与级别: P0/P1/P2,明确严重程度
  • 告警时间: 精确到秒,与日志时间对齐
  • 告警内容: 完整的告警消息,包括触发值、阈值、持续时间
  • 告警标签: 主机名、服务名、集群名、地域
  • 告警链路: 是否有关联告警?是否有告警风暴?

2.4.2 告警风暴的处理

故障发生时,告警可能像潮水一样涌来。这时候别慌。

  1. 先看第一条告警: 通常第一条告警是根因,后面的都是衍生告警
  2. 看告警的依赖关系: 数据库挂了 -> 应用超时 -> 网关报错,这是典型的传播链
  3. 记录告警时间线: 把每条告警的时间点画出来,看传播路径

注意: 告警系统本身也可能出问题。我曾经遇到过告警延迟了15分钟才发出,导致排查方向完全错了。所以,不要100%相信告警时间,一定要和日志时间交叉验证。

2.5 现场快照:给系统拍一张「CT」

现场快照是故障排查的「核武器」。它能让你在故障发生后,还能「回到」现场。

2.5.1 快照采集内容

快照类型 采集命令/工具 说明
进程快照 ps aux, top -b -n1 记录所有进程状态
网络快照 ss -tpan, lsof -i 记录所有网络连接
文件系统快照 df -h, du -sh /* 记录磁盘使用情况
内核快照 /proc/sys/... 关键文件 记录内核参数
应用快照 jstack, jmap, heap dump 记录应用内部状态

2.5.2 快照采集脚本示例

#!/bin/bash
# 故障现场快照采集脚本
# 用法: ./snapshot.sh <故障时间>

SNAPSHOT_DIR="/tmp/fault_snapshot_$(date +%Y%m%d_%H%M%S)"
mkdir -p $SNAPSHOT_DIR

# 1. 系统信息
echo "=== System Info ===" > $SNAPSHOT_DIR/system_info.txt
uname -a >> $SNAPSHOT_DIR/system_info.txt
cat /proc/loadavg >> $SNAPSHOT_DIR/system_info.txt
cat /proc/meminfo >> $SNAPSHOT_DIR/system_info.txt

# 2. 进程信息
ps aux --sort=-%cpu | head -50 > $SNAPSHOT_DIR/top_cpu_processes.txt
ps aux --sort=-%mem | head -50 > $SNAPSHOT_DIR/top_mem_processes.txt

# 3. 网络连接
ss -tpan > $SNAPSHOT_DIR/network_connections.txt
netstat -i > $SNAPSHOT_DIR/network_interfaces.txt

# 4. 磁盘IO
iostat -x 1 3 > $SNAPSHOT_DIR/disk_io.txt

# 5. 应用日志(最近1000行)
journalctl -u myapp --since "$1" --until "$(date -d "$1 + 30 minutes" +'%Y-%m-%d %H:%M:%S')" > $SNAPSHOT_DIR/app_log.txt

echo "Snapshot saved to: $SNAPSHOT_DIR"

我的建议: 把这个脚本提前部署到所有服务器上。故障发生时,直接运行。别等到故障了再现场写脚本,那时候你手都在抖。

2.6 信息收集的完整流程

说了这么多,我们来画一张图,把整个流程串起来。

故障信息收集流程图 故障信息收集完整流程 故障发生 黄金5分钟:停止操作 并行采集:日志 + 监控 + 告警 + 现场快照 日志采集 监控数据导出 告警信息整理 交叉验证:时间线对齐 核心原则 先拍照,后修复

2.7 信息归档与标记

信息收集完了,不是扔到文件夹里就完事了。归档和标记同样重要。

2.7.1 归档规范

  • 命名规则: 故障时间_服务名_故障类型_收集人,例如:20240115_1030_order_service_timeout_zhangsan
  • 目录结构: 按层级组织,logs/、metrics/、alerts/、snapshots/
  • 元数据文件: 每个归档包内放一个README.md,记录收集时间、收集人、故障现象、操作步骤

2.7.2 标记关键信息

在日志和监控数据中,标记出关键的时间点和异常点。这能帮你在后续分析时快速定位。

# 在日志中插入标记
echo "=== FAULT_MARKER: 2024-01-15 10:30:00 ===" >> app.log

# 在监控数据中标记
# 使用Grafana的注释功能,在时间轴上标记故障开始和结束时间

总结一下: 故障信息收集不是简单的「把日志拷出来」。它是一个系统性的工程,需要你在黄金时间内,并行采集日志、监控、告警和现场快照,然后交叉验证,最后归档标记。做到这一步,你的根因分析就已经成功了一半。