Cron 健康面板:job 显示 ok,数据却没更新

我有三十多个 Hermes cron。有一段时间里,面板上大半是绿的,页面却偶尔空白。基金页没有当日净值,AI 日报 JSON 缺了 headlines,网关凌晨「失败」但进程明明起来了。

问题不在调度器本身。问题在:我一直把 last_status 当成健康。

这篇文章写 venxine.vip 上的 /cronstatus/ 怎么从作业清单变成健康面板。数据从哪读、假绿灯怎么拆、自杀式重启怎么降噪,以及产品文件要校验到什么粒度。

页面在 Demo 密码后:https://venxine.vip/cronstatus/
System 页也有一条迷你摘要。

1. 旧面板只回答了一个问题

最早的 cronstatus 很诚实,也很浅:

  • ~/.hermes/cron/jobs.json
  • last_status 数 ok / error / paused
  • 表格列出名称、调度、上次运行时间
  • footer 写「60 秒刷新」,JS 却只有 filter

它回答的是:「最近一次跑完,调度器记了什么。」

它不回答:

  • 今天一共跑了几次、失败几次
  • 一个每 10 分钟该跑的任务是不是已经 40 分钟没动静
  • job 绿了,产物文件有没有今日业务键
  • 那条 error 是真故障,还是机制噪音

在任务少的时候够用。任务过了 30 个,启用十七八条之后,扫表变成负担,绿点也开始骗人。

2. 健康要叠三层,不能只信 last_status

我最后落成三层。每一层解决上一层看不见的东西。

P0:今日态势 + 能展开的错误

页顶一条健康条,大意是:

今日 24 次运行 · 23 成功 · 0 失败
作业 14 正常 · 0 异常 · 0 过期 · 17 暂停
下次关键:基金涨跌预警 · 7m 后

同时补上:

  • next_run_at 相对时间
  • 点击行展开 last_error / delivery error
  • error、stale 置顶排序
  • 真刷新:fetch /api/cronstatus,60 秒一次,登录 cookie 带上

这一步之后,面板从「静态清单」变成「现在能不能睡」。

P1:读 executions.db,不要只看最近一次

Hermes 在 ~/.hermes/cron/executions.db 里有执行账本。字段大致是 job_id、status、started_at、finished_at、error。

有了它才能算:

指标 用途
今日 runs / ok / fail 真正「今天」的态势
连续失败次数 比「上次 error」更有信号
平均 / 峰值耗时 抓 token 黑洞或卡住
stale 间隔任务超时未跑

stale 的启发式很土,但管用:从 cron 表达式估间隔,活跃窗口内若 now - last_run > max(2.5×interval, interval+5分钟) 就标过期。日更关键任务超过 36 小时没刷新也标。

注意:executions 里的 status 和 jobs.json 的 last_status 不一定同词。有 completed / failed / unknown。映射时要兼容,别只认 ok/error。

P2:产物心跳 + 业务校验

这是整篇里我最想强调的一层。

job 绿、文件 mtime 也是今天,仍然可能是假健康。典型例子:

  • daily-pl.json 今天被 touch 过,但 a.dates 最新还是昨天
  • tracker 有当日文件,内容是空列表
  • ainews 文件名是今天,headlines 只有 1 条

所以校验要进内容:

产物 业务条件(简化)
fund-tracker 当日快照 存在 YYYY-MM-DD.json,解析后非空
daily-pl.json 工作日 22:15 后应有今日 M/D;落后过多告警
ainews JSON 优先今日文件;headlines 不能过少
daily-analysis 最新日期键不能过旧
cron ticker 心跳文件不能太旧

盘中(工作日 9–15)tracker 或 ticker 挂了,顶部直接红条,不跟普通 warn 混。

告警文案尽量带建议动作,例如:

缺今日 7/24(最新 7/23)· 等22:00写入或23:45补漏
建议:等 daily-pl cron;仍缺则看补漏 job → /funddata/

绿点解决不了的问题,往往是「成功定义」写错了。成功不该是 exit 0,而该是「业务键在」。

3. 自杀式重启:error 不等于没重启

有一条 cron 天天 error,名字叫网关每日重启,脚本就两行意思:

systemctl --user restart hermes-gateway

04:30 跑它,等于让 job 去杀自己的宿主。结果:

  1. journal 里能看到 Stopping → Started,网关确实起来了
  2. jobs.json 的 last_status 仍是 error
  3. 错误文案类似:Gateway shutdown (final-cleanup) killed the job's tool subprocess...

