这一步不能省:务必把stderr显式重定向出去,并把日志按结构化方式记录清楚,至少要带上时间戳、主机名、任务标识、错误详情和退出码。日志最好落到专用文件里,再配合logrotate做管理;同时还要确保日志路径使用绝对路径、权限设置无误,最终做到出了问题能查、查了能追、追溯链条完整。

crontab 任务失败时如何捕获并记录告警
定时任务最让运维头疼的,往往不是“完全没跑”,而是它其实已经执行了,只是中途出了错,异常却被悄无声息地吞了下去。说到底,真正的关键不在于“有没有日志”,而在于“错误有没有被明确捕获,并且实实在在写入日志文件”。
crontab默认把stderr发到用户邮箱,但多数服务器没配邮件服务,结果就是错误彻底消失- 必须在 crontab 条目里用
>> /path/to/log 2>&1显式重定向,且路径必须绝对、目录存在、权限可写 - 更可靠的做法是在脚本内部用
$?判断上一条命令退出码,再写入结构化日志行,例如:echo "$(date '+%F %T') ERROR: rsync failed with code $?" >> /var/log/backup_alert.log
- 避免用
/dev/null吞掉所有输出——哪怕只是临时调试,也至少先重定向到临时文件
让告警记录带上下文和可追溯性
光记“失败”没用,得知道谁、什么时候、在哪台机器、因何失败。日志字段缺失会导致排查时间翻倍。
- 每条记录必须含时间戳(
$(date '+%F %T'))、主机名($(hostname -s))、任务标识(如mysql-backup) - 失败时优先记录具体错误来源:如果是
rsync,就捕获rsync的 stderr;如果是tar,就检查tar -tf是否返回非零 - 建议格式统一为:
2026-07-09 04:02:15 web01 mysql-backup ERROR: No space left on device (exit=1)
- 不要依赖
logger命令——它只进/var/log/messages,不易单独过滤;专用日志文件 +logrotate更可控
区分告警级别并控制记录频率
连续失败反复记同一行日志,等于没记;但完全去重又可能漏掉关键变化。需要平衡。
- 首次失败:完整记录错误信息 + 当前磁盘使用率(
df -h /backup | awk 'NR==2 {print $5}') - 连续失败(30分钟内相同 exit code):只追加时间戳,不重复写错误详情,避免日志膨胀
- 成功恢复后:记录一行
OK行,便于用grep -B1 OK /var/log/backup_alert.log快速定位上次失败终点 - 用
touch -c /tmp/alert_lock_${task} && sleep 300实现 per-task 静默期,比全局锁更精准
crontab 环境下日志路径权限常见问题
日志写不进去,90% 是权限或路径问题,不是语法问题。
- crontab 以指定用户身份运行,日志目录必须对该用户可写,例如
/var/log/backup_alert.log所在目录需chown root:backup /var/log/backup+chmod 750 /var/log/backup - 避免写到
/tmp——部分系统定期清理,且无持久性保障;生产环境一律用/var/log/下专用子目录 - 测试写权限:手动切到 cron 用户(如
sudo -u www-data bash),执行echo test >> /var/log/backup/try.log,看是否 Permission denied - 如果脚本由 root 运行但日志要给普通用户看,用
setgid目录 + 统一 group 写权限,比chmod 777安全得多