查日志查到眼瞎?这几个 Linux 命令教你秒级定位线上 Bug
摘要:线上出故障,还在傻傻地把 10G 日志下载到本地?还在用 vim 打开大文件导致服务器卡死?本文汇集了阿里、腾讯大厂运维都在用的 Linux 日志分析“神技”。从基础的 grep 到“上帝视角”的 awk,一文讲透,建议收藏!
很多新手(甚至工作几年的老手)在排查线上问题时,除了 tail -f 和 cat,几乎只会“肉眼扫描”。面对海量日志,就像大海捞针,查到眼瞎也找不到 Bug 在哪。
排查线上问题,有时候不拼技术深度,拼的是“手速”和“工具箱”。 今天,我们就来盘点一下那些 让运维大佬直呼内行 的 Linux 日志分析“骚操作”。从基础到高阶,教你 秒级定位 线上故障!
第一关:扔掉你的 cat 和 vim
在生产环境,永远不要用编辑器打开大日志文件! 这是铁律。不仅慢,而且容易引发 OOM(内存溢出),直接搞挂线上服务。
1. 优雅的 less
less 是查看大文件的神器,它不会一次性加载整个文件,而是按需加载。但你真的会用它的快捷键吗?大部分人只会按空格翻页。
less application.log
必备“骚操作”快捷键(建议背诵):
G(大写):直接跳到文件末尾。排查最新报错时必用。g(小写):跳到文件开头。/keyword:向下搜索关键字(按n查找下一个,N查找上一个)。?keyword:向上搜索关键字。F(Shift+f):进入“实时滚动模式”。- 这是最骚的! 效果等同于
tail -f,日志会实时滚动。 - 看到可疑日志时,直接按
Ctrl + C,立刻回到普通浏览模式,可以继续上下翻页查找上下文。 - 相比 tail -f,less 的 F 模式实现了“动静结合”。
- 这是最骚的! 效果等同于
2. 更有“上下文”的 grep
只 grep 报错关键字够吗?不够!
你看到了 NullPointerException,但你不知道是哪个参数导致的,因为参数打印在上一行;你也不知道堆栈信息,因为堆栈在下面几行。
错误示范:
grep "Exception" application.log
# 结果:只显示了一行报错,完全不知道前因后果
正确姿势:使用 -C 参数
# 查找 "Error" 关键字,并显示它 前后各 10 行 的内容
grep -C 10 "Error" application.log
# 只要上面的 10 行(Before)
grep -B 10 "Error" application.log
# 只要下面的 10 行(After)
grep -A 10 "Error" application.log
💡 Tips:加上
--color参数(大部分系统默认开启),关键字会高亮显示,不仅护眼,还能一眼看到重点。
第二关:精准打击,多重过滤
线上日志往往泥沙俱下。你想找真正的 Error,结果屏幕上全是无关紧要的 Info、Debug 日志,或者是某个已知的报错(噪音)刷屏。
这时候你需要 “排除法” 和 “组合拳”。
1. 排除干扰项 (-v)
假设日志里全是心跳检测(HeartBeat)的报错,掩盖了真正的业务错误。
# 查找 Error,但 排除 掉包含 "HeartBeat" 的行
grep "Error" application.log | grep -v "HeartBeat"
2. 逻辑或 (-E)
你想同时查 "Error" 和 "Exception"?写两遍 grep?太 Low 了。
# 使用扩展正则(Extended regex),同时查找 Error 或 Exception
grep -E "Error|Exception|Fatal" application.log
3. 精确匹配次数 (-c)
老板问:“这个问题今天发生了多少次?” 不要傻傻地数行数。
# 直接统计出现次数
grep -c "OutOfMemory" application.log
# 或者配合 wc -l (Word Count lines)
grep "OutOfMemory" application.log | wc -l
第三关:上帝视角,Awk 数据分析
如果你只会 grep,你只是个合格的搬砖工;如果你精通 awk,你就是 数据分析师。
Awk 是 Linux 下最强大的文本处理工具,它把每一行日志看作一条记录,每一个空格(默认)分割的字段看作列。
假设你的 Nginx 日志格式如下:
192.168.1.5 - - [27/Oct/2023:10:00:01] "GET /api/user HTTP/1.1" 200 1024 "-" "Chrome" 0.050
(注:最后一列 0.050 是响应时间)
场景一:那个 IP 在攻击我?(统计 Top 10 IP)
发现服务器负载飙高,想看看是哪个 IP 访问量最大(取第1列)。
# 1. awk '{print $1}': 打印第一列(IP)
# 2. sort: 排序(uniq 统计前必须排序)
# 3. uniq -c: 去重并统计出现次数
# 4. sort -nr: 按数量(n)倒序(r)排列
# 5. head -n 10: 取前10名
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -n 10
输出结果示例:
40240 219.144.89.39 <-- 抓到了!就是这个IP在刷
7468 219.144.88.173
7348 219.144.89.114
7141 219.144.89.44
5563 122.246.31.49
5534 122.246.30.211
5279 122.246.30.27
4631 219.144.88.174
4611 113.219.202.173
4522 113.219.202.141
...
高能预警:这行命令价值千金!在遭遇 DDoS 攻击 或 爬虫刷量 时,这就是你的“照妖镜”。
场景二:谁在拖慢系统?(耗时分析)
假设日志的 最后一列 是响应耗时。我们要找出耗时超过 1秒 的接口,并统计排名。
# 1. 筛选:如果最后一列 ($NF) 大于 1
# 2. 打印:打印第 7 列(URL)
awk '$NF > 1 {print $7}' access.log | sort | uniq -c | sort -nr | head -n 10
如果你想看具体的耗时和 URL:
awk '$NF > 1 {print $NF, $7}' access.log | sort -nr | head -n 10
第四关:时间穿越,范围搜索
故障发生在 10:00 到 10:30 之间,日志文件有几百万行,怎么只看这段时间的?
1. 笨办法(不推荐)
用 grep 拼命搜时间字符串。
grep "10:00", grep "10:01"... 缺点是如果是跨多分钟,你需要写一堆正则,非常痛苦。
常见的“坑”:Sed 范围搜索
网上很多教程教你用 sed -n '/10:00/,/10:30/p'。
千万别盲目用!
sed 只是机械地匹配字符串。如果你的日志包含好几天的数据,或者“10:00”刚好出现在某行报错信息正文里,sed 会像个开关一样被反复打开,导致你查出一堆无关日期的脏数据。
王者解法:Awk 字符串比较
awk 支持字符串的字典序比较(Lexicographical Comparison)。只要你的日志时间格式是标准的(如 HH:mm:ss 或 ISO8601),就可以直接比大小!
假设日志格式如下(时间在第 4 列,例如 [10:00:00]):
2023-10-27 10:00:01 INFO ...
命令如下:
# 假设时间在第 2 列 ($2)
# 语法: awk '$列号 >= "开始时间" && $列号 <= "结束时间"'
awk '$2 >= "10:00:00" && $2 <= "10:30:59"' application.log
为什么这个更强?
- 精准:它是真的在比大小,而不是在“找文字”。
- 无视缺失:即使日志里正好没有
10:00:00这一秒(比如那一秒没人访问),只要有10:00:01,awk依然能正确识别范围。而sed如果找不到起始锚点,可能直接就失效了。 - 支持跨天精准定位:如果加上日期,比如
$1" "$2(日期+时间),就能精准定位到某一天的具体时间段,彻底告别“昨日重现”。
# 组合日期和时间进行比较(假设 $1是日期,$2是时间)
awk '$1" "$2 >= "2023-10-27 10:00:00" && $1" "$2 <= "2023-10-27 10:30:00"' application.log
💡 经验之谈:永远不要相信日志里的每一秒都存在,“比较大小”永远比“精确匹配”更可靠。
第五关:Json 时代的利器 jq
现在的微服务日志大多是 JSON 格式。用 awk 或者 grep 处理 JSON 简直是噩梦。这时候你需要 jq(大多数服务器需要单独安装 yum install jq,但绝对值得)。
假设日志是这样的:
{"time": "2023-10-27", "level": "ERROR", "msg": "DB timeout", "meta": {"user_id": 1001, "trace_id": "abc-123"}}
1. 格式化美化
# 让一坨 JSON 变成可读的缩进格式
cat app.json.log | jq .
2. 提取特定字段
老板只想看 报错信息 和 用户ID,不想看其他乱七八糟的。
# 1. grep 筛选 ERROR
# 2. jq 构造新的 JSON 对象输出
grep "ERROR" app.json.log | jq '{message: .msg, user: .meta.user_id}'
输出结果:
{
"message": "DB timeout",
"user": 1001
}
终极必杀:组合拳实战 Case
光说不练假把式。我们来看两个真实的线上“灵异” Case。
Case 1:磁盘空间报警,但找不到大文件
现象:
- 监控报警:磁盘使用率 100%。
- 你登录服务器,执行
df -h确认满了。 - 执行
du -sh *统计各目录大小,结果加起来才占了 50%。 - 疑问:剩下的 50% 空间被鬼吃了?
原因:
通常是有大文件(如日志)被直接删除了(rm),但进程(如 Java/Nginx)还持有该文件的句柄(File Descriptor),导致文件虽然在目录里看不到了,但实际上并没有释放磁盘空间。
骚操作定位:
# 列出所有被打开的文件,并筛选出状态为 "deleted" 的
lsof | grep deleted
结果示例:
java 1234 root 1w REG 253,0 10G 123456 /var/log/app.log (deleted)
看到那个 10G 了吗?就是它!
解决:
- 找到 PID (1234),重启该进程。
- 以后别直接 rm,应该用
echo "" > app.log来清空文件。
Case 2:抓取线上偶发的 500 错误
现象:
只有极少数请求报错,刷新就没了,tail -f 刷得太快根本看不过来,眼睛都花了。
骚操作定位:
# --line-buffered: 保证 grep 能实时输出,而不是等缓冲区满了再吐出来
tail -f access.log | grep --line-buffered " 500 "
或者把报错日志实时“分流”到单独文件,慢慢分析:
tail -f access.log | grep --line-buffered " 500 " >> error_only.log &
(你可以去喝杯咖啡,过一会回来 cat error_only.log 慢慢看)。
总结
排查线上问题,快 就是优雅,准 就是专业。
- 看大文件:用
less,善用G和F。 - 查关键字:用
grep -C看上下文,grep -v排除噪音。 - 做统计:
awk + sort + uniq是运维统计学的灵魂。 - 查 JSON:装一个
jq,从此不求人。 - 查幽灵文件:
lsof | grep deleted专治磁盘灵异事件。
命令行不仅是工具,更是一种 工程师文化。当你能在黑色的终端窗口里指点江山,那种掌控感,是用鼠标点点点永远体会不到的。
收藏这篇文章,下次线上报警时,你就是那个全组最靓的仔!
👇 觉得有用?点个「推荐」或「分享」给那个正在查 Bug 的兄弟吧!
Q.E.D.


