2026-09-20 那天早上,我打开邮箱,没有日报。
查了一圈,三个 Windows 定时任务全被删了。不是报错、不是超时,是没有进程运行过------00:00 那条日志根本不存在。
更难受的是:哨兵没报警、告警邮件没发、health.json 一片安静。整套监控都好好的,只是它监控的那台机器上,没有任何东西活着来报告"我挂了"。
那天是靠人发现没收到日报,才发现系统停了。
本机自检有个覆盖不到的洞
原来的体检逻辑长这样:每轮跑完,检查日志、检查哨兵日期、不达标就发告警邮件。
它能证明的是:"我这一轮在跑"。
它证明不了的是:"这一轮根本没有发生"。
这是两件事。任务被删、机器整夜没醒、断电、系统被重装------这些情况下没有任何本机进程能说话。你写在本机上的监控,和要监控的那个东西,会一起消失。
补一个进程去监控另一个进程也没用,它们死在同一台机器上。
方向反过来:让外部等你报平安
改法是把方向调个个儿------本机每轮收尾时,主动向外部 POST 一次"我还活着",外部服务如果超过 26 小时没收到,就告警。
python
# deadman_ping.py(节选)
def build_payload(sentinel_path=None, now=None):
return {
"sentinel": read_sentinel(sentinel_path),
"sent_at": (now or datetime.now()).astimezone().isoformat(timespec="seconds"),
"host": socket.gethostname(),
}
def ping(url, payload, timeout=10, token=""):
body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
headers = {"Content-Type": "application/json"}
if token:
headers["Authorization"] = f"Bearer {token}"
req = urllib.request.Request(url, data=body, method="POST", headers=headers)
with urllib.request.urlopen(req, timeout=timeout) as resp:
return resp.status
外部那端用 Cloudflare Worker Cron 或者任意 uptime 心跳服务都行,逻辑就一句:"最后一次心跳超过 26 小时 → 发信"。
成本近零,但覆盖的正好是本机自检的盲区。 单点从"本机自检"变成"本机自检 + 外部反向监控"。
三个不显眼的坑
坑一:不要因为心跳失败就中断主流程。
心跳是旁路。网络抖一下、Worker 部署中、token 过期,都不该让日报发不出去。调用方用 FailSoft 包着,失败只记一行日志,不改退出码。
监控设施反过来把业务搞挂,是最不划算的一种故障。
坑二:读哨兵文件必须去 BOM。
哨兵是 PowerShell 的 Set-Content -Encoding UTF8 写的。PS 5.1 会带 BOM,直接用 utf-8 读出来是 \ufeff2026-10-20------这个脏字符会进入载荷、日志、告警正文。实测在 GBK 控制台还会直接 UnicodeEncodeError,把心跳本身打断。
python
with open(path, "r", encoding="utf-8-sig") as f:
first = (f.read().strip().split() or [""])[0]
return first.lstrip("\ufeff")
坑三(最隐蔽):谁有资格续心跳。
这条是后来加的。系统里除了日报主链,还有个每 6 小时跑一次的 Discover 任务,收尾时也会走同一段代码。如果随便谁都能 ping,那么日链停摆了,Discover 还在每 6 小时替它报平安------26 小时死信告警永远不会触发,监控形同虚设。
所以加了 H3 判据:只有哨兵日期等于今天(日链确实产出过日报),才续这一次心跳。
powershell
$deadmanReady = ((($dsv.Trim() -split "\s+")[0]) -eq $todayStr)
if ($cfg.deadman -and $cfg.deadman.ping_url -and $deadmanReady) { ... }
一个心跳如果谁都能发,它就不说明任何事。
最后
这套东西技术上没什么含量,86 行,一个 POST。但它解决的那类问题很特殊:系统不是坏了,是没在跑------而所有"检查系统有没有坏"的手段,都默认了系统正在跑。
以后再设计监控,我会先问一句:如果明天整台机器一个进程都没起来,谁能告诉我?