如何编写日志轮转压缩脚本

wen 实用脚本 4

从零到自动化运维的完整指南

目录导读

  1. 为什么需要日志轮转压缩?
  2. 核心原理与常用工具
  3. 手把手编写Shell脚本
  4. 高级策略:按大小/时间轮转
  5. 压缩与删除策略优化
  6. 错误处理与日志审计
  7. 常见问题与解答
  8. 生产环境部署建议

为什么需要日志轮转压缩?

在实际运维中,日志文件若不加以管理,会迅速膨胀至数百GB甚至TB级别,导致磁盘空间耗尽、性能下降,甚至引发服务崩溃,编写日志轮转压缩脚本的目的是自动化切割、压缩、归档旧日志,同时清理超期文件。

如何编写日志轮转压缩脚本

核心价值

  • 节约存储成本(压缩率可达90%)
  • 保证日志可读性(单文件不超过合理大小)
  • 符合合规要求(保留周期可控)

典型场景:Web服务器(Nginx/Apache)、数据库(MySQL慢查询日志)、Java应用(Spring Boot)、系统日志(rsyslog)


核心原理与常用工具

实现逻辑

日志文件 → 监测(大小/时间)→ 重命名/切割 → 压缩 → 清理旧文件

工具对比

工具 优点 缺点 适用场景
logrotate Linux标准,配置简单 依赖系统cron,灵活性差 系统级日志
自定义Shell脚本 完全可控,可集成监控 需手工处理并发 复杂业务需求
Python脚本 逻辑清晰,跨平台 依赖Python环境 需要与API集成

推荐方案:用Shell脚本作为基础,结合cron定时任务,辅以gzippigz(并行压缩)实现高性能轮转。


手把手编写Shell脚本

第一步:基础版本

#!/bin/bash
LOG_DIR="/var/log/myapp"
LOG_FILE="app.log"
MAX_SIZE="100M"  # 轮转阈值
RETENTION_DAYS=30
# 获取当前日志大小
current_size=$(stat --printf="%s" "$LOG_DIR/$LOG_FILE" 2>/dev/null)
max_bytes=$(echo $MAX_SIZE | awk '{print $1 * 1024 * 1024}')
if [ $current_size -ge $max_bytes ]; then
    timestamp=$(date +"%Y%m%d_%H%M%S")
    mv "$LOG_DIR/$LOG_FILE" "$LOG_DIR/${LOG_FILE}.${timestamp}"
    gzip "$LOG_DIR/${LOG_FILE}.${timestamp}"  # 压缩
    touch "$LOG_DIR/$LOG_FILE"  # 创建新空文件
fi
# 清理过期日志
find "$LOG_DIR" -name "${LOG_FILE}.gz" -mtime +$RETENTION_DAYS -delete

改进点

  • 增加copytruncate模式(零停机)
  • 添加信号处理(防止进程崩溃)
  • 支持多日志文件配置

高级策略:按大小/时间轮转

混合策略脚本片段

# 按时间轮转(每日凌晨执行)
if [ "$1" == "daily" ]; then
    archive_name="${LOG_FILE}.$(date -d yesterday +%Y%m%d)"
    cp "$LOG_DIR/$LOG_FILE" "$LOG_DIR/$archive_name"
    :> "$LOG_DIR/$LOG_FILE"  # 清空原文件
    gzip -9 "$LOG_DIR/$archive_name"
fi
# 按大小轮转(实时监测)
if [ $(stat -c%s "$LOG_DIR/$LOG_FILE") -gt 104857600 ]; then  # 100MB
    #... 执行切割压缩
fi

关键要点

  • 使用重定向创建空文件(保留文件inode)
  • 压缩后删除原日志(避免重复处理)
  • 同时支持rotate_by_sizerotate_by_time

压缩与删除策略优化

高性能压缩技巧

# 使用pigz并行压缩(需安装)
command -v pigz &>/dev/null && COMPRESS_CMD="pigz -p $(nproc)" || COMPRESS_CMD="gzip"
$COMPRESS_CMD "$log_archive"
# 使用zstd实现更快压缩
zstd -q -3 --rm "$log_archive"  # 速度比gzip快5倍

删除策略

  • 相对时间find -mtime +60
  • 绝对数量:保留最后10个文件
  • 混合删除:对最近30天的文件按天保留,更早的按周保留
# 示例:保留最近30个压缩文件,仅保留最近一周的原始日志
ls -1t "$LOG_DIR"/*.gz | tail -n +31 | xargs rm -f
find "$LOG_DIR" -name "${LOG_FILE}*" ! -name "*.gz" -mtime +7 -delete

错误处理与日志审计

完整错误处理模板

#!/bin/bash
set -euo pipefail
ERROR_LOG="/var/log/rotate_error.log"
log_error() {
    echo "[$(date)] ERROR: $*" >> "$ERROR_LOG"
    # 可选:发送告警到企业微信/钉钉
}
rotate_log() {
    local src=$1 dest=$2
    if ! cp "$src" "$dest" 2>/dev/null; then
        log_error "Failed to copy $src to $dest"
        return 1
    fi
    # 压缩...
}
trap 'log_error "Script interrupted"' INT TERM

审核要点

  • 记录每次旋转的文件名、大小、耗时
  • 检查磁盘剩余空间(小于1%时停止轮转)
  • 防止重复执行(使用锁文件)
LOCK_FILE="/var/run/rotate.lock"
exec 200>"$LOCK_FILE"
flock -n 200 || exit 1  # 仅允许单实例运行

常见问题与解答

Q1:日志轮转后,应用进程仍然往旧文件写日志怎么办?
A:使用copytruncate模式(先复制后清空),或者向进程发送SIGUSR1信号让应用重新打开日志文件。
kill -USR1 $(cat /var/run/myapp.pid)

Q2:压缩后的日志无法直接grep搜索?
A:使用zcatpigz -dc配合管道实时解压,或配置find ... -exec zcat {} \; | grep ...,建议生成索引文件加速检索。

Q3:脚本执行时间过长导致重叠?
A:添加超时机制timeout 300 ./rotate.sh,并使用flock防止并发冲突。

Q4:如何处理日志文件包含中文字符或特殊符号?
A:在脚本中设置export LANG=C避免locale干扰,文件名中避免使用空格。

Q5:是否有现成的开源工具推荐?
A:除了logrotate,还可以考虑:

  • cronolog(按时间切分)
  • rotatelogs(Apache自带)
  • savelogs(Python生态)

生产环境部署建议

  1. 测试验证:先在测试环境运行./rotate.sh --dry-run模拟执行
  2. cron配置:编辑/etc/crontab添加每小时执行
    35 * * * * root /opt/scripts/log_rotate.sh >> /var/log/rotate_cron.log 2>&1
  3. 监控集成:加入Prometheus或者Zabbix监控指标
    • 磁盘使用率>80%告警
    • 轮转失败计数>3触发工单
  4. 版本控制:脚本纳入Git管理,变更需经过Code Review

通过以上方法,你可以构建一套稳定、可扩展的日志轮转压缩体系,确保服务器长期运行的日志管理自动化、无故障,如有更复杂的需求(如多数据中心、实时流式日志),可以结合ELK或Loki实现云端归档。

抱歉,评论功能暂时关闭!