← 全部文章

定时任务失败时收到提醒

给任意定时命令包一层,失败时把退出码和最后几行输出发到你的收件箱。

定时任务出错时往往悄无声息:三周前就停掉的备份、没人注意到的证书续期失败。几行 shell 就能让每次失败都变成一条提醒。

1. 创建发送链接

为服务器创建一个频道(或直接使用设备自带的频道),再为它创建一个以机器命名的发送链接,例如 nas cron。把链接保存到只有 root 可读的文件中:

Terminal window
sudo install -m 600 /dev/null /etc/gregor-send-url
echo 'https://<你的服务器>/v1/send/<token>' | sudo tee /etc/gregor-send-url >/dev/null

2. 添加包装脚本

把下面的内容保存为 /usr/local/bin/alert-on-failure,并用 chmod +x 设为可执行:

#!/bin/sh
# 用法:alert-on-failure <任务名称> <命令> [参数...]
name="$1"; shift
output=$("$@" 2>&1)
status=$?
[ "$status" -eq 0 ] && exit 0
tail=$(printf '%s' "$output" | tail -n 5)
body=$(printf '%s 上的退出码 %s\n%s' "$(hostname)" "$status" "$tail")
jq -n --arg title "$name 失败" --arg body "$body" --arg subject "cron:$(hostname):$name" \
'{alert: {title: $title, body: $body}, level: "high",
event: {type: "cron.failed", subject: $subject}}' |
curl -fsS -X POST "$(cat /etc/gregor-send-url)" \
-H 'Content-Type: application/json' --data-binary @- >/dev/null
exit "$status"

脚本会执行任务;成功时什么都不做,失败时发送一条 high 级别的提醒,带上退出码和最后五行输出。JSON 由 jq 生成,输出中的引号和换行不会破坏请求。

3. 包装你的任务

在每条 crontab 记录前加上包装脚本和一个简短的名称:

30 2 * * * /usr/local/bin/alert-on-failure "夜间备份" /usr/local/bin/backup.sh
0 4 * * 1 /usr/local/bin/alert-on-failure "续期证书" certbot renew --quiet

用一个必定失败的命令测试一次:

Terminal window
/usr/local/bin/alert-on-failure "测试任务" false

变化用法

  • 设为紧急。 对失败后需要立即有人处理的任务,把 level 改为 "urgent"。Gregor 会每分钟提醒一次,直到有人确认。
  • 识别重复失败。 同一个任务每次失败时 event.subject 都相同,因此会被识别为同一事件的重复。
  • 不要把密钥发出去。 最后几行输出会放进提醒正文。如果任务会打印 token 或密码,请减少 tail -n 5 的行数,或只发送退出码。