Nginx 日志切割完全指南:从原理到生产实战

一、为什么需要日志切割?

Nginx 作为最流行的 Web 服务器/反向代理之一,每天都在产生大量的 access.logerror.log。如果不做切割,一个单文件日志可能会膨胀到几十 GB 甚至上百 GB,带来以下问题:

问题 影响
磁盘空间耗尽 日志文件过大导致服务器磁盘写满,服务不可用
查询效率低下 在几 GB 的日志中 grep 一条记录,耗时可能超过分钟级
日志分析困难 无法按天/周做访问趋势分析和报表
文件句柄风险 某些工具处理超大文件时可能触发内存溢出

最佳实践:按天切割日志,配合压缩保留 30~90 天的历史记录。


二、核心原理

Nginx 本身 不支持 按时间自动切分日志。它的日志写入流程是:

复制代码
Nginx Master 进程启动
    ↓
打开日志文件(获取文件描述符 fd)
    ↓
所有 Worker 进程通过 fd 写入日志
    ↓
日志文件持续增长...

如果直接用 mv 重命名日志文件,Nginx 仍然持有旧的文件描述符,会继续往重命名后的文件写入。所以关键一步是 通知 Nginx 重新打开日志文件

复制代码
# 方法一:发送 USR1 信号(最常用)
kill -USR1 $(cat /usr/local/nginx/logs/nginx.pid)

# 方法二:使用 nginx 命令
nginx -s reopen

USR1 信号会让 Nginx Master 进程通知所有 Worker 进程重新打开日志文件。完整流程:

复制代码
mv access.log access_20260416.log    # 重命名旧日志
kill -USR1 <pid>                     # 通知 Nginx 重新打开
# Nginx 自动创建新的 access.log,继续写入

⚠️ 千万不要直接删除日志文件 !必须先 mv 再发信号。直接 rm 会导致 Nginx 丢失文件描述符,磁盘空间不会被释放(直到进程重启)。


三、三种切割方案对比

方案 推荐度 优点 缺点 适用场景
logrotate ⭐⭐⭐⭐⭐ 系统自带、配置简单、功能完善 灵活性稍弱 生产环境首选
cron + Shell 脚本 ⭐⭐⭐⭐ 完全可控、可定制复杂逻辑 需自己维护脚本 有特殊需求时
Nginx 内置变量 ⭐⭐ 无需外部工具 高并发下性能差 仅学习/测试用

下面逐一详解。


四、方案一:logrotate(推荐)

4.1 什么是 logrotate?

logrotate 是 Linux 系统自带的日志轮转工具,由 cron 每天定时调用。大多数发行版安装 Nginx 后会自动生成配置文件。

4.2 检查现有配置

复制代码
# 查看 Nginx 的 logrotate 配置
cat /etc/logrotate.d/nginx

# 查看 logrotate 是否有 cron 调度
ls -l /etc/cron.daily/logrotate
cat /etc/cron.daily/logrotate

4.3 完整配置示例

