日志分析:从日志文件提取统计 | Log Analysis: Extracting Statistics from Log Files
章节概述
核心理念:日志是系统的”黑匣子”,掌握日志分析能力意味着你能从海量数据中快速定位问题、发现规律。Bash 提供了 awk、grep、sort、uniq 等工具链,可以像 C 语言处理结构化数据一样高效地分析日志。
第1节:awk 统计日志
1.1 awk 基础统计
awk 是日志分析的核心工具,它的语法与 C 语言有相似之处——都支持条件判断、循环和格式化输出。
# 统计日志总行数
awk 'END {print NR}' access.log
# 提取特定字段(以空格分隔)
awk '{print $1, $7}' access.log
# 按条件过滤(HTTP 500 错误)
awk '$9 >= 500' access.log
# 求和计算(统计响应时间总和)
awk '{sum += $NF} END {print "Total:", sum}' access.log
# 计算平均值
awk '{sum += $NF; count++} END {print "Average:", sum/count}' access.log1.2 awk 按状态码分类统计
# 统计每个 HTTP 状态码的出现次数
awk '{print $9}' access.log | sort | uniq -c | sort -rn
# 更详细的统计(状态码 + 路径)
awk '{print $9, $7}' access.log | sort | uniq -c | sort -rn | head -20
# awk 内部分组统计(无需外部 sort/uniq)
awk '{count[$9]++} END {for (code in count) print code, count[code]}' access.log
# 按小时统计请求量
awk '{split($4,a,":"); hour=a[2]; count[hour]++} END {for (h in count) print h":00", count[h]}' access.log | sort| awk 变量 | 说明 |
|---|---|
NR | 当前行号(累计) |
NF | 当前行字段数 |
$0 | 整行内容 |
$1 ~ $N | 第 N 个字段 |
$9 | HTTP 状态码(常见位置) |
$7 | 请求路径(常见位置) |
$NF | 最后一个字段(常为响应时间) |
第2节:grep 过滤
2.1 基础过滤
# 查找包含 ERROR 的行
grep "ERROR" app.log
# 忽略大小写
grep -i "error" app.log
# 反向匹配(排除 DEBUG)
grep -v "DEBUG" app.log
# 显示匹配行号
grep -n "timeout" app.log
# 使用正则表达式
grep -E "^[0-9]{4}-[0-9]{2}-[0-9]{2}" app.log
# 显示匹配行的前后各 3 行
grep -B3 -A3 "critical error" app.log2.2 高级过滤技巧
# 递归搜索目录下所有日志
grep -rn "ERROR" /var/log/myapp/
# 只搜索 .log 文件
grep -rn "ERROR" --include="*.log" /var/log/
# 多模式匹配
grep -E "ERROR|WARN|CRITICAL" app.log
# 二进制文件中搜索(如压缩日志)
zgrep "ERROR" app.log.gz
# 统计匹配行数
grep -c "404" access.log第3节:sort + uniq 计数
3.1 排序与去重
# 基础去重计数
sort access.log | uniq -c
# 按出现次数降序排列
sort access.log | uniq -c | sort -rn
# 只显示重复行
sort access.log | uniq -d
# 只显示唯一行(不重复的)
sort access.log | uniq -u
# 忽略前 N 个字段进行排序(如忽略时间戳)
sort -k2 access.log | uniq -c3.2 实用统计组合
# 统计最常访问的 IP
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
# 统计最常访问的 URL
awk '{print $7}' access.log | sort | uniq -c | sort -rn | head -10
# 统计每个 IP 的唯一请求数
awk '{print $1}' access.log | sort -u | wc -l
# 按日期统计访问量
awk '{print $4}' access.log | cut -d: -f1 | sort | uniq -c第4节:时间范围提取
4.1 按时间戳过滤
# 提取特定日期的日志(Apache/Nginx 格式)
awk '/22\/Aug\/2026/' access.log
# 提取时间范围(10:00 到 11:00)
awk -F'[: ]' '$5 >= "10" && $5 <= "11"' access.log
# 提取最近 1 小时的日志(假设日志有时间戳)
awk -v time="$(date -d '1 hour ago' '+%H:%M')" '$4 >= "["time' access.log
# 使用 grep 按日期过滤
grep "2026-08-22" app.log
# 提取特定日期范围(多天)
grep -E "2026-08-(20|21|22)" app.log4.2 时间格式转换与计算
# 将日志时间戳转换为 epoch
awk '{print $4}' access.log | tr -d '[]' | xargs -I{} date -d "{}" +%s
# 计算两个时间戳之间的差值
start_epoch=$(date -d "2026-08-22 10:00:00" +%s)
end_epoch=$(date -d "2026-08-22 11:00:00" +%s)
echo "Duration: $((end_epoch - start_epoch)) seconds"第5节:错误率统计
5.1 完整错误率分析
#!/bin/bash
# 日志错误率统计脚本
LOG_FILE="${1:-access.log}"
# 总请求数
total=$(wc -l < "$LOG_FILE")
# 4xx 错误数
client_errors=$(awk '$9 >= 400 && $9 < 500' "$LOG_FILE" | wc -l)
# 5xx 错误数
server_errors=$(awk '$9 >= 500 && $9 < 600' "$LOG_FILE" | wc -l)
# 成功请求数(2xx + 3xx)
success=$(awk '$9 >= 200 && $9 < 400' "$LOG_FILE" | wc -l)
# 计算错误率
client_rate=$(echo "scale=2; $client_errors * 100 / $total" | bc)
server_rate=$(echo "scale=2; $server_errors * 100 / $total" | bc)
echo "=== 日志统计报告 ==="
echo "总请求数: $total"
echo "成功请求: $success"
echo "4xx 错误: $client_errors ($client_rate%)"
echo "5xx 错误: $server_errors ($server_rate%)"
echo "错误率: $(echo "scale=2; ($client_errors + $server_errors) * 100 / $total" | bc)%"5.2 按时间段统计错误率
# 按小时统计错误率
awk '{
split($4, a, ":");
hour = a[2];
total[hour]++;
if ($9 >= 400) errors[hour]++;
}
END {
for (h in total) {
rate = errors[h] * 100 / total[h];
printf "%s:00 - Total: %d, Errors: %d, Rate: %.2f%%\n",
h, total[h], errors[h]+0, rate;
}
}' access.log | sort第6节:综合实战案例
6.1 Apache 访问日志完整分析
#!/bin/bash
# Apache 日志分析一键脚本
LOG="${1:-/var/log/apache2/access.log}"
echo "========= Apache 日志分析 ========="
echo ""
echo "--- Top 10 访问 IP ---"
awk '{print $1}' "$LOG" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- Top 10 请求路径 ---"
awk '{print $7}' "$LOG" | sort | uniq -c | sort -rn | head -10
echo ""
echo "--- HTTP 状态码分布 ---"
awk '{print $9}' "$LOG" | sort | uniq -c | sort -rn
echo ""
echo "--- 每小时请求量 ---"
awk '{split($4,a,":"); print a[2]":00"}' "$LOG" | sort | uniq -c | sort -k2
echo ""
echo "--- 平均响应时间 ---"
awk '{sum += $NF; count++} END {printf "%.3f ms\n", sum/count}' "$LOG"6.2 使用 awk 生成 CSV 报告
# 从日志生成 CSV
awk '{
split($4, a, "[/:]");
date = a[1] "-" a[2] "-" a[3];
printf "%s,%s,%s,%s,%s\n", $1, date, $7, $9, $NF
}' access.log > report.csv
# 添加 CSV 头
sed -i '1i IP,Date,Path,StatusCode,ResponseTime' report.csv