怎样实现日志轮转脚本

wen 实用脚本 29

从零搭建自动化日志管理方案

📚 目录导读

  1. 为什么需要日志轮转?
  2. 日志轮转脚本的核心原理
  3. 手写一个完整的日志轮转脚本
  4. 使用系统工具 logrotate 实现轮转
  5. 常见问题与故障排查
  6. QA 问答精选

为什么需要日志轮转?

任何运行中的服务(如 Nginx、Java 应用、数据库)都会持续输出日志,如果不加控制,日志文件会无限增长,最终导致:

怎样实现日志轮转脚本

  • 磁盘空间耗尽:导致服务崩溃甚至系统宕机
  • 日志读取困难:巨型文件无法用 tailgrep 快速检索
  • 影响性能:持续的 I/O 写入会抢占系统资源

日志轮转(Log Rotation) 通过“切分+归档+清理”机制,让日志文件保持在一个可控的大小和时间范围内,每天生成一个新日志文件,将旧文件压缩,并删除 30 天前的文件。

日志轮转脚本的核心原理

一个标准的轮转脚本通常包含以下步骤:

  1. 重命名(Rotate):将当前日志文件如 app.log 重命名为 app.log.1,或按日期命名 app.log-2025-01-25
  2. 通知进程:向应用进程发送信号(如 kill -USR1 <PID>),让程序重新打开日志文件句柄
  3. 压缩归档:将旧日志文件用 gzipbzip2 压缩,节省空间
  4. 清理过期文件:删除超过保留天数的日志文件

关键点:必须正确处理文件句柄,否则服务会继续向旧文件写入,导致轮转失败。

手写一个完整的日志轮转脚本

以下是一个生产可用的 Shell 脚本示例,支持按日期轮转、压缩和自动清理:

#!/bin/bash
# 配置区
LOG_PATH="/var/log/myapp"
LOG_FILE="app.log"
RETENTION_DAYS=30
COMPRESS=true
# 获取当前日期
DATE=$(date +%Y%m%d)
PID_FILE="/var/run/myapp.pid"
# 1. 检查日志文件是否存在
if [ ! -f "${LOG_PATH}/${LOG_FILE}" ]; then
    echo "[$(date)] 日志文件不存在,跳过轮转"
    exit 0
fi
# 2. 重命名日志文件(按日期)
mv "${LOG_PATH}/${LOG_FILE}" "${LOG_PATH}/${LOG_FILE}-${DATE}"
# 3. 通知应用重新打开文件句柄
if [ -f "$PID_FILE" ]; then
    PID=$(cat "$PID_FILE")
    kill -USR1 "$PID" 2>/dev/null || echo "警告:无法通知 PID $PID"
fi
# 4. 压缩旧日志
if $COMPRESS; then
    find "${LOG_PATH}" -name "${LOG_FILE}-*" -not -name "*.gz" -exec gzip {} \;
fi
# 5. 删除过期文件(保留今天文件)
find "${LOG_PATH}" -name "${LOG_FILE}-*" -mtime +${RETENTION_DAYS} -delete
echo "[$(date)] 日志轮转完成:${LOG_FILE}-${DATE}"

使用方法:将脚本加入 crontab 每天凌晨执行:

0 0 * * * /usr/local/bin/rotate_logs.sh >> /var/log/rotation.log 2>&1

使用系统工具 logrotate 实现轮转

手动维护脚本虽然灵活,但 Linux 已有成熟工具 logrotate,对于大多数场景,建议优先使用:

典型配置(以 Nginx 为例)

创建配置文件 /etc/logrotate.d/nginx

/var/log/nginx/*.log {
    daily           # 每天轮转
    rotate 30       # 保留 30 个旧文件
    compress        # 压缩旧文件
    delaycompress   # 延迟一天再压缩(方便调试)
    missingok       # 日志文件不存在时忽略
    notifempty      # 空文件不轮转
    dateext         # 用日期命名归档文件
    postrotate      # 轮转后执行命令
        [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
    endscript
}

手动测试轮转

sudo logrotate -d /etc/logrotate.d/nginx   # 调试模式(模拟运行)
sudo logrotate -f /etc/logrotate.d/nginx   # 强制立即轮转

logrotate 的优势在于:

  • 统一管理所有服务的轮转策略
  • 内置大小阈值(size 100M)、时间轮转、错误处理
  • 支持多文件模式(如 *.log

常见问题与故障排查

问题 1:轮转后日志丢失

  • 原因:脚本移动文件后未通知进程,导致进程继续向旧文件写入
  • 解决:确保发送了正确的信号(如 SIGHUPUSR1),不同信号含义不同,需参考应用文档

问题 2:压缩后原文件未删除

  • 原因delaycompress 选项使压缩推迟一天
  • 解决:确认配置中 delaycompresscompress 是否组合正确

问题 3:crontab 脚本不执行

  • 排查步骤
    1. 检查 crontab 中路径是否写全(如 /usr/local/bin/rotate.sh
    2. 查看邮件记录:grep rotate /var/log/cronjournalctl -u crond
    3. 确保脚本有执行权限:chmod +x rotate.sh

QA 问答精选

Q1:日志轮转脚本中重命名和通知进程的顺序重要吗?
A:非常重要。必须先重命名文件,再通知进程,如果先通知进程,进程会立即打开新文件,但旧文件还未移动,可能导致两个进程都写同一个文件,正确流程是:移动旧文件 → 发送信号 → 进程创建新文件句柄 → 后续写入新文件。

Q2:如果应用不支持信号(如 kill -USR1)怎么办?
A:要么修改应用代码(添加信号处理),要么使用替代方案:

  • 重启应用(但会导致短暂中断)
  • 使用 copytruncate 方式(logrotate 选项,先复制日志为空,再 truncate 原文件,但可能丢失少量实时数据)

Q3:日志文件大小超过 10GB,轮转脚本执行很慢怎么办?
A:建议:

  • 对超大文件使用 truncate + cp 方式(注意数据一致性)
  • 调整轮转频率,如改为每 6 小时轮转一次
  • 使用异步压缩:gzip & 后台执行,但需防止多个压缩任务堆积

Q4:如何测试轮转脚本是否正确,而不实际影响生产日志?
A:创建测试脚本:

  1. 复制一份日志到测试目录
  2. 修改脚本中的 LOG_PATH 为测试路径
  3. 手动执行 ./rotate.sh
  4. 检查归档文件命名、内容完整性、进程是否收到信号(可通过 strace 监控)

通过本文的脚本示例和 logrotate 配置,你可以根据实际业务需求选择手动轮转或工具轮转,对于生产环境,建议采用 logrotate 并结合 systemd 服务管理,它比 crontab 更健壮,且能更好处理进程生命周期,如果日志量极大(如每天超过 100GB),还可以考虑引入日志中心化系统(如 ELK、Loki)来彻底减轻本地存储压力。

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