日志采集工具: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

这个命令做了三件事:

  1. 判断第三列是不是 ERROR
  2. 用第四列(线程名)作为 key 计数
  3. 最后打印每个线程的 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:替换 / 删除行 排查结论 先定位,再加工,顺序别搞反

这张图的核心逻辑很简单:日志不会自己告诉你问题在哪。你得先用 tail 和 grep 把范围缩小到几十行,再用 awk 和 sed 把关键信息提炼出来。别想着一步到位,分步走才不容易漏掉线索。

最后一个小建议:把这四个命令的常用参数记在脑子里,或者贴在你的终端旁边。排查问题时,每多花一秒想命令,就多一秒的延迟。熟练了,你就能像条件反射一样,看到日志就知道该用什么工具。