综合实战:日志分析
实战场景
假设你是一名运维工程师,需要分析 Nginx 访问日志,找出:
- 访问最多的 10 个 IP
- 访问最多的 10 个 URL
- HTTP 状态码分布
- 每小时访问量趋势
- 响应时间超过 1 秒的请求
日志格式
Nginx 默认访问日志格式:
$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time
示例日志行:
192.168.1.1 - - [15/Jan/2024:10:00:01 +0800] "GET /index.html HTTP/1.1" 200 1234 "-" "Mozilla/5.0" 0.023
192.168.1.2 - - [15/Jan/2024:10:00:02 +0800] "GET /api/users HTTP/1.1" 200 5678 "https://example.com" "Mozilla/5.0" 0.156
192.168.1.1 - - [15/Jan/2024:10:00:03 +0800] "POST /api/login HTTP/1.1" 401 89 "-" "curl/7.68.0" 0.045
192.168.1.3 - - [15/Jan/2024:10:00:04 +0800] "GET /images/logo.png HTTP/1.1" 200 45678 "https://example.com" "Mozilla/5.0" 0.234
字段说明:
| 字段 | 示例 | 含义 |
|---|---|---|
| $1 | 192.168.1.1 | 客户端 IP |
| $4 | [15/Jan/2024:10:00:01 | 访问时间(含前括号) |
| $6 | "GET | 请求方法(含引号) |
| $7 | /index.html | 请求 URL |
| $9 | 200 | HTTP 状态码 |
| $10 | 1234 | 响应大小(字节) |
| $12 | "-" | Referer |
| $13 | "Mozilla/5.0" | User-Agent |
| $14 | 0.023 | 请求处理时间(秒) |
创建测试日志
#!/bin/bash
# 生成测试日志
cat > generate_log.sh
#!/bin/bash
ips=("192.168.1.1" "192.168.1.2" "192.168.1.3" "10.0.0.1" "10.0.0.2")
urls=("/index.html" "/api/users" "/api/login" "/images/logo.png" "/css/style.css" "/js/app.js")
methods=("GET" "POST")
codes=(200 200 200 200 301 404 500)
for i in $(seq 1 1000); do
ip=${ips[$RANDOM % ${#ips[@]}]}
url=${urls[$RANDOM % ${#urls[@]}]}
method=${methods[$RANDOM % ${#methods[@]}]}
code=${codes[$RANDOM % ${#codes[@]}]}
size=$((RANDOM % 10000 + 100))
time=$(printf "%.3f" $(echo "scale=3; $RANDOM/1000" | bc -l))
hour=$(printf "%02d" $((RANDOM % 24)))
min=$(printf "%02d" $((RANDOM % 60)))
sec=$(printf "%02d" $((RANDOM % 60)))
echo "$ip - - [15/Jan/2024:$hour:$min:$sec +0800] \"$method $url HTTP/1.1\" $code $size \"-\" \"Mozilla/5.0\" $time"
done
# Ctrl+D
chmod +x generate_log.sh
./generate_log.sh > access.log
分析脚本
#!/bin/bash
# analyze.sh - Nginx 日志分析脚本
LOG_FILE="${1:-access.log}"
if [ ! -f "$LOG_FILE" ]; then
echo "Error: Log file '$LOG_FILE' not found"
echo "Usage: $0 <log_file>"
exit 1
fi
echo "========================================"
echo "Nginx 日志分析报告"
echo "日志文件: $LOG_FILE"
echo "总请求数: $(wc -l < "$LOG_FILE")"
echo "========================================"
echo
# 1. Top 10 IP
echo "【Top 10 访问 IP】"
awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -n 10 | awk '{printf "%6s %s\n", $1, $2}'
echo
# 2. Top 10 URL
echo "【Top 10 访问 URL】"
awk '{print $7}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -n 10 | awk '{printf "%6s %s\n", $1, $2}'
echo
# 3. HTTP 状态码分布
echo "【HTTP 状态码分布】"
awk '{print $9}' "$LOG_FILE" | sort | uniq -c | sort -rn | awk '{printf "%6s %s\n", $1, $2}'
echo
# 4. 每小时访问量
echo "【每小时访问量】"
awk '{print substr($4, 14, 2)}' "$LOG_FILE" | sort | uniq -c | sort -k2 | awk '{printf "%02d:00 %6s\n", $2, $1}'
echo
# 5. 慢请求(> 1秒)
echo "【慢请求统计(> 1秒)】"
awk '$14 > 1.0 {print $1, $7, $9, $14}' "$LOG_FILE" | head -n 10
echo "慢请求总数: $(awk '$14 > 1.0 {count++} END{print count+0}' "$LOG_FILE")"
echo
# 6. 平均响应时间
echo "【平均响应时间】"
awk '{sum+=$14; count++} END{printf "%.3f 秒\n", sum/count}' "$LOG_FILE"
echo
# 7. 4xx 和 5xx 错误统计
echo "【错误请求统计】"
echo "4xx 错误: $(awk '$9 ~ /^4/ {count++} END{print count+0}' "$LOG_FILE")"
echo "5xx 错误: $(awk '$9 ~ /^5/ {count++} END{print count+0}' "$LOG_FILE")"
echo
# 8. 响应大小统计
echo "【响应大小统计】"
awk '{sum+=$10; count++} END{printf "平均: %.0f 字节\n最大: %s 字节\n", sum/count, max}' "$LOG_FILE"
进阶:实时日志监控
#!/bin/bash
# monitor.sh - 实时监控日志
LOG_FILE="${1:-access.log}"
if [ ! -f "$LOG_FILE" ]; then
echo "Error: Log file '$LOG_FILE' not found"
exit 1
fi
echo "开始监控日志: $LOG_FILE"
echo "按 Ctrl+C 停止"
echo
# 使用 tail -f 实时追踪,配合 awk 分析
tail -f "$LOG_FILE" | awk '
{
ip = $1
url = $7
status = $9
size = $10
time = $14
# 统计 IP
ip_count[ip]++
# 统计 URL
url_count[url]++
# 统计状态码
status_count[status]++
# 统计总请求数
total++
# 每 10 条输出一次统计
if (total % 10 == 0) {
print "\n=== 实时统计 (总请求: " total ") ==="
print "Top 3 IP:"
for (i in ip_count) {
print " " ip_count[i] " " i
}
print "状态码分布:"
for (s in status_count) {
print " " status_count[s] " " s
}
}
# 检测异常(5xx 错误)
if (status ~ /^5/) {
print "[ALERT] 5xx error: " status " " url " " ip
}
# 检测慢请求
if (time > 1.0) {
print "[SLOW] " time "s " url " " ip
}
}'
本篇小结
- Nginx 日志字段:IP、时间、方法、URL、状态码、大小、Referer、User-Agent、处理时间
- 分析流程:提取字段 → 排序 → 统计 → 排序 → 取 Top N
- 黄金组合:
awk '{print $N}' | sort | uniq -c | sort -rn | head - 实时监控:
tail -f log | awk '...'实时处理新增日志 - 异常检测:
$9 ~ /^5/检测 5xx 错误,$14 > 1.0检测慢请求 - 完整脚本结构:参数检查 → 数据统计 → 格式化输出
动手实践
生成测试日志:
# 使用上面的 generate_log.sh 生成 1000 条日志 ./generate_log.sh > access.log wc -l access.log手动分析练习:
# Top 10 IP awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -n 10 # Top 10 URL awk '{print $7}' access.log | sort | uniq -c | sort -rn | head -n 10 # 状态码分布 awk '{print $9}' access.log | sort | uniq -c | sort -rn # 每小时访问量 awk '{print substr($4, 14, 2)}' access.log | sort | uniq -c | sort -k2 # 慢请求 awk '$14 > 1.0 {print $1, $7, $9, $14}' access.log | head -n 10运行分析脚本:
# 创建上面的 analyze.sh chmod +x analyze.sh ./analyze.sh access.log实时监控练习(需要持续产生日志):
# 终端 1:持续生成日志 while true; do echo "192.168.1.1 - - [$(date '+%d/%b/%Y:%H:%M:%S %z')] \"GET /api/test HTTP/1.1\" 200 123 \"-\" \"Mozilla/5.0\" $(printf '%.3f' $(echo "scale=3; $RANDOM/1000" | bc -l))" >> access.log sleep 1 done # 终端 2:实时监控 tail -f access.log | awk '$9 ~ /^5/ {print "[ERROR]", $0}'扩展练习:修改分析脚本,增加以下功能:
- 统计每个 IP 的平均响应时间
- 找出访问最频繁的 User-Agent
- 统计每个 URL 的错误率(4xx+5xx / 总请求)
思考:如果日志文件达到 10GB,
awk '{print $1}' | sort | uniq -c | sort -rn这个命令会遇到什么问题?如何优化大文件日志分析的性能?