如何用脚本自动分割日志文件?

wen 实用脚本 1

如何用脚本自动分割日志文件?一篇指南解决日志管理难题

目录导读

  • 日志分割的必要性:为什么不能忽视这个细节?
  • 核心方法:三种主流脚本方案对比(Shell/Python/Logrotate)
  • 实战演示:从零编写一个自动日志分割脚本
  • 常见问题解答:分割后日志丢失?时间戳错误?一次说清
  • 最佳实践:结合定时任务与监控告警

日志分割的必要性:为什么不能忽视这个细节?

在服务器运维中,日志文件会无节制增长,一个每天产生500MB日志的Java应用,一个月后单个文件可能超过15GB,这会导致:

如何用脚本自动分割日志文件?

  • 磁盘空间耗尽:日志填满分区,服务突然崩溃
  • 检索效率极低:用grep搜索15GB文件需要数十分钟
  • 备份困难:单文件过大会导致传输中断或备份失败

通过脚本自动分割日志,你可以:

  1. 按时间(每天/每小时)切割
  2. 按大小(100MB/1GB)切割
  3. 保留最近N份,自动删除旧日志

核心方法:三种主流脚本方案对比

以下是经过搜索引擎去伪存真后,业界最常用的三种方案:

Shell + logrotate(Linux标配,推荐)

logrotate 是系统自带工具,一条配置即可生效。
优点:成熟稳定、支持压缩、日期后缀
缺点:需系统权限,Windows不可用
配置示例/etc/logrotate.d/myapp):

/var/log/myapp/*.log {
    daily
    rotate 30
    compress
    dateext
    postrotate
        systemctl restart myapp 2>/dev/null || true
    endscript
}

Python脚本(灵活可控)

适合需要自定义逻辑的场景,如按业务关键字分割。
核心代码

import os, shutil, datetime
log_file = '/var/log/app.log'
max_size = 100 * 1024 * 1024  # 100MB
backup_dir = '/backup/logs'
def rotate_log():
    if os.path.getsize(log_file) > max_size:
        timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
        shutil.move(log_file, f"{backup_dir}/app_{timestamp}.log")
        open(log_file, 'w').close()
# 可结合cron每小时执行

简易Shell分割脚本(无需额外依赖)

#!/bin/bash
LOG_PATH="/var/log/nginx/access.log"
SIZE=500000000  # 500MB
if [ -f "$LOG_PATH" ] && [ $(stat -c%s "$LOG_PATH") -gt $SIZE ]; then
    mv "$LOG_PATH" "${LOG_PATH}_$(date +%Y%m%d_%H%M%S)"
    # 通知进程重新打开文件(如Nginx:kill -USR1 `cat /var/run/nginx.pid`)
    kill -USR1 $(cat /var/run/nginx.pid)
fi

实战演示:从零编写一个自动日志分割脚本

目标:每周一凌晨3点分割,保留最近4周的日志,文件名带日期后缀。

步骤1:创建脚本文件

vim /opt/scripts/auto_split_logs.sh

步骤2:写入以下代码(已结合搜索引擎常见错误修正)

#!/bin/bash
LOG_DIR="/var/log/myproject"
RETENTION_WEEKS=4
DATE_SUFFIX=$(date +%Y%m%d)
for logfile in ${LOG_DIR}/*.log; do
    if [ -f "$logfile" ]; then
        # 防止重复分割(如果昨天已经分割过则跳过)
        if [[ ! "$logfile" =~ ${DATE_SUFFIX} ]]; then
            mv "$logfile" "${logfile}_${DATE_SUFFIX}"
            # 通知服务重新打开日志(此处假设是tomcat)
            kill -USR1 $(cat /var/run/tomcat.pid 2>/dev/null) 2>/dev/null
        fi
    fi
done
# 删除超过4周的旧日志
find ${LOG_DIR} -name "*.log_*" -type f -mtime +$((7*RETENTION_WEEKS)) -delete

步骤3:添加定时任务

crontab -e
# 添加:每周一3点执行
0 3 * * 1 /bin/bash /opt/scripts/auto_split_logs.sh

关键验证点

  • 执行后检查分割效果:ls -lh /var/log/myproject/
  • 确认旧日志被删除:find /var/log/myproject/ -name "*.log_*" -mtime +28
  • 服务进程是否重新打开日志:查看/var/log/messages有无错误

常见问题解答

Q1:分割后新日志不写入?
A:常见原因是进程仍持有旧文件句柄,需要向进程发送USR1或HUP信号重启日志接口,例如Nginx:kill -USR1 $(cat /var/run/nginx.pid)

Q2:按大小分割时,文件恰好落在边界怎么办?
A:建议使用大于上限的阈值,比如目标100MB,设置阈值为120MB,分割后生成的文件略小于100MB。

Q3:Windows环境如何实现?
A:可用PowerShell脚本,配合Task Scheduler,核心命令:Move-Item C:\logs\app.log "C:\logs\app_$(Get-Date -Format yyyyMMdd).log",然后重启服务进程。

Q4:日志内容被截断或丢失?
A:确保使用mv而非cp+rm,因为mv是原子操作,同时分割前确认应用已配置日志立即刷新(如flush),避免缓冲区滞留。


最佳实践:结合定时任务与监控告警

  1. 使用logrotate原生功能:它已集成压缩(compress)、邮件告警(mail)、旧日志删除(rotate)。
  2. 监控分割失败:在脚本中加上退出码检查,通过Prometheus或Zabbix发告警。
    if [ $? -ne 0 ]; then
        echo "分割失败:$(date)" | mail -s "[ALERT]日志分割异常" admin@example.com
    fi
  3. 测试环境先行:先在测试服务器跑一个月,观察磁盘I/O和进程稳定性。
  4. 留空日志文件:分割后务必创建空文件(touch> file),否则某些服务(如Syslog)会报错。

通过本文的脚本方案,你可以快速实现自动化日志分割,避免因单文件过大导致的运维事故,建议优先采用系统自带的logrotate,只有需要复杂逻辑时才自写脚本,如果在生产环境部署,请先在测试环境验证信号处理和文件句柄释放逻辑。

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