复制代码
/usr/local/nginx/logs/*.log {
    daily                    # 每天轮转一次
    missingok                # 日志文件不存在时不报错
    rotate 30                # 保留最近 30 个轮转文件
    dateext                  # 使用日期作为后缀(如 access.log-20260416)
    dateformat -%Y%m%d       # 自定义日期格式
    compress                 # 压缩旧日志(gzip)
    delaycompress            # 延迟压缩:保留最近一个未压缩,方便排查
    notifempty               # 日志为空时不轮转
    create 0640 nginx adm    # 轮转后创建新文件,权限 0640,属主 nginx,属组 adm
    sharedscripts            # 所有日志只执行一次 postrotate 脚本
    postrotate
        # 读取 pid 文件并向 Nginx 发送 USR1 信号
        [ -f /usr/local/nginx/logs/nginx.pid ] && kill -USR1 `cat /usr/local/nginx/logs/nginx.pid`
    endscript
}

4.4 参数详解

参数 说明
daily 每天轮转,也支持 weeklymonthlyyearly
rotate N 保留 N 份旧日志,超出的自动删除
dateext 用日期作为后缀替代 .1.2 递增编号
compress / delaycompress 压缩旧日志 / 延迟压缩(最近一份不压缩)
notifempty 空文件不轮转
missingok 日志文件缺失时静默跳过
create MODE OWNER GROUP 轮转后立即创建新日志文件
sharedscripts 多个日志匹配时,只运行一次脚本
size N 文件超过 N(如 100M1G)时也触发轮转

4.5 手动测试

复制代码
# 调试模式:只打印将要执行的操作,不实际执行
sudo logrotate -d /etc/logrotate.d/nginx

# 强制执行一次轮转(即使今天已经执行过)
sudo logrotate -vf /etc/logrotate.d/nginx

4.6 验证结果

复制代码
ls -lh /usr/local/nginx/logs/

输出示例:

复制代码
-rw-r----- 1 nginx adm  12M Apr 17 00:00 access.log
-rw-r----- 1 nginx adm 156M Apr 16 23:59 access.log-20260416
-rw-r----- 1 nginx adm  12M Apr 15 00:00 access.log-20260415.gz
-rw-r----- 1 nginx adm 890K Apr 17 00:00 error.log
-rw-r----- 1 nginx adm 2.1M Apr 16 23:59 error.log-20260416

4.7 排查 logrotate 未生效

复制代码
# 查看 logrotate 执行记录
cat /var/lib/logrotate/status | grep nginx

# 手动执行并查看详细输出
sudo logrotate -vf /etc/logrotate.d/nginx 2>&1

# 常见原因
# 1. cron 服务未启动:systemctl status crond
# 2. 配置文件语法错误:logrotate -d 检查
# 3. 日志文件名不匹配通配符
# 4. 状态文件中标记为 "already rotated this year"

五、方案二:cron + Shell 脚本(完全可控)

5.1 基础版脚本

复制代码
#!/bin/bash
# ============================================
# Nginx 日志按天切割脚本(基础版)
# 适用于:单 Nginx 实例,标准日志路径
# ============================================

LOGS_PATH="/usr/local/nginx/logs"
PID_FILE="/usr/local/nginx/logs/nginx.pid"
KEEP_DAYS=30

# 获取昨天的日期(兼容 macOS 和 Linux)
if [[ "$OSTYPE" == "darwin"* ]]; then
    YESTERDAY=$(date -v-1d +%Y%m%d)
else
    YESTERDAY=$(date -d "yesterday" +%Y%m%d)
fi

# 1. 移动旧日志
mv ${LOGS_PATH}/access.log ${LOGS_PATH}/access_${YESTERDAY}.log
mv ${LOGS_PATH}/error.log  ${LOGS_PATH}/error_${YESTERDAY}.log

# 2. 通知 Nginx 重新打开日志
if [ -f "$PID_FILE" ]; then
    kill -USR1 $(cat "$PID_FILE")
    echo "[$(date)] 已发送 USR1 信号给 Nginx (PID: $(cat $PID_FILE))"
else
    echo "[$(date)] 错误:找不到 PID 文件 $PID_FILE"
    exit 1
fi

# 3. 压缩并清理过期日志
find ${LOGS_PATH} -name "access_*.log" -mtime +${KEEP_DAYS} -exec gzip {} \;
find ${LOGS_PATH} -name "error_*.log"  -mtime +${KEEP_DAYS} -exec gzip {} \;
find ${LOGS_PATH} -name "*.gz"        -mtime +$((KEEP_DAYS * 2)) -delete

echo "[$(date)] 日志切割完成"

5.2 增强版脚本(生产可用)

以下脚本增加了日志大小检测、多路径支持、告警通知等企业级特性:

复制代码
#!/bin/bash
# ============================================
# Nginx 日志切割脚本(增强版)
# 功能:多日志路径、大小检测、自动压缩、
#       钉钉告警、执行日志记录
# ============================================

set -euo pipefail

# ============ 配置区 ============
NGINX_PID_FILE="/usr/local/nginx/logs/nginx.pid"
LOG_DIRS=(
    "/usr/local/nginx/logs"
    "/data/nginx_logs/api.example.com"
    "/data/nginx_logs/web.example.com"
)
KEEP_DAYS=30
COMPRESS_AFTER_DAYS=3        # 超过 N 天的日志自动压缩
MIN_LOG_SIZE="10M"           # 小于此大小的日志不切割(防止空转)
LOG_DIR="/var/log/nginx-rotate"  # 脚本自身执行日志
DINGTALK_WEBHOOK=""          # 钉钉告警 Webhook(留空则不告警)
# ================================

mkdir -p "$LOG_DIR"
LOG_FILE="${LOG_DIR}/rotate_$(date +%Y%m%d).log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

alert() {
    local msg="$1"
    log "[ALERT] $msg"
    if [[ -n "$DINGTALK_WEBHOOK" ]]; then
        curl -s -X POST "$DINGTALK_WEBHOOK" \
            -H 'Content-Type: application/json' \
            -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"⚠️ Nginx日志切割告警: ${msg}\"}}" \
            > /dev/null 2>&1
    fi
}

# 检查 Nginx 是否运行
check_nginx() {
    if [[ ! -f "$NGINX_PID_FILE" ]]; then
        alert "Nginx PID 文件不存在: $NGINX_PID_FILE"
        return 1
    fi
    local pid
    pid=$(cat "$NGINX_PID_FILE")
    if ! kill -0 "$pid" 2>/dev/null; then
        alert "Nginx 进程 (PID: $pid) 未运行"
        return 1
    fi
    log "Nginx 运行正常 (PID: $pid)"
}

# 获取昨天日期
get_yesterday() {
    if [[ "$OSTYPE" == "darwin"* ]]; then
        date -v-1d +%Y%m%d
    else
        date -d "yesterday" +%Y%m%d
    fi
}

# 获取文件大小(人类可读转字节)
size_to_bytes() {
    local size_str="$1"
    local num="${size_str%[KkMmGg]*}"
    local unit="${size_str##*[0-9]}"
    case "${unit^^}" in
        K) echo $((num * 1024)) ;;
        M) echo $((num * 1024 * 1024)) ;;
        G) echo $((num * 1024 * 1024 * 1024)) ;;
        *) echo "$num" ;;
    esac
}

# 切割单个日志目录
rotate_logs() {
    local log_dir="$1"
    local yesterday=$(get_yesterday)
    local min_bytes=$(size_to_bytes "$MIN_LOG_SIZE")
    local rotated=0

    for log_file in "${log_dir}"/*.log; do
        [[ -f "$log_file" ]] || continue

        local file_size=$(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file" 2>/dev/null)

        if [[ "$file_size" -lt "$min_bytes" ]]; then
            log "跳过 ${log_file}(大小 ${file_size} 字节,小于阈值 ${MIN_LOG_SIZE})"
            continue
        fi

        local basename=$(basename "$log_file" .log)
        local target="${log_dir}/${basename}_${yesterday}.log"

        mv "$log_file" "$target"
        log "已切割: ${log_file} -> ${target} ($(numfmt --to=iec $file_size 2>/dev/null || echo ${file_size}B))"
        rotated=$((rotated + 1))
    done

    echo "$rotated"
}

# 压缩过期日志
compress_old_logs() {
    local log_dir="$1"
    local count=0

    for log_file in "${log_dir}"/*_[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].log; do
        [[ -f "$log_file" ]] || continue
        local file_age
        if [[ "$OSTYPE" == "darwin"* ]]; then
            file_age=$(( ($(date +%s) - $(stat -f%m "$log_file")) / 86400 ))
        else
            file_age=$(( ($(date +%s) - $(stat -c%Y "$log_file")) / 86400 ))
        fi

        if [[ "$file_age" -ge "$COMPRESS_AFTER_DAYS" ]]; then
            gzip -f "$log_file"
            count=$((count + 1))
        fi
    done

    # 删除超过保留期的压缩文件
    local deleted=0
    for gz_file in "${log_dir}"/*.log.gz; do
        [[ -f "$gz_file" ]] || continue
        local file_age
        if [[ "$OSTYPE" == "darwin"* ]]; then
            file_age=$(( ($(date +%s) - $(stat -f%m "$gz_file")) / 86400 ))
        else
            file_age=$(( ($(date +%s) - $(stat -c%Y "$gz_file")) / 86400 ))
        fi

        if [[ "$file_age" -gt "$KEEP_DAYS" ]]; then
            rm -f "$gz_file"
            deleted=$((deleted + 1))
        fi
    done

    log "目录 ${log_dir}: 压缩 ${count} 个文件, 删除 ${deleted} 个过期文件"
}

# ============ 主流程 ============
log "====== 开始日志切割 ======"

check_nginx || exit 1

total_rotated=0
for dir in "${LOG_DIRS[@]}"; do
    if [[ ! -d "$dir" ]]; then
        log "目录不存在,跳过: $dir"
        continue
    fi
    local_count=$(rotate_logs "$dir")
    total_rotated=$((total_rotated + local_count))
    compress_old_logs "$dir"
done

# 发送 USR1 信号
if [[ "$total_rotated" -gt 0 ]]; then
    kill -USR1 $(cat "$NGINX_PID_FILE")
    log "已发送 USR1 信号,共切割 ${total_rotated} 个日志文件"
else
    log "没有日志需要切割,跳过信号发送"
fi

log "====== 日志切割完成 ======"

5.3 配置 cron 定时任务

复制代码
# 编辑 crontab
crontab -e

# 每天凌晨 00:05 执行日志切割
5 0 * * * /usr/local/bin/nginx-log-rotate.sh >> /var/log/nginx-rotate/cron.log 2>&1

5.4 带 logrotate 配置的 cron 方案

如果想在 cron 中实现更灵活的切割逻辑(如按大小切割),可以这样:

复制代码
#!/bin/bash
# 按大小+时间混合切割
# 当日志超过 500M 或距离上次切割超过 24 小时时执行

LOG_FILE="/usr/local/nginx/logs/access.log"
PID_FILE="/usr/local/nginx/logs/nginx.pid"
MAX_SIZE=524288000  # 500MB in bytes
MIN_INTERVAL=86400   # 24 hours in seconds
MARKER_FILE="/tmp/nginx-rotate-marker"

rotate() {
    local ts=$(date +%Y%m%d_%H%M%S)
    mv "$LOG_FILE" "${LOG_FILE}.${ts}"
    gzip "${LOG_FILE}.${ts}" &
    kill -USR1 $(cat "$PID_FILE")
    date +%s > "$MARKER_FILE"
    echo "$(date) - 已切割日志 (${LOG_FILE} -> ${LOG_FILE}.${ts}.gz)"
}

# 检查文件大小
file_size=$(stat -f%z "$LOG_FILE" 2>/dev/null || stat -c%s "$LOG_FILE" 2>/dev/null)
# 检查上次切割时间
last_rotate=0
[[ -f "$MARKER_FILE" ]] && last_rotate=$(cat "$MARKER_FILE")
now=$(date +%s)
elapsed=$((now - last_rotate))

if [[ "$file_size" -gt "$MAX_SIZE" ]] || [[ "$elapsed" -gt "$MIN_INTERVAL" ]]; then
    rotate
else
    echo "$(date) - 日志无需切割 (大小: ${file_size}B, 上次切割: ${elapsed}s 前)"
fi

六、实战案例

📋 案例一:多域名 Vhost 日志切割

场景:一台服务器托管多个站点,每个站点有独立的 access/error 日志。

复制代码
/usr/local/nginx/logs/
├── nginx.pid
├── api.example.com_access.log
├── api.example.com_error.log
├── web.example.com_access.log
├── web.example.com_error.log
├── admin.example.com_access.log
└── admin.example.com_error.log

logrotate 配置

复制代码
/usr/local/nginx/logs/*_access.log
/usr/local/nginx/logs/*_error.log {
    daily
    missingok
    rotate 60
    dateext
    dateformat -%Y%m%d
    compress
    delaycompress
    notifempty
    create 0640 nginx nginx
    sharedscripts
    postrotate
        [ -f /usr/local/nginx/logs/nginx.pid ] && kill -USR1 `cat /usr/local/nginx/logs/nginx.pid`
    endscript
}

切割后效果:

复制代码
/usr/local/nginx/logs/
├── api.example.com_access.log                      # 当天
├── api.example.com_access.log-20260416             # 昨天(未压缩)
├── api.example.com_access.log-20260415.gz          # 前天(已压缩)
├── api.example.com_error.log-20260416
├── ...

📋 案例二:日志按小时切割(高流量场景)

场景:日请求量过亿的 API 网关,日志增长极快,需要按小时切割。

复制代码
#!/bin/bash
# Nginx 日志按小时切割
# Cron: 0 * * * * (每小时执行一次)

LOGS_PATH="/usr/local/nginx/logs"
PID_FILE="${LOGS_PATH}/nginx.pid"
KEEP_HOURS=168  # 保留 7 天 = 168 小时
HOUR=$(date -d '1 hour ago' '+%Y%m%d%H' 2>/dev/null || date -v-1H '+%Y%m%d%H')

for log_file in ${LOGS_PATH}/*.log; do
    [[ -f "$log_file" ]] || continue
    base=$(basename "$log_file" .log)
    target="${LOGS_PATH}/${base}_${HOUR}.log"
    mv "$log_file" "$target"
done

# 通知 Nginx
[ -f "$PID_FILE" ] && kill -USR1 $(cat "$PID_FILE")

# 清理超过保留期的压缩日志
find "$LOGS_PATH" -name "*_[0-9]*.log.gz" -mmin +$((KEEP_HOURS * 60)) -delete
# 压缩超过 1 小时的日志
find "$LOGS_PATH" -name "*_[0-9]*.log" -mmin +60 -exec gzip {} \;

Cron 配置:

复制代码
crontab -e
# 每小时整点执行
0 * * * * /usr/local/bin/nginx-hourly-rotate.sh

📋 案例三:Docker 环境下的日志切割

场景:Nginx 运行在 Docker 容器中,宿主机需要管理日志文件。

方案 A:宿主机 logrotate(推荐)

docker-compose.yml 中挂载日志目录:

复制代码
services:
  nginx:
    image: nginx:1.27
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./logs:/var/log/nginx        # 日志挂载到宿主机
    ports:
      - "80:80"

宿主机 logrotate 配置 /etc/logrotate.d/docker-nginx

复制代码
/path/to/project/logs/*.log {
    daily
    missingok
    rotate 30
    dateext
    compress
    delaycompress
    notifempty
    copytruncate                    # 关键!容器内无法接收宿主机的信号
    create 0644 root root
}

注意 :Docker 容器内无法直接接收宿主机的 kill -USR1,所以这里必须使用 copytruncate。它的原理是先复制日志内容,再清空原文件。缺点是在高并发下可能丢失少量日志。

方案 B:进入容器发信号
复制代码
#!/bin/bash
# Docker 容器内日志切割

CONTAINER_NAME="nginx"
LOG_DIR="/var/log/nginx"
KEEP_DAYS=30
YESTERDAY=$(date -d "yesterday" +%Y%m%d)

# 进入容器执行切割
docker exec $CONTAINER_NAME bash -c "
    cd $LOG_DIR && \
    for f in *.log; do
        [ -f \"\$f\" ] && mv \"\$f\" \"\${f%.log}_${YESTERDAY}.log\"
    done && \
    nginx -s reopen && \
    find . -name '*_[0-9]*.log' -mtime +${KEEP_DAYS} -exec gzip {} \; && \
    find . -name '*.gz' -mtime +$((KEEP_DAYS * 2)) -delete
"

📋 案例四:日志切割 + ELK 集成

场景:切割后的日志需要推送到 ELK(Elasticsearch + Logstash + Kibana)做集中分析。

复制代码
#!/bin/bash
# 日志切割 + 推送到 Logstash

LOGS_PATH="/usr/local/nginx/logs"
YESTERDAY=$(date -d "yesterday" +%Y%m%d)
LOGSTASH_HOST="10.12.12.80:5044"

# 1. 切割日志
for log_file in ${LOGS_PATH}/*_access.log; do
    [[ -f "$log_file" ]] || continue
    base=$(basename "$log_file" _access.log)
    target="${LOGS_PATH}/${base}_access_${YESTERDAY}.log"
    mv "$log_file" "$target"
done

kill -USR1 $(cat ${LOGS_PATH}/nginx.pid)

# 2. 使用 nc 推送切割后的日志到 Logstash(Filebeat 更推荐)
for rotated_log in ${LOGS_PATH}/*_access_${YESTERDAY}.log; do
    [[ -f "$rotated_log" ]] || continue
    log "推送日志: $rotated_log -> $LOGSTASH_HOST"
    # 生产环境建议使用 Filebeat 而非 nc
    # /usr/bin/filebeat -c /etc/filebeat/filebeat.yml -e &
done

# 3. 压缩旧日志
find ${LOGS_PATH} -name "*_access_*.log" -mtime +3 -exec gzip {} \;

推荐使用 Filebeat 作为日志采集器,配置自动跟踪新文件:

复制代码
# /etc/filebeat/filebeat.yml
filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /usr/local/nginx/logs/*_access_*.log
    fields:
      log_type: nginx_access
    clean_inactive: 168h       # 168 小时未更新的文件不再跟踪
    ignore_older: 24h          # 忽略超过 24 小时的旧文件(避免重复采集)

output.logstash:
  hosts: ["10.12.12.80:5044"]

七、方案三:Nginx 内置变量(不推荐生产使用)

通过 $time_iso8601 动态生成日志文件名:

复制代码
http {
    map $time_iso8601 $logdate {
        ~^(?<ymd>\d{4}-\d{2}-\d{2}) $ymd;
        default 'nodate';
    }

    # 每个请求都需要解析 map 变量并打开/写入对应文件
    # 高并发时 I/O 压力巨大,不推荐!
    access_log /usr/local/nginx/logs/access-$logdate.log main;
}

为什么不推荐?

  1. 每个请求都要执行一次 map 正则匹配
  2. 每个 Worker 需要独立打开并维护当天日期的日志文件句柄
  3. 跨天时存在文件切换竞争条件
  4. 无法在不停机的情况下压缩旧日志

结论:仅限于个人博客、开发环境等低流量场景。


八、故障排查速查表

问题现象 可能原因 排查命令
切割后日志不再写入 USR1 信号未发送成功 kill -0 $(cat nginx.pid) 检查进程
日志文件被清空 使用了 >truncate 而非 mv 改用 mv + kill -USR1
磁盘空间未释放 直接 rm 了日志文件 `lsof
logrotate 不执行 cron 服务未运行 systemctl status crond
新日志文件权限错误 create 参数配置不当 检查 create MODE OWNER GROUP
Docker 容器日志未切割 容器内无法接收宿主机信号 使用 copytruncate 或进入容器执行
日期后缀不生效 缺少 dateext 参数 确认配置中有 dateext

快速恢复已删除但未释放的日志

复制代码
# 查找被进程占用但已删除的文件
lsof | grep deleted | grep nginx

# 临时恢复(通过 /proc 文件描述符)
cat /proc/<pid>/fd/<fd> > /usr/local/nginx/logs/access.log.recovered

九、自动化巡检脚本

在日志切割的基础上,可以加一个简单的日志巡检,每日检查切割是否正常:

复制代码
#!/bin/bash
# Nginx 日志切割巡检脚本
# 建议:每日 08:00 执行,检查昨天的切割结果

LOGS_PATH="/usr/local/nginx/logs"
YESTERDAY=$(date -d "yesterday" +%Y%m%d)
ALERT_THRESHOLD=0  # 昨天切割文件数为 0 则告警

echo "===== Nginx 日志切割巡检 ====="
echo "检查日期: $(date)"
echo "目标日期: ${YESTERDAY}"

# 检查切割文件是否生成
count=$(find "$LOGS_PATH" -name "*_${YESTERDAY}*" -type f | wc -l)
echo "切割文件数: ${count}"

if [[ "$count" -eq "$ALERT_THRESHOLD" ]]; then
    echo "[WARNING] 昨天没有生成切割日志文件!"
    # 可接入钉钉/企业微信告警
    exit 1
fi

# 检查压缩情况
compressed=$(find "$LOGS_PATH" -name "*_${YESTERDAY}*.gz" -type f | wc -l)
echo "已压缩文件数: ${compressed}"

# 检查磁盘空间
disk_usage=$(df -h "$LOGS_PATH" | tail -1 | awk '{print $5}' | tr -d '%')
echo "磁盘使用率: ${disk_usage}%"
if [[ "$disk_usage" -gt 80 ]]; then
    echo "[WARNING] 磁盘使用率超过 80%!"
fi

# 统计昨天日志行数(检查是否有写入)
total_lines=0
for f in $(find "$LOGS_PATH" -name "access_${YESTERDAY}*"); do
    lines=$(wc -l < "$f")
    total_lines=$((total_lines + lines))
done
echo "昨日总访问量: ${total_lines}"

echo "===== 巡检完成 ====="

十、总结

Nginx 日志切割是运维工作中最基础也是最重要的任务之一。根据场景选择合适的方案:

场景 推荐方案
标准生产环境 logrotate(零成本,系统自带)
多域名 / 多路径 cron + 增强版 Shell 脚本
高流量 API 网关 按小时切割 + Filebeat 采集
Docker/K8s 容器化 copytruncate 或 sidecar 方案
需要告警和监控 脚本 + 钉钉/企业微信 + 巡检

最后再强调一次 :切割日志的正确姿势永远是 mv,再发 USR1 信号,切勿直接删除或清空日志文件。


本文涉及的脚本已在 Rocky Linux 9 和 Ubuntu 22.04 上测试通过,如有问题欢迎交流讨论。

相关推荐
ping某1 天前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
Inhand陈工3 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
酣大智3 天前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_3 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
施努卡机器视觉3 天前
SNK施努卡侧滑门锁上滑轮总成自动化装配线,从零件到组件,全流程精密制造方案
运维·自动化·制造
AC赳赳老秦3 天前
用 OpenClaw 搭建服务器故障应急响应系统,自动处理 80% 常见运维故障
android·运维·服务器·python·rxjava·deepseek·openclaw
java_cj3 天前
深入kube-apiserver认证机制:从Bearer Token到mTLS的完整认证链解析
linux·运维·服务器·云原生·容器·kubernetes