如何写一个脚本定时清理日志

wen 实用脚本 1

如何写一个脚本定时清理日志(附完整代码与避坑策略)

目录导读

  1. 为什么需要定时清理日志?
  2. 日志清理脚本的底层逻辑拆解
  3. 实操:从零编写一个通用的日志清理脚本(Shell & Python)
  4. 定时任务配置详解(crontab vs systemd timer)
  5. 常见问题与解答(Q&A)
  6. 扩展思考:日志清理的自动化监控与安全加固

为什么需要定时清理日志?

日志是运维的“黑匣子”,但如果不加管控,它会迅速耗尽磁盘空间,导致服务宕机。
核心痛点

如何写一个脚本定时清理日志

  • 日志文件可能以每天数GB的速度增长(尤其是Web服务器、数据库、容器编排平台)。
  • 手动清理不可控,容易误删或漏删。
  • 权限问题、路径错误、日志轮转不彻底等,都可能导致脚本失效。

写作目的
本文不仅教你写一个能运行的脚本,还会解释为什么这样写,以及如何避免常见的坑,确保脚本在线上环境稳定运行。


日志清理脚本的底层逻辑拆解

一个优秀的清理脚本需要满足:

  1. 按时间清理:删除N天前的日志(最常用)。
  2. 按大小清理:当日志总大小超过阈值时,删除最旧的日志。
  3. 保留关键日志:保留最近N天的同时,保留最近一个月的归档日志。
  4. 幂等性:重复执行不会报错或误删。
  5. 日志记录:记录每次清理操作,方便回溯。

关键命令

  • find /var/log -type f -name "*.log" -mtime +30 -delete
  • du -sh /var/log 检查磁盘占用
  • 对于压缩文件(.gz.bz2),需区分是否保留原始文件

实操:从零编写一个通用的日志清理脚本

1 Shell脚本版本(推荐轻量级场景)

#!/bin/bash
# 功能:删除指定日志目录下,超过7天的日志文件
# 用法:chmod +x clean_logs.sh && ./clean_logs.sh
LOG_DIR="/var/log/myapp"
RETENTION_DAYS=7
LOG_FILE="/var/log/log_cleaner.log"
# 创建日志目录(如不存在)
mkdir -p "$(dirname "$LOG_FILE")"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始清理 $LOG_DIR" >> "$LOG_FILE"
# 查找并删除超过N天的文件
find "$LOG_DIR" -type f \( -name "*.log" -o -name "*.log.*" \) -mtime +$RETENTION_DAYS -delete 2>>"$LOG_FILE"
# 检查执行结果
if [ $? -eq 0 ]; then
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] 清理完成" >> "$LOG_FILE"
else
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] 清理失败:权限不足或路径错误" >> "$LOG_FILE"
    exit 1
fi
# 可选:删除超过30天的日志压缩包
find "$LOG_DIR" -type f -name "*.gz" -mtime +30 -delete 2>>"$LOG_FILE"

避坑指南

  • 永远不要用rm -rf+通配符,改用-delete参数,避免误删。
  • -mtime +7 表示7天前被修改的文件,不包含当天的。
  • 添加2>>重定向错误,方便排查权限问题。

