生产环境随着业务的持续执行,nginx日志【access.log】或redis日志【redis.log】文件会越来越庞大,处理不及时会导致系统告急。本文分享如何用Linux命令自动切割日志和清理旧日志。
1 nginx日志切割命令
重点【其余的可以照抄】:
NGINX_PID_FILE="/run/nginx.pid" # Nginx 的 PID 文件路径
LOGS_DIR="/var/log/nginx/" # 日志存储目录
LOG_NAMES=("access.log" "error.log") # 要切割的日志文件名(可配置多个)
SAVE_DAYS=20 # 日志保留天数(超过自动删除)
COMPRESS_LEVEL=9 # 压缩级别(0-9,9最高)
说明:
nginx支持 USR1 信号,切割完可重新打开日志文件,相当于删除日志文件并重新加载nginx,生成新的access.log。
以下是 nginx_logrotate.sh 文件内容
#!/bin/bash
# Nginx 日志切割脚本
# 功能:按日期切割日志、压缩旧日志、自动清理过期文件
# 使用方法:添加到 cron 每日执行(例如每天 00:00 执行)
# 配置区(按需修改)===============================================
NGINX_PID_FILE="/run/nginx.pid" # Nginx 的 PID 文件路径
LOGS_DIR="/var/log/nginx/" # 日志存储目录
LOG_NAMES=("access.log" "error.log") # 要切割的日志文件名(可配置多个)
SAVE_DAYS=20 # 日志保留天数(超过自动删除)
COMPRESS_LEVEL=9 # 压缩级别(0-9,9最高)
# ================================================================
# 生成日期字符串(示例:20240610_0200)
DATE_SUFFIX=$(date +%Y%m%d_%H%M)
# 循环处理每个需要切割的日志
for LOG_NAME in "${LOG_NAMES[@]}"; do
LOG_PATH="${LOGS_DIR}/${LOG_NAME}"
# 检查源日志文件是否存在
if [ ! -f "${LOG_PATH}" ]; then
echo "[WARNING][$(date "+%Y-%m-%d %H:%M:%S")] 日志文件不存在: ${LOG_PATH}" >> "${LOGS_DIR}/logrotate.log"
continue
fi
# 重命名日志文件(保留 inode)
NEW_LOG_NAME="${LOG_NAME%.*}_${DATE_SUFFIX}.log"
mv -f "${LOG_PATH}" "${LOGS_DIR}/${NEW_LOG_NAME}"
# 压缩日志文件(后台执行不阻塞)
gzip -${COMPRESS_LEVEL} "${LOGS_DIR}/${NEW_LOG_NAME}" &
done
# 向 Nginx 主进程发送 USR1 信号,重新打开日志文件
if [ -f "${NGINX_PID_FILE}" ]; then
kill -USR1 $(cat "${NGINX_PID_FILE}")
echo "[INFO][$(date "+%Y-%m-%d %H:%M:%S")] Nginx 日志已重新打开 (PID: $(cat ${NGINX_PID_FILE}))" >> "${LOGS_DIR}/logrotate.log"
else
echo "[ERROR][$(date "+%Y-%m-%d %H:%M:%S")] Nginx PID 文件不存在: ${NGINX_PID_FILE}" >> "${LOGS_DIR}/logrotate.log"
exit 1
fi
# 清理过期日志文件(按修改时间)
find "${LOGS_DIR}" -name "*.log.gz" -mtime +${SAVE_DAYS} -exec rm -f {} \;
echo "[INFO][$(date "+%Y-%m-%d %H:%M:%S")] 已清理 ${SAVE_DAYS} 天前的历史日志" >> "${LOGS_DIR}/logrotate.log"
# 记录操作日志
echo "[$(date "+%Y-%m-%d %H:%M:%S")] 日志切割完成" >> "${LOGS_DIR}/logrotate.log"
2 redis日志切割命令
重点【其余的可以照抄】:
REDIS_CLI="/usr/local/redis6003/bin/redis-cli" #请确认你的 redis-cli 路径
REDIS_PORT="6002" # Redis 端口
REDIS_AUTH="higehrzjm-redis-001" # 如果有密码,填写 -a password,否则留空
PID_FILE="/opt/redis6002/redis.pid" #pid文件路径
LOGS_DIR="/opt/redis6002/" # 日志目录
LOG_NAMES=("redis.log") #日志文件
SAVE_DAYS=90 # 日志保留天数(超过自动删除)
COMPRESS_LEVEL=9 # 压缩级别(0-9,9最高)
说明:
redis并不支持 USR1 信号,切割时不能像nginx一样重新打开日志文件,只能采用切割完清空日志文件。
以下是 redis_logrotate.sh 文件内容
#!/bin/bash
# redis 日志切割脚本 (修正版)
# 原理:复制当前日志 -> 清空当前日志(Truncate) -> 压缩旧日志
# 注意:Redis 不支持 USR1 信号,必须使用 copytruncate 方式
# 配置区 ===============================================================
REDIS_CLI="/usr/local/redis6003/bin/redis-cli" # 请确认你的 redis-cli 路径,可以用 which redis-cli 查看
REDIS_PORT="6002" # Redis 端口
REDIS_AUTH="member-redis-001" # 如果有密码,填写 -a password,否则留空
PID_FILE="/opt/redis6002/redis.pid" #pid文件路径
LOGS_DIR="/opt/redis6002/" # 日志目录
LOG_NAMES=("redis.log") #日志文件
SAVE_DAYS=90 # 日志保留天数(超过自动删除)
COMPRESS_LEVEL=9 # 压缩级别(0-9,9最高)
# =========================================================================
DATE_SUFFIX=$(date +%Y%m%d_%H%M)
# 循环处理每个需要切割的日志
for LOG_NAME in "${LOG_NAMES[@]}"; do
LOG_PATH="${LOGS_DIR}/${LOG_NAME}"
# 1. 检查源日志文件是否存在且不为空
if [ ! -f "${LOG_PATH}" ]; then
echo "[WARNING][$(date "+%Y-%m-%d %H:%M:%S")] 日志文件不存在: ${LOG_PATH}" >> "${LOGS_DIR}/logrotate.log"
continue
fi
# 如果文件为空,跳过切割
if [ ! -s "${LOG_PATH}" ]; then
continue
fi
NEW_LOG_NAME="${LOG_NAME%.*}_${DATE_SUFFIX}.log"
NEW_LOG_PATH="${LOGS_DIR}/${NEW_LOG_NAME}"
# 2. 复制日志文件 (cp)
cp "${LOG_PATH}" "${NEW_LOG_PATH}"
# 3. 清空原日志文件 (truncate) - 关键步骤!
# 这会让 Redis 继续向原文件写入,而不会丢失句柄
cat /dev/null > "${LOG_PATH}"
# 4. 压缩旧日志 (后台执行)
gzip -${COMPRESS_LEVEL} "${NEW_LOG_PATH}" &
echo "[INFO][$(date "+%Y-%m-%d %H:%M:%S")] 日志已切割: ${LOG_NAME}" >> "${LOGS_DIR}/logrotate.log"
done
# 5. (可选) 尝试通知 Redis 关闭再打开日志
# 虽然上面的 cp+truncate 方法已经足够,但为了保险,可以尝试用 redis-cli 刷新日志
# 如果你的环境没有 redis-cli,可以注释掉下面这段
if command -v ${REDIS_CLI} &> /dev/null; then
AUTH_ARGS=""
if [ -n "${REDIS_AUTH}" ]; then
AUTH_ARGS="-a ${REDIS_AUTH}"
fi
# 发送 LOG CLOSE 然后 LOG OPEN (部分版本支持) 或者仅仅依赖 truncate 即可
# 这里主要为了记录状态,不做强制 kill
${REDIS_CLI} -p ${REDIS_PORT} ${AUTH_ARGS} PING > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "[INFO][$(date "+%Y-%m-%d %H:%M:%S")] Redis 服务运行正常" >> "${LOGS_DIR}/logrotate.log"
fi
fi
# 6. 清理过期日志文件
find "${LOGS_DIR}" -name "*.log.gz" -mtime +${SAVE_DAYS} -exec rm -f {} \;
echo "[INFO][$(date "+%Y-%m-%d %H:%M:%S")] 已清理 ${SAVE_DAYS} 天前的历史日志" >> "${LOGS_DIR}/logrotate.log"
echo "[$(date "+%Y-%m-%d %H:%M:%S")] 日志切割任务结束" >> "${LOGS_DIR}/logrotate.log"
3 crontab -e 定时任务配置
xxx.sh文件可以放在任何目录,只要配置的路径正确就可以,建议放在日志目录下
#每个月10号12点
0 12 10 * * /opt/redis6002/redis_logrotate.sh > /dev/null 2>&1
#每天0点
0 0 * * * /usr/local/nginx/logs/nginx_logrotate.sh > /dev/null 2>&1