Appearance
日志分析脚本
本节实现一个通用的日志分析脚本,能够从日志文件中提取关键信息、统计错误频率、分析访问趋势。
功能设计
- 统计各级别日志(ERROR/WARN/INFO)数量
- 提取高频错误信息
- 分析 Nginx 访问日志(IP、状态码、URL)
- 生成分析报告
Nginx 访问日志分析
bash
#!/usr/bin/env bash
#
# Nginx 访问日志分析脚本
# 用法:./analyze_nginx.sh <日志文件> [--top N]
#
set -euo pipefail
LOG_FILE="${1:-/var/log/nginx/access.log}"
TOP_N="${2:-10}"
[[ ! -f "$LOG_FILE" ]] && { echo "日志文件不存在:$LOG_FILE" >&2; exit 1; }
echo "========================================"
echo " Nginx 访问日志分析报告"
echo " 文件:$LOG_FILE"
echo " 时间:$(date '+%Y-%m-%d %H:%M:%S')"
echo "========================================"
# 总访问量
total=$(wc -l < "$LOG_FILE")
echo ""
echo "📊 总访问量:$total 条"
# 按状态码统计
echo ""
echo "📈 HTTP 状态码统计:"
awk '{print $9}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -20 | \
awk '{printf " %s: %d 次\n", $2, $1}'
# TOP IP
echo ""
echo "🌐 访问 TOP ${TOP_N} IP:"
awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -rn | head "$TOP_N" | \
awk '{printf " %-20s %d 次\n", $2, $1}'
# TOP URL
echo ""
echo "🔗 访问 TOP ${TOP_N} URL:"
awk '{print $7}' "$LOG_FILE" | sort | uniq -c | sort -rn | head "$TOP_N" | \
awk '{printf " %d 次 %s\n", $1, $2}'
# 错误请求(4xx/5xx)
echo ""
echo "❌ 错误请求统计(4xx/5xx):"
awk '$9 ~ /^[45]/' "$LOG_FILE" | awk '{print $9, $7}' | \
sort | uniq -c | sort -rn | head "$TOP_N" | \
awk '{printf " %d 次 [%s] %s\n", $1, $2, $3}'
# 按小时统计访问量
echo ""
echo "⏰ 按小时访问量分布:"
awk '{print $4}' "$LOG_FILE" | cut -d: -f2 | sort | uniq -c | \
awk '{printf " %02d:00 %d 次\n", $2, $1}' | sort
# 流量统计(字节)
echo ""
total_bytes=$(awk '{sum += $10} END {print sum}' "$LOG_FILE" 2>/dev/null || echo 0)
total_mb=$(( total_bytes / 1024 / 1024 ))
echo "📦 总流量:${total_mb} MB(${total_bytes} 字节)"通用错误日志分析
bash
#!/usr/bin/env bash
#
# 通用应用日志分析脚本
# 用法:./analyze_log.sh <日志文件> [--date 2025-01-01]
#
set -euo pipefail
LOG_FILE="${1:?请提供日志文件路径}"
FILTER_DATE="${2:-}"
# 如果指定日期,只分析该日期的日志
if [[ -n "$FILTER_DATE" ]]; then
log_content=$(grep "$FILTER_DATE" "$LOG_FILE")
else
log_content=$(cat "$LOG_FILE")
fi
echo "========================================"
echo " 日志分析报告"
echo " 文件:$LOG_FILE"
echo " 日期过滤:${FILTER_DATE:-全部}"
echo "========================================"
# 各级别统计
echo ""
echo "📊 日志级别统计:"
for level in ERROR WARN INFO DEBUG; do
count=$(echo "$log_content" | grep -c "\[$level\]" 2>/dev/null || echo 0)
printf " %-8s %d 条\n" "$level" "$count"
done
# 错误信息 TOP 10
echo ""
echo "❌ 高频错误 TOP 10:"
echo "$log_content" | grep "\[ERROR\]" | \
sed 's/.*\[ERROR\] //' | \
sort | uniq -c | sort -rn | head 10 | \
awk '{count=$1; $1=""; printf " %d 次 %s\n", count, $0}'
# 最新 20 条错误
echo ""
echo "🕐 最近 20 条错误:"
echo "$log_content" | grep "\[ERROR\]" | tail -20 | \
while IFS= read -r line; do
echo " $line"
done日志定期清理脚本
bash
#!/usr/bin/env bash
#
# 日志清理脚本
# 用法:./cleanup_logs.sh [--days 30] [--dir /var/log/myapp]
#
set -euo pipefail
KEEP_DAYS=30
LOG_DIR="/var/log/myapp"
DRY_RUN=false
while [[ $# -gt 0 ]]; do
case "$1" in
--days) KEEP_DAYS="$2"; shift 2 ;;
--dir) LOG_DIR="$2"; shift 2 ;;
--dry-run) DRY_RUN=true; shift ;;
*) echo "未知参数:$1"; exit 1 ;;
esac
done
echo "日志清理 | 目录:$LOG_DIR | 保留天数:$KEEP_DAYS"
$DRY_RUN && echo "(演练模式,不实际删除)"
total_size_before=$(du -sh "$LOG_DIR" 2>/dev/null | cut -f1)
# 查找并删除过期日志
find "$LOG_DIR" -name "*.log" -mtime "+$KEEP_DAYS" | while read -r file; do
size=$(du -h "$file" | cut -f1)
echo " 删除:$file($size)"
$DRY_RUN || rm -f "$file"
done
# 压缩 7 天前的日志
find "$LOG_DIR" -name "*.log" -mtime +7 -not -name "*.gz" | while read -r file; do
echo " 压缩:$file"
$DRY_RUN || gzip "$file"
done
total_size_after=$(du -sh "$LOG_DIR" 2>/dev/null | cut -f1)
echo "清理完成 | 清理前:$total_size_before | 清理后:$total_size_after"使用方法
bash
# 分析 nginx 日志
chmod +x analyze_nginx.sh
./analyze_nginx.sh /var/log/nginx/access.log --top 20
# 分析应用日志(只看今天)
./analyze_log.sh /var/log/myapp/app.log "$(date +%Y-%m-%d)"
# 定期清理(加入 cron)
# 0 2 * * * /opt/scripts/cleanup_logs.sh --days 30 --dir /var/log/myapp