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 上测试通过,如有问题欢迎交流讨论。

相关推荐
黑蛋同志2 小时前
KVM虚拟化热迁移
运维·虚拟化·kvm
涛声依旧393162 小时前
Nginx+Docker 部署HTTPS站点+身份认证 完整实操一键部署脚本
nginx·docker·https
爱学习的小囧2 小时前
ESXi CPU 使用率高怎么排查?esxtop 一键定位占用高的虚拟机与进程
java·linux·运维·服务器·网络·虚拟化
Fanfanaas2 小时前
Linux 进程篇 (四)
linux·运维·服务器·开发语言·c++·学习
终端行者2 小时前
Jenkins流水线Pipeline声明式语法基础入门----下
运维·jenkins·cicd
文慧的科技江湖2 小时前
光伏管理系统产品需求文档(PRD) -【详细功能需求及研发核心字段清单】
运维·开源·慧知重卡开源充电桩平台·慧知开源充电桩平台·开源充电桩平台·光伏开源管理系统
Jacob程序员2 小时前
Linux 下启动达梦数据库 Manager 图形化客户端
linux·运维·服务器
IMPYLH2 小时前
Linux 的 pwd 命令
linux·运维·服务器·bash