3、日志分析工具:grep、awk、sed在日志分析中的应用、日志分析脚本编写入门
日志分析,说白了就是在一堆看似杂乱无章的文本里,找到你想要的线索。我刚开始接触系统运维那会儿,面对动辄几个G的日志文件,真是头皮发麻。后来慢慢摸清了门道,发现只要用好三个工具——grep、awk、sed,再配合一点脚本思维,大部分问题都能迎刃而解。
嗯,咱们今天就聊聊这三个老伙计,以及怎么把它们串起来写点实用的分析脚本。
3.1 grep:最快的文本搜索器
grep 是我用得最频繁的工具,没有之一。它的核心能力就一个字:找。从海量日志里捞出你关心的那几行,是它的看家本领。
我个人习惯,排查问题时第一反应就是 grep。比如系统报了个 OutOfMemoryError,我直接敲:
grep "OutOfMemoryError" /var/log/app.log
这就把包含错误关键字的行全拎出来了。但光这样还不够,你想想看,错误发生前后往往有上下文信息。这时候 -C 参数就派上用场了:
grep -C 5 "OutOfMemoryError" /var/log/app.log
这个命令会把匹配行以及它前后各5行都打印出来。我在项目中遇到过好几次,光看错误行根本看不出原因,但看了上下文才发现是某个连接池配置出了问题。
grep -c 统计一下匹配行数,心里有个数再决定要不要全量输出。我曾经直接对一个5GB的日志文件执行 grep,结果终端卡了半分钟……嗯,从那以后我就学乖了。
另外,grep 支持正则表达式,这是它的杀手锏。比如你想找所有IP地址:
grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' /var/log/access.log
-E 启用扩展正则,-o 只输出匹配的部分。这样你得到的就是一列干净的IP地址,方便后续处理。
3.2 awk:列处理与格式化输出
grep 擅长找行,但找到行之后,往往需要提取其中的某些字段。这时候 awk 就登场了。它默认以空格或制表符分割每一行,然后通过 $1、$2 来引用第1列、第2列。
举个例子,Apache 的访问日志格式通常是这样的:
192.168.1.1 - - [10/Oct/2023:13:55:36 +0000] "GET /index.html HTTP/1.1" 200 2326
我想提取出所有返回状态码为500的请求的IP地址和请求路径,可以这么写:
awk '$9 == 500 {print $1, $7}' /var/log/apache2/access.log
这里 $9 是状态码(第9列),$1 是IP,$7 是请求路径。一行命令,就把关键信息筛出来了。
我记得有一次线上服务间歇性超时,排查了半天没头绪。后来我用 awk 统计了每个IP的请求次数:
awk '{count[$1]++} END {for (ip in count) print ip, count[ip]}' /var/log/nginx/access.log | sort -k2 -rn | head -10
结果发现有一个IP的请求量是其他IP的几十倍,明显是爬虫在捣乱。嗯,找到元凶后直接封掉,问题就解决了。
3.3 sed:流式文本编辑器
sed 的名字叫流编辑器,说白了就是批量替换和修改文本。它不会直接修改原文件(除非你用 -i 参数),而是把处理后的结果输出到标准输出。
最常见的用法是替换字符串。比如日志里有个旧的IP地址 10.0.0.1,现在要改成 10.0.0.2:
sed 's/10\.0\.0\.1/10.0.0.2/g' old.log > new.log
注意,正则表达式里的点号 . 需要转义成 \.,否则它会匹配任意字符。这个坑我踩过,有一次替换IP地址,结果把 10.0.0.11 也误改了……
除了替换,sed 还能删除特定行。比如你想去掉日志里所有以 # 开头的注释行:
sed '/^#/d' config.log
或者只打印第10到第20行:
sed -n '10,20p' /var/log/syslog
-n 参数表示不自动打印,只有明确用 p 命令的行才会输出。这个组合在查看日志中间部分时非常实用。
sed -i 直接修改原文件时,建议先备份。我曾经在线上环境执行 sed -i 's/old/new/g' /etc/nginx/nginx.conf,结果正则写错了,把配置文件改坏了……还好有备份,不然就等着挨骂吧。
3.4 日志分析脚本编写入门
单个工具能解决简单问题,但实际工作中,我们往往需要组合使用。写一个简单的 Shell 脚本,把 grep、awk、sed 串起来,就能实现自动化分析。
下面这个脚本,是我以前处理 Nginx 访问日志时写的。它的功能是:统计过去一小时内,返回 5xx 错误的 Top 10 URL。
#!/bin/bash
# 文件名:analyze_5xx.sh
# 用途:统计过去1小时内返回5xx错误的Top10 URL
LOG_FILE="/var/log/nginx/access.log"
CURRENT_HOUR=$(date +"%d/%b/%Y:%H")
TEMP_FILE="/tmp/5xx_errors.txt"
# 第一步:用grep筛选出当前小时的5xx错误行
grep "$CURRENT_HOUR" "$LOG_FILE" | grep -E 'HTTP/1\.[01]" 5[0-9]{2}' > "$TEMP_FILE"
# 第二步:用awk提取URL并统计出现次数
awk '{
# 提取请求路径(第7列)
url = $7
# 用关联数组计数
count[url]++
}
END {
# 输出统计结果
for (u in count) {
print count[u], u
}
}' "$TEMP_FILE" | sort -k1 -rn | head -10
# 清理临时文件
rm -f "$TEMP_FILE"
这个脚本的思路很清晰:
- 筛选:用
grep先按时间范围过滤,再按状态码过滤。 - 提取与统计:用
awk提取URL字段,并用关联数组计数。 - 排序与输出:用
sort按次数降序排列,取前10条。
我在项目中遇到过类似场景,当时线上某个接口突然大量报500错误,我手动跑了一遍这个脚本,发现是某个新上线的API路径被频繁调用,而那个接口存在内存泄漏。嗯,找到问题后,回滚代码就解决了。
最后,我想说一句:这三个工具虽然老,但真的不过时。你想想看,无论日志格式怎么变,核心需求无非就是找、筛、改、统。把这四个动作练熟了,大部分日志分析场景你都能应付。
下一章,咱们聊聊更高级的日志分析框架,比如 ELK 和 Loki。但在此之前,我建议你先动手写几个脚本练练手。毕竟,纸上得来终觉浅嘛。