3. 日志分析工具:从命令行到 Python 的实战武器库
日志分析这事儿,工具选对了,事半功倍。我见过不少同事,对着几 GB 的日志文件,还在用鼠标滚轮翻,看得我直着急。其实,用好 grep、awk、sed 这三个老伙计,再加上 jq 和 Python 那几把刷子,大部分问题都能快速定位。
今天我就把这几样东西的「家常用法」给你捋一遍。嗯,都是我在线上环境里真刀真枪用过的。
3.1 命令行三剑客:grep / awk / sed
这三个工具,说白了就是文本处理的瑞士军刀。你不需要背所有参数,记住最常用的那几个就行。
3.4.1 grep —— 最快的过滤器
grep 就是用来「找东西」的。我个人习惯,上来先加个 -i 忽略大小写,再加个 --color 高亮匹配项,看着舒服。
核心用法:
grep "ERROR" app.log—— 找包含 ERROR 的行grep -v "INFO" app.log—— 排除 INFO 行grep -E "ERROR|FATAL" app.log—— 匹配多个关键词grep -A 5 -B 5 "NullPointer" app.log—— 显示匹配行前后各 5 行
我的习惯:排查问题时,我一般先用 grep -c "ERROR" 看看错误数量,心里有个底。如果文件太大,记得加 --line-buffered,配合 tail -f 实时过滤。
3.1.2 awk —— 列处理神器
awk 默认按空格或 Tab 切分每一行。我最常用的场景是:从日志里提取某个字段。
# 提取第 1 列和第 3 列
awk '{print $1, $3}' app.log
# 以逗号分隔,提取第 2 列
awk -F',' '{print $2}' csv.log
# 统计每个 IP 出现的次数
awk '{count[$1]++} END {for (ip in count) print ip, count[ip]}' access.log
我曾经用一行 awk 命令,从 2GB 的 Nginx 日志里,5 秒钟就统计出了 Top 10 的请求 IP。你想想看,如果用 Python 写脚本,光读取文件就得半天。
3.1.3 sed —— 流式编辑器
sed 用来「改东西」。线上排查时,我经常用它来替换敏感信息,或者清理日志里的乱码。
# 替换文本(全局)
sed 's/old_text/new_text/g' file.log
# 只替换第 3 行到第 10 行
sed '3,10s/ERROR/WARNING/g' file.log
# 删除空行
sed '/^$/d' file.log
# 直接修改文件(小心!)
sed -i 's/password=.*/password=***/g' config.log
避坑指南:我曾经在线上直接用了 sed -i 替换配置文件,结果替换错了,差点把生产环境搞挂。从那以后,我养成了一个习惯:先用 sed 不加 -i 预览结果,确认无误后再加 -i。或者用 sed -i.bak 先备份。
3.2 jq —— JSON 日志的救星
现在很多应用都输出 JSON 格式的日志。用 grep 去搜 JSON?那简直是灾难。jq 就是专门干这个的。
我刚开始用 jq 时,也觉得语法怪怪的。但用顺手后,真香。
# 格式化 JSON 输出
cat log.json | jq '.'
# 提取某个字段
cat log.json | jq '.message'
# 条件过滤
cat log.json | jq 'select(.level == "ERROR")'
# 提取嵌套字段
cat log.json | jq '.request.headers."User-Agent"'
实战场景:有一次,线上服务报 502,我拿到一段 JSON 日志,里面有个 response_time 字段。我直接用 jq 过滤出所有 response_time 大于 5000ms 的请求,然后按时间排序,很快就锁定了慢查询的 SQL。
jq 还有一个很实用的功能:jq -r 可以输出原始字符串,方便重定向到文件或管道给其他命令。
3.3 Python 日志分析库:re / collections / pandas
当命令行工具搞不定时,就该 Python 上场了。比如需要复杂的关联分析、统计报表,或者要处理多行日志。
3.3.1 re —— 正则表达式引擎
Python 的 re 模块,比 grep 的 -E 灵活得多。我一般用它来解析非结构化的日志。
import re
log_line = "2025-03-21 14:23:45 ERROR [main] NullPointerException at com.example.service.UserService.getUser()"
pattern = r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (\w+) \[(\w+)\] (.+)'
match = re.match(pattern, log_line)
if match:
timestamp = match.group(1)
level = match.group(2)
thread = match.group(3)
message = match.group(4)
print(f"时间: {timestamp}, 级别: {level}, 线程: {thread}, 消息: {message}")
我的习惯:写正则时,我一般先用 regex101.com 在线测试,确认无误再贴到代码里。另外,记得给正则加 r 前缀,避免转义地狱。
3.3.2 collections —— 计数器与默认字典
collections 里的 Counter 和 defaultdict,是我做日志统计时的左膀右臂。
from collections import Counter, defaultdict
# 统计错误类型
error_counts = Counter()
with open('app.log') as f:
for line in f:
if 'ERROR' in line:
# 假设错误类型在行尾
error_type = line.split()[-1]
error_counts[error_type] += 1
print(error_counts.most_common(10))
# 按时间分组统计
hourly_errors = defaultdict(int)
with open('app.log') as f:
for line in f:
if 'ERROR' in line:
hour = line[11:13] # 提取小时
hourly_errors[hour] += 1
for hour, count in sorted(hourly_errors.items()):
print(f"{hour}:00 -> {count} 次错误")
你想想看,用 Counter 一行代码就能搞定 Top N 统计,比写一堆 if-else 清爽多了。
3.3.3 pandas —— 结构化分析利器
当日志量达到百万级别时,pandas 的优势就体现出来了。它能把日志当成表格来处理。
import pandas as pd
# 读取日志文件,假设是 CSV 格式
df = pd.read_csv('app.log', sep='\t', names=['timestamp', 'level', 'thread', 'message'])
# 过滤出 ERROR 级别的日志
errors = df[df['level'] == 'ERROR']
# 按小时统计错误数量
errors['hour'] = pd.to_datetime(errors['timestamp']).dt.hour
hourly_counts = errors.groupby('hour').size()
# 找出错误最多的前 5 个小时
print(hourly_counts.sort_values(ascending=False).head(5))
实战案例:有一次,我需要分析一周的 API 网关日志,总共 5000 万行。用 pandas 的 groupby 和 agg 函数,我花了 10 分钟就写完了分析脚本,跑完只用了 3 分钟。如果用手工统计,估计得干到天亮。
3.4 知识体系总览
下面这张图,把今天讲的内容串起来了。你可以把它当成一个「工具选择指南」。
3.5 工具选择速查表
为了方便你快速决策,我整理了一个对照表。遇到问题时,直接查表就行。
| 场景 | 推荐工具 | 理由 |
|---|---|---|
| 快速搜索关键词 | grep | 速度最快,支持正则 |
| 提取某列数据 | awk | 默认按分隔符切分,灵活 |
| 批量替换文本 | sed | 流式处理,适合大文件 |
| 解析 JSON 日志 | jq | 原生支持 JSON 结构 |
| 复杂正则匹配 | Python re | 支持分组、回溯等高级特性 |
| 统计计数 | Counter | 一行代码搞定 Top N |
| 大规模数据分析 | pandas | 支持 groupby、聚合、可视化 |
我的建议:别想着把所有参数都背下来。记住每个工具最常用的 3-5 个用法,剩下的用到时再查。我干了十年运维,grep 的 -P 参数到现在还得翻手册。
好了,这一章的内容就这些。工具是死的,思路是活的。下次遇到日志问题,先想想:这个场景适合用哪个工具?然后动手试试。嗯,多练几次,你就能像我一样,看到日志就能条件反射地想到对应的命令了。