
作为互联网开发,你肯定遇过这种情况:线上报了异常,日志文件几百兆,同事对着屏幕一顿操作,半小时过去了还没找到完整堆栈,急得额头冒冷汗,而你看着却只能干着急?上周我就碰到了一模一样的事儿,今天把解决办法掰开揉碎讲给你,下次再遇到,你也能当回 “技术救星”。
一、那顿让我 “血压飙升” 的查日志现场
周三下午,测试环境突然弹出钉钉报警:“订单接口出现 NullPointerException”,新来的同事小杨立马接手排查。我刚好写完一段代码,想着凑过去看看能不能帮上忙,结果这一看,差点把我刚喝的咖啡喷出来。
小杨先是敲了句tail -f a.log | grep “
java.lang.NullPointerException”,然后就盯着屏幕等。足足等了十分钟,才跳出来一行日志:
2025-07-03 11:38:48.339 [http-nio-8960-exec-1] [47gK4n32jEYvTYX8AYti48] [INFO] [GlobalExceptionHandler] java.lang.NullPointerException, ex: java.lang.NullPointerException
java.lang.NullPointerException: null
我提醒他:“兄弟,这只有一行啊,堆栈信息都没出来,怎么找问题根源?”
小杨 “哦” 了一声,紧接着打开 vi 编辑器,把整个 a.log 文件加载进来,然后用/NullPointerException搜索,一个n一个n地往下翻。文件太大,vi 加载时卡了两秒,翻页的时候又一顿一顿的,半分钟过去了,他还在对着满屏的日志发呆,嘴里念叨着:“怎么还没到异常发生的地方……”
我拍了拍他的肩膀:“别这么折腾了,再翻十分钟你也找不到,我教你套‘grep 组合拳’,保证 5 分钟内拿到完整异常信息。” 小杨眼睛一下亮了,赶紧把光标移到终端,掏出小本本准备记 —— 说实话,这种 “被急需” 的感觉,还真有点成就感。
核心问题:为啥你查日志总比别人慢?
实则小杨的操作,许多开发刚入行时都做过,不是他不认真,而是没 get 到日志查询的 “核心技巧”。咱们细想下,查日志时最头疼的问题无非三个:
- 看不到完整上下文:Java 异常堆栈少则十几行,多则几十行,只搜关键词只能拿到一行,根本不知道异常是哪个方法抛出来的;
- 实时日志等得慌:用tail -f等新日志就算了,万一异常半天不出现,时间全浪费在 “等” 上;
- 压缩日志不会查:服务器日志按天压缩成.gz 文件,总不能每次都先解压再搜,既占空间又费时间。
找到问题根源,解决起来就简单了。接下来,我就把当时教给小杨的三招,连同步率带场景,全分享给你,每一招都能直接用到工作里。
3 招 grep 组合拳,覆盖 90% 查日志场景
第一招:查异常堆栈,用grep -A抓完整上下文
小杨最开始的问题,就是没拿到完整堆栈。这时候别用 vi 硬翻,直接给 grep 加-A N参数(A 是 After 的缩写,N 代表 “后面 N 行”),就能把异常后面的日志全抓出来。
当时我让小杨敲的命令是:
# 查找NullPointerException,同时显示后面50行日志
grep -A 50 "java.lang.NullPointerException" a.log
他敲完回车,满屏的异常堆栈直接弹了出来,从异常发生的方法,到参数传递的过程,一目了然。小杨当时就惊呼:“原来还能这么玩?我之前翻十分钟都没翻到的内容,这一下就出来了!”
如果日志太多,屏幕滚得太快,还能加个less分页,方便慢慢看:
grep -A 50 "java.lang.NullPointerException" a.log | less
在 less 视图里,用↑↓键上下滚,按G直接跳末尾,按/还能继续搜关键词,看完按q退出就行 —— 这招一用,小杨当场就把 “vi + 搜索” 的操作从自己的 “技能库” 里删了。
第二招:实时监控日志,tail -f + grep精准捕捉
有时候异常不是一直有,而是偶尔冒出来,这时候总不能一直等tail -f输出。这时候就把第一招和tail -f结合起来,让日志 “主动找你”。
我给小杨举了个例子:如果想实时监控订单接口的异常,就用这行命令:
# 实时监控a.log,只显示包含NullPointerException的行及后面50行
tail -f a.log | grep -A 50 "java.lang.NullPointerException"
这样一来,只要异常一出现,终端就会立刻把完整堆栈打出来,不用再盯着屏幕 “守株待兔”。小杨试了下,还特意模拟了一次异常触发,结果日志秒级弹出,他笑着说:“后来摸鱼的时候,也能知道异常啥时候来,再也不用一直盯着屏幕了!”
对了,这里还有个小细节:加-i参数能忽略大小写,列如把 “NullPointerException” 写成 “nullpointerexception” 也能搜到,避免由于拼写大小写失误白等半天。
第三招:查压缩日志,zgrep直接干不用解压
服务器上的日志,一般都会按天压缩成.log.2025-07-02.gz这种格式,小杨之前遇到压缩日志,都是先解压再查,光解压就要等一分钟。实则根本不用这么麻烦,用zgrep命令就能直接搜压缩文件,和查普通日志没区别。
我让小杨试了搜前一天的压缩日志:
# 搜索所有.gz压缩文件中的NullPointerException,显示文件名和后面50行
zgrep -H -A 50 "java.lang.NullPointerException" *.gz
这里的-H参数很关键,能显示异常来自哪个压缩文件,列如 “a.log.2025-07-02.gz”,这样就不用猜异常是哪天发生的了。小杨敲完命令,三秒钟就出了结果,他当场感慨:“之前解压查日志,浪费的时间加起来,都能多写两个接口了!”
成果:从 “半小时查不到” 到 “5 分钟定位问题”
教完这三招,我让小杨重新排查测试环境的异常。这次他先是用grep -A 50拿到完整堆栈,发现异常出在 “
OrderService.getOrderDetail ()” 方法里;接着用tail -f + grep监控实时日志,确认异常不再复现;最后用zgrep查了前三天的日志,确认这是第一次出现 —— 整个过程下来,只用了 4 分 20 秒,比之前的 “半小时无果” 快了 7 倍!
小杨后来跟我说,他把这三招教给了前公司的同事,对方直接把他拉进了技术分享群,还让他多分享点 “实用技巧”。实则咱们做开发,不必定非要搞多复杂的架构,有时候掌握几个 “小技巧”,就能节省大量时间,把精力放在更核心的代码上。
最后问你个事儿:你平时查日志,用的是这些方法吗?还是有更高效的技巧?评论区分享下,咱们一起当 “高效开发”,少加班多摸鱼~
收藏了,感谢分享