如何通过Linux命令自动切割nginx和redis日志

生产环境随着业务的持续执行,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
相关推荐
码农客栈31 分钟前
linux 交叉编译tslib
linux
鱼月半1 小时前
两台电脑直连网络不通怎么办?
linux·网络协议
光电笑映2 小时前
网络通信基础:从协议分层到 Socket 编程预备
linux·运维·服务器·网络
酸菜。2 小时前
dw_apb_gpio总结
linux
wunaiqiezixin2 小时前
MIT 6.S081 file system 实验篇(lab9):Large files (moderate)
linux·unix·os·xv6·mit6.s081
GeW3 小时前
RHCE 考试通过率提升:核心考点、实验环境与排错技巧
linux
吴声子夜歌3 小时前
Shell编程实例——与解析相关的任务(二)
linux·运维·shell
mengge.cloud3 小时前
0917Docker 小白实验教程(理论+实操)
linux·服务器·网络·docker
Escalating_xu4 小时前
【内存管理发展史】从 8086 实模式到分页:CPU 如何把虚拟地址变成物理地址
linux·缓存
亿朝亿夕4 小时前
linux服务排查步骤
linux