用日志把故障原因收窄
目标
从生产日志(catalina.out、GC 日志、访问日志、nginx 错误日志)中提取依据, 缩小故障原因的范围,并编写带有数字的故障报告。
为什么重要
故障响应中最常见的失败,是遇到 502 却去加大超时。
502 是“收到了无效的响应”,而 504 是“没有在规定时间内响应”。
把二者混为一谈,会浪费好几个小时。此外,OOM 的处理方式因类型(Java heap space /
Metaspace / unable to create native thread)而完全不同,
如果不读消息就只是调高 -Xmx,反而可能更糟。
有了从日志中提取数字的本领,这种判断就会从猜测变成依据。
步骤
- 创建
/root/ts,并把/opt/lab/fixtures/tomcat/logs/中的四个文件原样复制过来。 (catalina-oom.log、gc.log、access.log、nginx-error.log) 内容必须与原件完全相同。 - 在
catalina-oom.log中找到 OutOfMemoryError,创建/root/ts/oom.txt。 共两行,格式必须与下面完全一致。
(占位符依次为日志中记录的时间戳、OOM 类型字符串。)time=<로그에 적힌 타임스탬프> type=<OOM 종류 문자열>type取Java heap space/Metaspace/GC overhead limit exceeded/unable to create native thread之一。 - 分析
gc.log,创建/root/ts/gc.txt。共两行。
(占位符依次为 Full GC 发生次数、最长停顿时间——单位毫秒,小数点部分原样保留。)fullgc=<Full GC 발생 횟수> maxpause=<가장 긴 정지 시간, 밀리초, 소수점 포함 그대로> - 从
access.log中提取处理时间最长的前 5 个 URL, 创建/root/ts/slow.csv。首行为url,count,max_ms, 按max_ms降序排列。 - 在
access.log中按 URL 统计 5xx 响应,创建/root/ts/5xx.csv。 首行为url,status,count,按次数降序排列。 - 在
nginx-error.log中按原因统计上游错误, 创建/root/ts/upstream.csv。首行为code,cause,count。code为502或504,cause为connection refused或timeout。 - 启动 Tomcat,导出线程转储并保存到
/root/ts/threads.txt, 然后创建/root/ts/threadstat.txt。共两行。
(占位符依次为转储中的线程总数、处于 WAITING 状态的线程数。) 这两个值必须与total=<덤프에 있는 전체 스레드 수> waiting=<WAITING 상태 스레드 수>threads.txt的内容一致。 - 编写
/root/ts/rca.md。 必须包含## 현상、## 원인、## 조치、## 재발방지这四个 h2 标题(韩文,依次意为“现象”“原因”“措施”“防止复发”), 正文中要原样引用第 2 步的 OOM 类型字符串和第 3 步的fullgc数值。
参考
- 每个 URL 的最大值:
awk -F'|' '{ if ($3 > m[$2]) m[$2]=$3; c[$2]++ } END {...}' - 线程转储:
jcmd <PID> Thread.print > /root/ts/threads.txt - 转储中的线程数:以双引号开头的行就是一个线程。
- 常见错误 1:统计 Full GC 时把 Young GC 也一起算进去。
- 常见错误 2:排序时用字典序排序而不用
sort -n,导致9.5比12.3还大。 - 常见错误 3:报告中没有数字,只写“内存不足”。
保留日志副本
创建 /root/ts,并把 /opt/lab/fixtures/tomcat/logs/ 中的四个文件原样复制过来。
(catalina-oom.log、gc.log、access.log、nginx-error.log)
内容必须与原件完全相同。
故障分析的第一个动作是保留原件。分析过程中日志被轮转或被覆盖的事,真的会发生。
确认 OOM 的发生时刻和类型
在 catalina-oom.log 中找到 OutOfMemoryError,创建 /root/ts/oom.txt。
共两行,格式必须与下面完全一致。
time=<로그에 적힌 타임스탬프>
type=<OOM 종류 문자열>
(占位符依次为日志中记录的时间戳、OOM 类型字符串。)
type 取 Java heap space / Metaspace / GC overhead limit exceeded /
unable to create native thread 之一。
OutOfMemoryError 的处理方式因类型而完全不同。类型写在消息的后半部分。
分析 GC 日志
分析 gc.log,创建 /root/ts/gc.txt。共两行。
fullgc=<Full GC 발생 횟수>
maxpause=<가장 긴 정지 시간, 밀리초, 소수점 포함 그대로>
(占位符依次为 Full GC 发生次数、最长停顿时间——单位毫秒,小数点部分原样保留。)
只需统计 Full GC。停顿时间是每行末尾的毫秒值。其中有小数点,所以请注意排序方式。
提取最慢的 URL
从 access.log 中提取处理时间最长的前 5 个 URL,
创建 /root/ts/slow.csv。首行为 url,count,max_ms,
按 max_ms 降序排列。
访问日志的最后一个字段是处理时间。需要按 URL 分组求最大值。使用 awk 的关联数组,一次扫描就能完成。
5xx 的发生分布
在 access.log 中按 URL 统计 5xx 响应,创建 /root/ts/5xx.csv。
首行为 url,status,count,按次数降序排列。
必须准确指出状态码字段。只挑出以 5 开头的三位数,按 URL 统计。
区分 502 和 504 的原因
在 nginx-error.log 中按原因统计上游错误,
创建 /root/ts/upstream.csv。首行为 code,cause,count。
code 为 502 或 504,cause 为 connection refused 或 timeout。
nginx 错误日志中的措辞会告诉你原因。连接本身被拒绝,与超时,留下的是不同的措辞。
分析真实的线程转储
启动 Tomcat,导出线程转储并保存到 /root/ts/threads.txt,
然后创建 /root/ts/threadstat.txt。共两行。
total=<덤프에 있는 전체 스레드 수>
waiting=<WAITING 상태 스레드 수>
(占位符依次为转储中的线程总数、处于 WAITING 状态的线程数。)
这两个值必须与 threads.txt 的内容一致。
在运行中的 Tomcat 上导出转储,然后按状态统计线程数。转储中线程状态以大写关键字表示。
编写故障报告
编写 /root/ts/rca.md。
必须包含 ## 현상、## 원인、## 조치、## 재발방지 这四个 h2 标题(韩文,依次意为“现象”“原因”“措施”“防止复发”),
正文中要原样引用第 2 步的 OOM 类型字符串和第 3 步的 fullgc 数值。
报告的价值在于数字。请原样引用前面步骤中提取出的值。防止复发的条目中,不要写“加以注意”,而要写具体的配置或监控项目。