如何写一个脚本定时清理日志(附完整代码与避坑策略)
目录导读
- 为什么需要定时清理日志?
- 日志清理脚本的底层逻辑拆解
- 实操:从零编写一个通用的日志清理脚本(Shell & Python)
- 定时任务配置详解(crontab vs systemd timer)
- 常见问题与解答(Q&A)
- 扩展思考:日志清理的自动化监控与安全加固
为什么需要定时清理日志?
日志是运维的“黑匣子”,但如果不加管控,它会迅速耗尽磁盘空间,导致服务宕机。
核心痛点:

- 日志文件可能以每天数GB的速度增长(尤其是Web服务器、数据库、容器编排平台)。
- 手动清理不可控,容易误删或漏删。
- 权限问题、路径错误、日志轮转不彻底等,都可能导致脚本失效。
写作目的:
本文不仅教你写一个能运行的脚本,还会解释为什么这样写,以及如何避免常见的坑,确保脚本在线上环境稳定运行。
日志清理脚本的底层逻辑拆解
一个优秀的清理脚本需要满足:
- 按时间清理:删除N天前的日志(最常用)。
- 按大小清理:当日志总大小超过阈值时,删除最旧的日志。
- 保留关键日志:保留最近N天的同时,保留最近一个月的归档日志。
- 幂等性:重复执行不会报错或误删。
- 日志记录:记录每次清理操作,方便回溯。
关键命令:
find /var/log -type f -name "*.log" -mtime +30 -deletedu -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:按顺序排查:
- 手动执行脚本,观察输出。
- 检查
find命令中的路径是否存在。 - 检查文件类型:
-name "*.log"是否覆盖了所有日志后缀(如.out、.txt)。 - 是否文件被其他进程占用?使用
lsof /var/log/myapp/xxx.log查看。 - 权限问题:确保执行用户对日志目录有写权限。
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使用,形成多层防护。
行动清单:
- 根据场景选择Shell或Python模板。
- 修改路径和保留天数。
- 用
crontab或systemd timer设置定时任务。 - 运行后检查
/var/log/log_cleaner.log确认效果。
立即动手,你的磁盘空间将告别“警报满屏”的烦恼。