2 Python脚本版本(适合复杂业务逻辑)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import time
import shutil
import logging
# 配置参数
LOG_DIR = "/var/log/myapp"
RETENTION_DAYS = 7
CURRENT_TIME = time.time()
SECONDS_PER_DAY = 86400
# 设置日志记录
logging.basicConfig(
    filename='/var/log/log_cleaner_py.log',
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
def clean_old_logs():
    if not os.path.exists(LOG_DIR):
        logging.error(f"目录 {LOG_DIR} 不存在")
        return False
    deleted_count = 0
    for root, dirs, files in os.walk(LOG_DIR):
        for file in files:
            # 只处理日志或压缩文件
            if not (file.endswith('.log') or file.endswith('.gz')):
                continue
            file_path = os.path.join(root, file)
            try:
                # 获取文件最后修改时间
                file_mtime = os.path.getmtime(file_path)
                if (CURRENT_TIME - file_mtime) > (RETENTION_DAYS * SECONDS_PER_DAY):
                    os.remove(file_path)
                    logging.info(f"已删除: {file_path}")
                    deleted_count += 1
            except PermissionError:
                logging.error(f"无权限删除: {file_path}")
            except Exception as e:
                logging.error(f"删除失败 {file_path}: {str(e)}")
    logging.info(f"本次清理完成,共删除 {deleted_count} 个文件")
    return True
if __name__ == "__main__":
    clean_old_logs()

优势

  • 可精确处理异常(如权限错误、文件被占用)。
  • 便于扩展:支持按文件大小、正则匹配、发送报警邮件。

定时任务配置详解

1 crontab(Linux标准方案)

# 编辑当前用户的crontab
crontab -e
# 每天凌晨3点执行清理
0 3 * * * /usr/local/bin/clean_logs.sh >> /var/log/cron_clean.log 2>&1

重要提示

  • 脚本中必须使用绝对路径(/usr/bin/find/bin/bash)。
  • 重定向日志路径,避免cron执行结果丢失。
  • 如果需要root权限,请将脚本写入/etc/cron.d/或使用sudo crontab -e

2 systemd timer(更现代的方案)

编写service文件 /etc/systemd/system/log-clean.service

[Unit]
Description=Log Cleaner Service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/clean_logs.sh
User=root

编写timer文件 /etc/systemd/system/log-clean.timer

[Unit]
Description=Run log cleaner daily
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target

启用并启动

systemctl daemon-reload
systemctl enable log-clean.timer
systemctl start log-clean.timer

常见问题与解答(Q&A)

Q1:脚本执行后,日志文件未删除,怎么办?

A:按顺序排查:

  1. 手动执行脚本,观察输出。
  2. 检查find命令中的路径是否存在。
  3. 检查文件类型:-name "*.log"是否覆盖了所有日志后缀(如.out.txt)。
  4. 是否文件被其他进程占用?使用lsof /var/log/myapp/xxx.log查看。
  5. 权限问题:确保执行用户对日志目录有写权限。

Q2:如何同时清理N天前的日志和大于500MB的日志?

A:组合条件,例如在Shell脚本中加入:

find "$LOG_DIR" -type f -name "*.log" \( -mtime +7 -o -size +500M \) -delete

注意:-o表示“或”,需用括号保证逻辑优先级。

Q3:如何保留最近一周的日志,但每天只保留最多10个文件?

A:Python方案更易实现:

import glob
files = sorted(glob.glob(f"{LOG_DIR}/*.log"), key=os.path.getmtime, reverse=True)
for f in files[10:]:  # 只保留最新的10个
    if (CURRENT_TIME - os.path.getmtime(f)) > 7*86400:
        os.remove(f)

Q4:日志清理后,磁盘空间没有立即释放?

A:某些文件被进程打开后,即使删除,空间也不会释放,直到进程重启。
解决方案:

  • 配合logrotate工具进行日志轮转(设置copytruncate实现不中断服务)。
  • 如果必须重启进程,使用kill -HUP <PID>发送挂起信号。

扩展思考:日志清理的自动化监控与安全加固

1 监控清理效果

增加脚本发送清理报告到企业微信/钉钉/Slack:

# 在脚本末尾添加
if [ $deleted_count -gt 0 ]; then
    curl -X POST "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=你的密钥" \
    -H "Content-Type: application/json" \
    -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"日志清理完成,删除$deleted_count个文件\"}}"
fi

2 安全加固

  • 不要直接删除根目录:脚本中强制绑定路径变量,且添加判断(如if [ "$LOG_DIR" = "/" ]; then exit; fi)。
  • 使用白名单:只清理指定后缀(.log.out.gz),避开数据库文件(.data.db)。
  • 版本控制:将脚本纳入Git仓库,每次修改需经过Code Review。

3 替代方案:logrotate(生产环境首选)

如果不想自己写脚本,Linux自带的logrotate已经成熟:

# 在 /etc/logrotate.d/ 下创建配置文件
/var/log/myapp/*.log {
    daily
    rotate 7           # 保留7天
    compress           # 开启压缩
    delaycompress      # 延迟一天压缩
    missingok          # 忽略缺失文件
    notifempty         # 空日志不轮转
    create 644 root root
    postrotate
        /bin/kill -HUP `cat /var/run/myapp.pid 2>/dev/null` 2>/dev/null || true
    endscript
}

但自己写脚本的优势

  • 更灵活(自定义清理逻辑、实时报警)。
  • 适合云原生环境(K8s中挂载卷的日志清理)。
  • 无需安装额外依赖。

本文从需求分析脚本编写定时部署故障排查,完整覆盖了“如何写一个脚本定时清理日志”的每个环节,无论你选择Shell还是Python,核心原则是:幂等、安全、易监控,建议将脚本配合logrotate使用,形成多层防护。

行动清单

  1. 根据场景选择Shell或Python模板。
  2. 修改路径和保留天数。
  3. crontabsystemd timer设置定时任务。
  4. 运行后检查/var/log/log_cleaner.log确认效果。

立即动手,你的磁盘空间将告别“警报满屏”的烦恼。

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