我查了 7/23、7/24 两天:大约 04:30 开始停,04:33–04:34 新进程起来。进程 uptime 也从凌晨算起。重启是成功的,记账失败。

处理方式我选了展示层降噪,没有停任务:

'c931a015fa63': {
    'expected_noise': True,
    'noise_patterns': [
        'Gateway shutdown',
        'final-cleanup',
        'killed the job',
    ],
}

命中后:

  • 徽章显示 NOISE,不是 ERR
  • 不计入健康条的异常数
  • 不进顶部告警列表
  • 行变灰,可用「噪音」筛选
  • 展开仍能看见原始 last_error

jobs.json 里的 error 真相保留。面板信噪比回来了。

也可以改成 systemd user timer 在 Hermes 外重启,彻底没有这条 job error。我暂时没改,因为「每天凌晨重启一次」这个意图还在,只是别让它污染基金采集的健康判断。

4. UI:核心四卡,下载类默认折叠

启用任务里,真正决定「今天钱和内容还在不在」的就那么几条:

  • 📡 基金数据采集(盘中每 10 分钟)
  • ⚠️ 基金涨跌预警
  • 📋 每日收益数据(22:00)
  • 🛡️ 每日数据一致性补漏(23:45)

面板顶部固定核心任务条,一眼扫完。

另一边是一堆暂停的「每周下载-*」。它们 last_status 很吵,日常又不相关。默认折叠,按钮可展开。否则 30 行表里一半是噪音行。

筛选也拆细了:不健康、过期、噪音、核心、采集、内容、运维。
「正常」不含 NOISE;「不健康」不含已降噪的网关 error。

5. 数据流(实现侧)

jobs.json  ──► 作业元数据 / last_status / next_run
executions.db ──► 今日计数 / 连续失败 / 耗时
产物文件 mtime + JSON 内容 ──► 业务校验
                │
                ▼
        _cron_build_dashboard()
                │
        ┌───────┴────────┐
        ▼                ▼
 /cronstatus/      /api/cronstatus
 (服务端渲染)       (60s 前端刷新)
        │
        ▼
 /system/  Cron 健康迷你卡

权限:/cronstatus//api/cronstatus 都挂在 Demo 密码前缀下,避免执行错误和路径信息裸奔。

关键代码位置(本站):

  • fund-web/app.py_cron_build_dashboard_cron_product_heartbeats_cron_load_exec_stats
  • templates/cronstatus.html:健康条、核心条、表格、刷新
  • 网关脚本:~/.hermes/scripts/gateway-daily-restart.sh

6. 几个坑

  1. footer 写了自动刷新却没写 setInterval,属于文案欺诈。要么实现,要么删文案。
  2. 只看 mtime 会把「空文件今日 touch」判绿。要进 JSON。
  3. 跨月 day-number 碰撞(6/2 vs 7/2)在别的页面害过人;日期键尽量用完整 M/D 或 ISO。
  4. 自杀式任务不要跟行情任务共用「error = 坏了」的语义。要分开。
  5. 启用任务一多,没有核心分组就没有 30 秒扫一眼这回事。

7. 现在怎么用

盘中:看核心四卡 + 盘中红线。tracker 超过大约 25 分钟没更新就该紧张。
收盘后:盯 daily-pl 是否写入今日键;22:15 还没有就按建议去查 22:00 job 和 23:45 补漏。
凌晨:网关 NOISE 可以当背景音,别和采集故障一起报警。
System 页:只看今日成功/失败和异常数,细节仍回 cronstatus。

8. 和相邻系统的关系

这套面板不替代 cron 编排本身。编排、prompt、deliver 仍是 Hermes jobs.json 的事,我另有一篇写过调度侧。

它也不替代数据管线文章。fundtracker 从零搭建、三层防线、DCA 同源,说的是「数怎么来」。健康面板说的是「数还敢不敢信」。

账户侧的驾驶舱、真账草稿吃的是同一批 cron 产物。面板红了,那些页再好看也是旧戏重放。

收尾

我现在看 cron,第一眼不再数有多少绿点。

第一眼看:今天跑了几次、核心四条在不在、产物业务键在不在、那条红是不是又在自杀。

last_status 还在用,只是降级成其中一票,不是唯一票。

面板入口:https://venxine.vip/cronstatus/(Demo 密码)