为什么 `exit code 0` 在欺骗你

发布: (2026年4月3日 GMT+8 17:01)
6 分钟阅读
原文: Dev.to

Source: Dev.to

请提供您希望翻译的具体文本内容,我将为您翻译成简体中文,并保持原有的格式、Markdown 语法以及技术术语不变。谢谢!

“did it run?” 的问题

你的 cron 任务在凌晨 2 点运行。退出码是 0。一切都正常,对吧?

上个月,我的 SIRENE 数据导入脚本连续 3 周每个星期天都成功运行。退出码 0。日志中没有错误。全程绿色。

但它实际导入了 0 行,而不是 1680 万行。没有人注意到这 3 周的错误。

大多数 cron‑monitoring 工具只回答一个问题:任务是否执行了?
它们使用“死人的开关”——你的脚本在完成时会 ping 一个 URL,如果 ping 没到达,你就会收到警报。

这很有用,但它忽略了最危险的失效模式:任务成功运行却产生了垃圾数据。

  • 你的备份脚本运行了,但数据库连接悄悄失败 → 0 字节被备份
  • 你的 ETL 任务处理了 12 行而不是 14 000 行 → 退出码 0
  • 你的导入用了 45 分钟而不是 2 分钟 → 没有错误,只是慢速退化

退出码 0 并不意味着“一切正常”。它的含义是“我没有崩溃”。

Source:

如果你的监控能够理解输出会怎样?

我创建了 Crontiq 来回答另一个问题:它是否运行了 输出是否正常?

思路很简单。与其仅仅 ping 一个 URL,不如把作业的输出以 JSON 形式发送:

# 在脚本结束时:
curl -X POST https://ping.crontiq.io/p/$API_KEY/import-sirene \
  -H "Content-Type: application/json" \
  -d "{\"rows\": $ROW_COUNT, \"duration_ms\": $DURATION, \"errors\": $ERROR_COUNT}"

Crontiq 自动完成三件事:

  1. 从你的 JSON 中提取所有数值(包括嵌套对象,例如 {"db": {"rows": 100}}db.rows = 100)。
  2. 使用 sparkline 图表随时间跟踪每个指标
  3. 使用移动平均 + 2 个标准差检测异常

无需配置,无需设定阈值,也不需要定义模式。只要发送 JSON,它就能自行处理。

这段数学简直太简单了

异常检测并不是机器学习或其他高级技术。它只是对最近 10 个值进行移动平均,再加上 2 个标准差:

average = avg(last_10_values)
stddev  = stddev(last_10_values)
is_anomaly = abs(current_value - average) > 2 * stddev

如果你的 rows 指标通常在 16,800,000 左右,突然降到 0,这相当于约 7,200 个标准差的偏差 → 你会收到警报。

如果它在几周内从 16.8 百万逐渐下降到 16.7 百万,则不会触发任何警报——这仍在正常范围内。

Healthchecks.io 将你的 POST 正文存储为原始文本。Cronitor 要求你使用特定的查询参数语法(&metric=count:3329)来格式化指标。而使用 Crontiq 时,只需发送脚本已经生成的任意 JSON 即可。

自我推销的徽章

当你将监控公开时,Crontiq 会生成一个实时 SVG 徽章:

[![SIRENE Import](https://crontiq.io/badge/cq_chk_xxx.svg)](https://crontiq.io/public/cq_chk_xxx)

徽章显示实际数据,而不仅仅是“通过”或“失败”:

import-sirene | 16.8M rows | ✓ healthy

把它放在你的 GitHub README 中,所有访问者都能看到你的数据管道正在运行且状态良好。点击徽章会打开一个带有迷你图和历史记录的公开状态页面。

示例

构建方式

The stack is straightforward:

  • Java 21 / Spring Boot 3.3 – 处理 ping 数据摄取
  • PostgreSQL(分区表)– 按月存储 ping 和指标
  • Redis – 缓存监控状态,以实现快速徽章生成
  • Hetzner – 单服务器,Docker 容器

Ping 接口的响应时间低于 50 ms。JSON 解析、指标提取以及异常检测均在异步执行,因此你的脚本永远不需要等待 Crontiq 完成。

JSON 扁平化是递归的——任何嵌套结构都会转换为点号表示法的键:

{
  "db": {
    "connections": 5,
    "pool_size": 20
  },
  "rows": 14209
}

转换为:

  • db.connections = 5
  • db.pool_size = 20
  • rows = 14209

每个键都会在仪表盘上生成各自的 sparkline。

免费。真的免费。

Crontiq 是 AZMORIS 开发者生态系统的免费入口:20 个监控、无限次 ping、自动指标、邮件提醒——无需信用卡,无试用期。

为什么免费?因为它最初是为了解决我自己的问题(监控我的 API)而构建的,而一个好的亏本产品比付费墙带来更多价值。

试试吧:crontiq.io

Crontiq 是 AZMORIS 生态系统的一部分,生态系统还包括 GEOREFER(法国商业数据 API)、Doxnex(文档智能)、GreenCalc(碳足迹 API)和 IDonex(身份管理)。

0 浏览
Back to Blog

相关文章

阅读更多 »

停止搜索 Cron 语法 - 改用此

Cron Expression Builder 如果你曾经为 cron 表达式而苦恼,你并不孤单。记忆像 /5 这样的格式或调试损坏的计划会浪费时间....