如何通过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
相关推荐
爱和冰阔落1 小时前
【Linux】两个毫无关系的进程怎么通信?命名管道 FIFO 从原理到 Server/Client 实战
android·linux·数据库·c++·vim
ltl2 小时前
Windows 内核与 Linux 的关键差异
linux
ltl2 小时前
CentOS 停服与生态重组:Rocky、Alma、openEuler、龙蜥
linux
ltl9 小时前
eBPF 可观测性全景:bcc、bpftrace、libbpf 的工程路径
linux
2401_8685347810 小时前
数仓开发落地手册
linux·运维
范什么特西11 小时前
回答知识总结04(redis)
数据库·redis·缓存
正在走向自律11 小时前
使用atop工具监控Linux系统指标
linux·运维·php·实时监控·atop分析内存
果果燕12 小时前
实习笔记(一):NFS、Samba、VNC、Git、CMake、systemd、内核、交叉编译
linux·arm开发·c++·笔记·git
yyy(十一月限定版)13 小时前
017【入门】最小栈
linux·运维·服务器
不负岁月无痕13 小时前
简单理解操作系统结构
java·linux·c语言·开发语言·c++·面试