一、为什么需要日志切割?
Nginx 作为最流行的 Web 服务器/反向代理之一,每天都在产生大量的 access.log 和 error.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 |
每天轮转,也支持 weekly、monthly、yearly |
rotate N |
保留 N 份旧日志,超出的自动删除 |
dateext |
用日期作为后缀替代 .1、.2 递增编号 |
compress / delaycompress |
压缩旧日志 / 延迟压缩(最近一份不压缩) |
notifempty |
空文件不轮转 |
missingok |
日志文件缺失时静默跳过 |
create MODE OWNER GROUP |
轮转后立即创建新日志文件 |
sharedscripts |
多个日志匹配时,只运行一次脚本 |
size N |
文件超过 N(如 100M、1G)时也触发轮转 |
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;
}
为什么不推荐?
- 每个请求都要执行一次 map 正则匹配
- 每个 Worker 需要独立打开并维护当天日期的日志文件句柄
- 跨天时存在文件切换竞争条件
- 无法在不停机的情况下压缩旧日志
结论:仅限于个人博客、开发环境等低流量场景。
八、故障排查速查表
| 问题现象 | 可能原因 | 排查命令 |
|---|---|---|
| 切割后日志不再写入 | 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 上测试通过,如有问题欢迎交流讨论。