日志切割脚本失败需内部重试而非依赖crontab,因crontab不感知执行结果;应按错误类型区分可重试(如“no such file”“text file busy”)与不可重试(如权限拒绝、进程不存在),采用指数退避+随机抖动的while循环重试,并归档失败详情、绝对路径调用命令。

日志切割脚本本身失败概率低,但一旦失败(如 kill -USR1 无响应、mv 权限不足、nginx.pid 不存在),会导致后续日志堆积、监控失效或磁盘爆满——必须加重试,且不能靠 crontab 重跑(它只按时间触发,不感知上次是否成功)。
为什么直接在 crontab 里加重试没用
crontab 不判断命令退出码,0 0 * * * /usr/local/nginx/sbin/cut_nginx_log.sh 即使脚本第 1 行就 exit 1,第二天同一时间仍会再执行一次,不会“接着上次失败点重试”。重试逻辑必须下沉到脚本内部。
重试前先区分哪些错误值得重试
盲目重试反而掩盖问题。需在脚本中根据错误类型做判断:
-
mv: cannot stat 'access.log': No such file or directory→ 可能是 Nginx 没写日志,属临时状态,可重试 -
kill: (12345): No such process→nginx.pid丢失或 Nginx 崩溃,需人工介入,不重试 -
Permission denied→ 权限配置错误,永久性故障,不重试 -
Text file busy→ 文件正被写入,典型瞬时冲突,应重试
建议用 stderr 关键词匹配 + $? 组合判断:
if [[ $? -ne 0 ]] && grep -q -E "(No such file|Text file busy|Device or resource busy)" "$LOG_TMP"; then should_retry=true fi
用 while 循环实现带退避的有限重试
不要用 for ((i=1; i 简单轮询——它固定等长间隔,容易和上游服务限流节奏共振。改用指数退避 + 随机抖动:
- 最大重试次数设为
MAX_RETRY=3(覆盖绝大多数瞬时问题) - 第 1 次失败后
sleep 1,第 2 次sleep 2,第 3 次sleep 4 - 实际 sleep 时间加 ±20% 随机抖动:
sleep $(echo "scale=1; $((2 ** attempt)) * (0.8 + $RANDOM/32767*0.4)" | bc) - 每次重试前重新生成
today时间戳,避免跨天切割错乱
关键:重试循环必须包裹整个切割主干(mv + kill -USR1),而不是只包某一条命令。
失败后必须归档原始错误并告警
重试耗尽后,静默退出等于没重试。要留下可追溯线索:
- 把完整 stderr 输出、
nginx -t校验结果、ls -l $log_path快照打包进/var/log/nginx/cut_failures/cut_$(date +%s).tar.gz - 用
mail -s "NGINX log cut failed"或curl推送钉钉机器人(仅一次,不放循环里) - 在 crontab 条目末尾加
>> /var/log/nginx/cut.log 2>&1,确保所有重试过程都被捕获
最易被忽略的一点:cut_nginx_log.sh 必须用绝对路径调用 mv、kill、date(如 /bin/mv),因为 cron 的 PATH 极窄,which mv 在交互 shell 里能用,cron 里常报 command not found。











