如何通过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
相关推荐
susplus43 分钟前
【IMX6ULL Linux系统移植】SD卡分区 | uboot详解
linux·arm·u-boot
@三十一Y1 小时前
Linux:实现简易的shell
linux
yuezhilangniao1 小时前
阿里云 CentOS 7 升级 Redis 到 7.2 实录-centos7安装redis7
redis·阿里云·centos
2301_777998342 小时前
TCP连接断开:进程终止、主机断电与网络断开
linux·服务器·网络·tcp/ip
我不会起名字3222 小时前
Redis 缓存与数据库一致性:先删缓存还是先更新库的 4 种方案
数据库·redis·缓存·一致性·延迟双删
星恒随风2 小时前
Linux进程基础(二):进程调度、上下文切换、O(1)调度器、环境变量与虚拟地址空间
linux·笔记·学习·状态模式
xbzb2 小时前
一边装一边报错:CentOS 7 + LNMP 跑 ECShop 的常见故障与解法
linux·运维·centos
学linux的QQ蛋2 小时前
图解 Linux 驱动编译进内核的完整工作流程
linux·运维·服务器
承渊政道3 小时前
Linux网络学习【UDP Socket编程实战:网络命令与客户端访问Linux验证】
linux·网络·学习·ubuntu·编程实战·udp socket
程序员-Benothing3 小时前
正则表达式详解:BRE、ERE、PCRE语法与实战案例
linux·运维·服务器