3. 日志采集工具:tail、grep、awk、sed 在日志分析中的实战用法
日志分析的第一步是什么?不是写脚本,不是上平台。说白了,是先把日志「拿到手」。
我见过太多人,一上来就想着用 ELK、用 Splunk,结果连日志文件在哪、怎么快速翻看都不清楚。嗯,这其实是个基本功问题。今天我们就聊聊 Linux 下四个最经典的日志处理工具——tail、grep、awk、sed。它们单个拎出来都不难,但组合起来,能解决 90% 的日常日志分析需求。
核心思路:tail 负责「看」,grep 负责「筛」,awk 负责「切」,sed 负责「改」。四者配合,一条命令就能完成从原始日志到结构化数据的转换。
3.1 tail:实时追踪日志的「眼睛」
tail 是我用得最频繁的命令,没有之一。线上排查问题,第一件事就是 tail -f 挂上日志。
3.1.1 基础用法
tail -n 100 app.log:看最后 100 行。默认是 10 行,但日志分析时 10 行通常不够。tail -f app.log:实时追踪。日志有新写入,终端立刻显示。tail -f --pid=12345 app.log:当进程 12345 退出时自动停止追踪。这个我经常在脚本里用。
我的习惯:线上看日志,我一般用 tail -f -n 200 app.log | grep --line-buffered "ERROR"。加上 --line-buffered 能让 grep 实时输出,否则会有延迟。这个坑我踩过,当时排查一个接口超时问题,grep 半天没反应,后来才发现是缓冲区没刷新。
3.1.2 进阶技巧
- 多文件追踪:
tail -f app.log error.log,每个文件前会显示文件名。 - 从指定行开始:
tail -n +500 app.log,从第 500 行开始显示到末尾。适合分段查看大文件。 - 结合 less:
tail -n 10000 app.log | less,先看尾部一万行,再用 less 翻页搜索。
3.2 grep:日志过滤的「筛子」
grep 的核心就一个字——「筛」。从海量日志里把你要的行挑出来。
3.2.1 常用参数
| 参数 | 作用 | 实战场景 |
|---|---|---|
-i |
忽略大小写 | 日志里 ERROR 和 error 混用的情况 |
-v |
反向匹配 | 排除心跳日志:grep -v "heartbeat" |
-E |
扩展正则 | 匹配多个关键词:grep -E "ERROR|FATAL" |
-A/-B/-C |
上下文行 | grep -A 3 -B 1 "OOM" 看错误前后 |
-c |
计数 | 统计错误次数:grep -c "500" access.log |
--line-buffered |
行缓冲输出 | 配合 tail -f 实时过滤 |
我曾经踩过的坑:有一次排查线上频繁的 502 错误,我用 grep "502" access.log 搜了半天,结果一条都没找到。后来才发现,Nginx 的日志格式里状态码前面有空格,我搜的是 "502",但实际日志里是 " 502"。从那以后,我习惯用 grep " 502 " 或者 grep -E "\b502\b" 来精确匹配。
3.2.2 实战组合
# 统计每个小时 5xx 错误的数量
grep " 5[0-9][0-9] " access.log | awk '{print $4}' | cut -d: -f2 | sort | uniq -c
# 找出最近 10 分钟的错误日志
tail -n 5000 app.log | grep -E "ERROR|FATAL" | grep "$(date +'%H:%M' -d '-10 min')"
# 多关键词且关系(同时包含 A 和 B)
grep "A" app.log | grep "B"
3.3 awk:日志解析的「瑞士军刀」
awk 是这四个工具里最强大的,也是学习曲线最陡的。但别怕,日志分析只需要掌握几个核心用法就够了。
3.3.1 核心概念
awk 默认按空格或制表符分割每一行,分割后的字段用 $1、$2... 表示。$0 表示整行。
# 假设日志行:2024-01-15 10:30:45 ERROR [main] NullPointerException
# $1=2024-01-15 $2=10:30:45 $3=ERROR $4=[main] $5=NullPointerException
# 只打印时间戳和错误级别
awk '{print $1, $2, $3}' app.log
# 只打印 ERROR 级别的行
awk '$3 == "ERROR" {print $0}' app.log
3.3.2 常用模式
| 模式 | 说明 | 示例 |
|---|---|---|
BEGIN{} |
处理前执行一次 | awk 'BEGIN{print "时间,级别,消息"}' |
END{} |
处理后执行一次 | awk 'END{print "总计:", NR, "行"}' |
/pattern/ |
正则匹配行 | awk '/ERROR|FATAL/' |
条件表达式 |
字段比较 | awk '$3 > 500' |
3.3.3 实战案例
# 统计每个接口的请求次数和平均响应时间
# 日志格式:/api/user 200 0.032
awk '{count[$1]++; sum[$1]+=$3} END{for(k in count) print k, count[k], sum[k]/count[k]}' access.log
# 找出响应时间超过 1 秒的慢请求
awk '$3 > 1.0 {print $1, $2, $3}' access.log | sort -k3 -rn | head -20
# 按小时统计错误数
awk '{hour=substr($2,1,2); if($3=="ERROR") err[hour]++} END{for(h in err) print h, err[h]}' app.log
我的建议:awk 的数组和 END 块是日志统计的利器。你想想看,一条命令就能完成分组、计数、求平均,比写 Python 脚本快多了。我经常在排查问题时,先用 awk 快速跑一个统计,看看数据分布,再决定要不要深入分析。
3.4 sed:日志清洗的「手术刀」
sed 的定位是「流编辑器」。它不擅长统计,但擅长对文本做精确的增删改查。
3.4.1 核心操作
| 操作 | 语法 | 实战场景 |
|---|---|---|
| 替换 | sed 's/旧/新/g' |
脱敏 IP、清理特殊字符 |
| 删除 | sed '/pattern/d' |
过滤掉 debug 日志 |
| 插入 | sed '3i\新行' |
在指定行前加标记 |
| 行号范围 | sed -n '100,200p' |
提取日志的某一段 |
3.4.2 实战用法
# 脱敏:把日志里的 IP 替换成 xxx.xxx.xxx.xxx
sed -E 's/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/xxx.xxx.xxx.xxx/g' app.log
# 删除所有 DEBUG 级别的日志
sed '/DEBUG/d' app.log > clean.log
# 提取第 1000 到 2000 行
sed -n '1000,2000p' app.log
# 在每行末尾添加逗号(方便转 CSV)
sed 's/$/,/' app.log
注意:sed 默认直接输出到终端,不会修改原文件。要原地修改,加 -i 参数:sed -i 's/foo/bar/g' file。但线上环境我建议先不加 -i 试跑一次,确认没问题再改。我曾经手滑把配置文件改坏了,还好有备份。
3.5 四件套组合实战
单独用每个工具都能干活,但组合起来才是真正的生产力。下面是我在项目中常用的几个组合套路。
3.5.1 实时错误监控 + 统计
# 实时监控错误,每 10 秒统计一次
tail -f app.log | grep --line-buffered "ERROR" | awk '{print $1, $2, $5}' |
while read line; do
echo "$line"
# 每 10 行输出一次统计
count=$((count+1))
if [ $((count % 10)) -eq 0 ]; then
echo "--- 已捕获 $count 条错误 ---"
fi
done
3.5.2 日志清洗 + 结构化输出
# 原始日志:2024-01-15 10:30:45 [http-nio-8080-exec-10] ERROR com.example.Service - 数据库连接超时
# 目标:时间,线程,级别,类,消息
tail -n 1000 app.log |
grep -E "ERROR|WARN" |
sed -E 's/^([0-9-]+ [0-9:]+) \[([^]]+)\] ([A-Z]+) ([^ ]+) - (.*)/\1,\2,\3,\4,\5/' |
awk -F, '{print $1, $3, $5}'
3.5.3 慢查询 Top 10
# 从 MySQL 慢查询日志中找出最慢的 10 条
grep "Query_time:" slow.log |
awk '{print $3, $0}' |
sort -rn |
head -10 |
sed 's/^[0-9.]* //'
3.6 避坑指南
我总结的几个常见坑:
- 管道缓冲区:tail -f 管道给 grep 时,一定要加
--line-buffered,否则输出会延迟。 - 正则贪婪匹配:awk 和 sed 的正则默认是贪婪的,用
[^...]来限制范围。 - 大文件处理:超过 1GB 的日志,不要直接用
cat管道,用tail或sed -n分段处理。 - 编码问题:中文日志可能出现乱码,加
| iconv -f utf-8 -t gbk转换。 - 备份习惯:sed -i 之前,先
cp file file.bak。这个习惯救过我很多次。
好了,这四个工具就讲到这里。你想想看,是不是比想象中简单?tail 看、grep 筛、awk 切、sed 改,一条管道串起来,就能完成从原始日志到分析结果的完整链路。下次遇到线上问题,别急着上平台,先试试这四件套——很多时候,一条命令就能定位到根因。