日志采集工具:tail、grep、awk、sed 在日志排查中的实战用法
日志排查这件事,说白了就是跟时间赛跑。系统出问题了,你第一反应肯定是「看看日志」。但日志文件动辄几百兆,甚至上 G,你不可能用编辑器打开慢慢翻。这时候,tail、grep、awk、sed 这四个命令就是你的救命稻草。
我个人习惯把这四个工具分成两组:「定位组」和「加工组」。tail 和 grep 负责快速定位问题范围,awk 和 sed 负责把定位到的信息提炼成可读的结论。下面我一个个讲,每个都会带上我在项目中踩过的坑。
核心思路:先缩小范围,再提取关键字段,最后格式化输出。别一上来就 awk 花式处理,先看看 tail -f 能不能直接看出问题。
1. tail:实时追踪与快速截取
tail 是我用得最频繁的命令,没有之一。它的核心能力就两个:看尾巴和跟刷新。
- tail -n 100 app.log:看最后 100 行。适合刚启动完服务,想看最后几条日志有没有报错。
- tail -f app.log:实时追踪。适合你在重启服务或压测时,盯着日志滚动。
- tail -f --pid=$PID app.log:当指定进程退出时,自动停止追踪。这个我经常在自动化脚本里用。
我的习惯:线上排查时,我一般先 tail -n 500 error.log | grep -i "exception",快速扫一眼最近有没有异常。如果没发现,再扩大范围。
我曾经遇到过一个 case:服务频繁 OOM,但日志里没有任何 ERROR。我盯着 tail -f gc.log 看了十分钟,发现 Full GC 间隔越来越短。嗯,问题不在业务日志,在 JVM 参数上。所以 tail 不只是看业务日志,GC 日志、系统日志都得会看。
2. grep:精准过滤与上下文提取
grep 是日志排查的「显微镜」。它能帮你从海量日志里捞出你想要的那几行。
| 场景 | 命令 | 说明 |
|---|---|---|
| 精确匹配 | grep "ERROR" app.log |
只显示包含 ERROR 的行 |
| 忽略大小写 | grep -i "timeout" app.log |
匹配 Timeout、TIMEOUT 等 |
| 显示上下文 | grep -C 5 "NullPointer" app.log |
显示匹配行前后各 5 行 |
| 正则匹配 | grep -E "2024-10-1[5-9]" app.log |
匹配 10 月 15 日到 19 日的日志 |
| 反向匹配 | grep -v "INFO" app.log |
排除 INFO 级别,只看 WARN 和 ERROR |
避坑指南:我曾经在排查问题时,直接 grep "error",结果漏掉了大写开头的 "Error"。从那以后,我养成了习惯:只要不确定大小写,就加 -i。另外,日志文件很大时,grep 会吃掉大量内存,建议先 head -n 10000 切一小段再 grep。
3. awk:结构化提取与统计
awk 是日志排查的「手术刀」。它能把一行日志拆成字段,然后按条件提取或计算。
举个例子,你的日志格式是:2024-10-20 10:30:45 ERROR [http-nio-8080-exec-10] TimeoutException: Connection timed out。你想统计每个线程出现 ERROR 的次数,可以这样写:
awk '$3 == "ERROR" {thread[$4]++} END {for (t in thread) print t, thread[t]}' app.log
这个命令做了三件事:
- 判断第三列是不是 ERROR
- 用第四列(线程名)作为 key 计数
- 最后打印每个线程的 ERROR 次数
我经常用 awk 做时间窗口统计。比如,统计每分钟的请求量:
awk '{minute=substr($2,1,5); count[minute]++} END {for (m in count) print m, count[m]}' access.log | sort
我的经验:awk 的默认分隔符是空格,如果你的日志里字段之间用竖线或逗号分隔,记得加 -F'|' 或 -F','。我见过有人用默认分隔符处理 CSV 日志,结果整行被当成一个字段,折腾了半天。
4. sed:流式编辑与批量替换
sed 在日志排查里主要干两件事:替换和删除。它不改变原文件,只修改输出流。
- sed -n '100,200p' app.log:打印第 100 到 200 行。比 head + tail 组合更优雅。
- sed '/ERROR/s/timeout/TIMEOUT/g' app.log:在包含 ERROR 的行里,把 timeout 替换成 TIMEOUT。
- sed '/^#/d' config.conf:删除所有以 # 开头的注释行。
我记得有一次,线上日志里某个敏感字段没脱敏,需要紧急处理。我直接用 sed 's/password=[^&]*/password=***/g' app.log 把密码替换掉,然后重定向到新文件。整个过程不到 10 秒,比写脚本快多了。
注意:sed 默认不会修改原文件。如果你确认要原地修改,加 -i 参数。但线上环境我建议先重定向到新文件,确认无误后再覆盖。我曾经手滑 sed -i 's/prod/dev/g' config.xml,结果把生产配置改成了开发配置……嗯,从那以后我养成了先备份的习惯。
5. 组合实战:一次完整的日志排查流程
光讲单个命令没意思,我带你走一遍真实场景。假设你的 Java 服务在 10:00 到 10:05 之间出现了大量超时,你需要定位根因。
第一步:定位时间窗口
grep -n "2024-10-20 10:0[0-5]" app.log | head -20
先看看这 5 分钟里日志长什么样,有没有明显的异常堆栈。
第二步:提取异常类型
grep "2024-10-20 10:0[0-5]" app.log | grep -i "exception" | awk '{print $3, $NF}' | sort | uniq -c | sort -rn
这条命令把异常类型和最后字段提取出来,统计每种异常出现的次数。结果可能是:
45 ERROR TimeoutException
12 ERROR NullPointerException
3 ERROR SQLException
嗯,TimeoutException 最多,重点查它。
第三步:查看异常上下文
grep -n "TimeoutException" app.log | awk -F: '{print $1}' | head -5 | xargs -I{} sed -n '{},+10p' app.log
找到前 5 个 TimeoutException 的行号,用 sed 打印出该行及其后 10 行,看看堆栈信息。
第四步:关联分析
如果发现 TimeoutException 都跟某个数据库查询有关,再用 grep 统计那个 SQL 的执行时间分布。整个过程,tail、grep、awk、sed 四个命令来回组合,基本能覆盖 80% 的日志排查场景。
总结一下我的组合套路:
- tail 看实时 + 截尾巴
- grep 过滤 + 看上下文
- awk 提取字段 + 做统计
- sed 替换 + 删无用行
这四个命令单独用都有限,但组合起来,威力巨大。
6. 知识体系图:日志排查工具链
下面这张图展示了这四个工具在日志排查流程中的位置和关系。你可以把它当作一个「排查路线图」来用。
这张图的核心逻辑很简单:日志不会自己告诉你问题在哪。你得先用 tail 和 grep 把范围缩小到几十行,再用 awk 和 sed 把关键信息提炼出来。别想着一步到位,分步走才不容易漏掉线索。
最后一个小建议:把这四个命令的常用参数记在脑子里,或者贴在你的终端旁边。排查问题时,每多花一秒想命令,就多一秒的延迟。熟练了,你就能像条件反射一样,看到日志就知道该用什么工具。