事情是这样的。
小王刚上线的功能在测试环境报错了,告警第一时间,我正好有空,就想着过去指导一下,顺便展示下老司机的经验。
结果我过去时,他正盯着终端,执行这个命令:
tail -f app.log | grep "NullPointerException"
屏幕上一片寂静。等了快一分钟,终于跳出一行:
2023-11-15 14:30:01.339 ERROR - java.lang.NullPointerException: null
“找到了!”小王兴奋地说。
我忍不住插话:“兄弟,你这只能看到异常类型,堆栈信息全丢了啊。”
小王明显愣了一下,然后熟练地用 vi 打开整个日志文件,开始 /NullPointerException 搜索,接着不停地按 n 键在几十万行日志里逐个翻找……
实在没忍住,我推开键盘:“让一下,给你看个更利索的。”
他盯着屏幕愣了三秒,然后猛地一拍桌子:“我靠!原来还能这么玩?你这套操作必须写成文章,我前公司的兄弟们现在还在用vi硬翻呢!”
既然需求这么明确,今天我就把这份压箱底的日志排查指南完整分享给大家。
看完对你一定有用!
我接手后,在小王面前执行了第一条命令:
tail -f error.log | grep -A 30 "NullPointerException\|TimeoutException"
屏幕输出瞬间改变,不再是孤立的单行错误,而是完整的异常堆栈、业务参数和调用链,开始实时滚动呈现。
“查历史压缩日志,可以跳过解压步骤。”我接着执行了第二条命令:
zgrep -A 30 -H "触发条件关键词" *.gz
文件名、异常堆栈、关键上下文直接展示在眼前。
前后不过一分钟,我们便精准定位了问题根源——一个隐蔽的并发边界条件。
事后我和小王复盘了一下。
小王原来的操作方式属于典型低效操作方式:
单行陷阱:tail -f | grep 只能捕获异常片段,丢失关键堆栈信息。
视觉疲劳:逐行搜索消耗注意力,上下文关联困难。
格式障碍:面对压缩日志文件时束手无策,额外解压步骤打断工作流。
以下内容是实战干货,大家可以收藏!
一、grep 组合拳:四类实战场景
# 关键参数:-A 确保堆栈完整性grep -A50 "NullPointerException" application.log | less
实战要点:
1)-A N:显示匹配行后N行,覆盖典型堆栈深度
2)less 分页器:支持 /、? 搜索、空格翻页、q退出
3)适用场景:事后分析、根因定位
# 生产环境实时告警模式tail -f application.log | grep -Ai30 "ERROR\|Exception"
专业技巧:
1)-i:忽略大小写,覆盖不同日志规范
2)正则组合:使用 \| 匹配多类异常关键词
3)Ctrl+C 优雅终止,避免信号干扰
# 跳过解压步骤,直击问题核心zgrep -H -A50 "OutOfMemoryError" *.gz
参数解析:
1)-H:保留文件名,便于多文件溯源
2)原生支持 .gz 格式,无需预处理
3)扩展应用:结合 zcat | grep 处理特殊压缩格式
# 多文件异常频率分析grep -c "ConnectionTimeout" *.log | sort -nr -t: -k2
进阶统计:
1)管道组合:sort 按数量降序排列
2)可视化准备:输出可直接导入监控系统
3)模式识别:通过频率变化发现系统瓶颈
1)上下文控制矩阵
2)反向工程技巧
# 排除干扰信息,聚焦核心问题grep -v "健康检查\|心跳" app.log | grep -A30 "异常"# 过滤已知噪音,提升信号纯度
二、生产环境实战进阶
# 时间窗口关联查询grep -C10 "2023-11-15 14:30" app.log | grep -A20 "事务回滚"# 分布式追踪集成grep -A40 "traceId:0a1b2c3d" service*.log
# 大文件处理加速方案grep -m1000 "ERROR" large.log # 限制输出数量grep --binary-files=text "异常" binary.log # 二进制文件安全处理
# 高效模式匹配grep -E "Timeout\|Reject\|Failure" # 扩展正则,逻辑清晰fgrep -f patterns.txt app.log # 固定字符串匹配,性能最优
三、扩展你的工具链
1)wc:行数统计利器
# 统计错误出现的总次数grep "ERROR" app.log | wc -l# 统计唯一异常类型数量grep "Exception" app.log | awk -F':' '{print $4}' | sort | uniq | wc -l
2)awk:字段处理与数据提炼
# 提取特定字段(如第7列状态码为500的日志行)awk '$7 == 500' app.log# 统计接口平均响应时间awk '{sum+=$9; count++} END {print "平均响应时间:", sum/count, "ms"}' app.log# 统计每个URL的访问次数awk '{print $5}' access.log | sort | uniq -c | sort -nr
3)sed:流式文本编辑与清洗
# 提取今天14:00到14:10的日志sed -n '/2023-11-15 14:00:00/,/2023-11-15 14:10:00/p' app.log# 清理日志中的敏感信息(如手机号)sed 's/\([0-9]\{3\}\)[0-9]\{4\}\([0-9]\{4\}\)/\1****\2/g' app.log
1)场景:统计每分钟超时错误的数量
grep "Timeout" application.log | \sed -n 's/.*\(2023-11-15 14:[0-9][0-9]\).*/\1/p' | \sort | \uniq -c
2)场景:分析Nginx日志,找出返回码非200的请求IP
awk '$9 != 200 {print $1}' access.log | sort | uniq -c | sort -nr | head -20
如果字段的最大可能长度超过255字节,那么长度值可能…
只能说作者太用心了,优秀
感谢详解
一般干个7-8年(即30岁左右),能做到年入40w-50w;有…
230721