个人项目也需要可观测性:我给脚本加的三样东西
我维护着十几个"自己的小东西":几个定时脚本、一个每天自动发的流程、一个数据抓取任务,还有一堆半途而废的实验。
它们的共同点是——没人管就悄悄坏掉。不是报错,而是"某天你突然发现它已经三周没跑了"。
这周一我就发现了一个:某个每日任务连续三天没执行成功,而我没有收到任何提示。原因很蠢——它依赖的一个页面多了个弹窗。
于是我给自己定了个规矩:任何长期运行的脚本,都必须能回答三个问题。
一、问题一:它最近一次成功是什么时候?
不是"有没有报错",而是"最后一次成功的时间戳"。
做法很简单:每次成功就跑一次
date +%s > .last_success
或者写进状态文件:
{ "last_success": "2026-11-07T10:03:12+08:00", "consecutive_failures": 0 }
关键在读取端:我现在会定期看一眼"距离上次成功过了多久"。超过周期 1.5 倍就说明有问题。
这一条就能覆盖 80% 的"静默死亡"。因为大多数失败不是崩溃,而是卡住:等待一个不存在的元素、等待一个永远不返回的接口、等待一个人工确认。
二、问题二:失败时,它留下了什么线索?
"失败了"这三个字没有信息量。有信息量的是:
- 失败发生在第几步
- 当时的输入是什么
- 页面/接口返回了什么(哪怕只有前 200 字符)
- 上一次成功时的同类数据长什么样
我现在固定这么写日志:
[2026-11-07 10:03] step=login ok
[2026-11-07 10:03] step=submit FAILED reason="标题不能为空" title_len=0
[2026-11-07 10:03] snapshot=.debug/20261107-1003.html
title_len=0 这一行,比任何堆栈都有用——它直接告诉我"输入没落进去",而不是让我去猜选择器对不对。
原则:日志要能让人在三个月后一眼看懂,而不是让自己当时看懂。
三、问题三:失败之后,它会自己重试吗?会不会重试出问题?
这两件事必须分开想。
重试策略:网络抖动、页面加载慢,值得重试;业务错误(比如"标题不能为空")重试一百次也没用,只会刷一堆噪音。所以我的脚本里,错误分成两类:
RETRYABLE: 超时、5xx、元素未出现(限次数)
FATAL: 参数校验失败、401、明确的业务拒绝
幂等性:重试最大的风险是"做两次"。所以每个写操作都要带一个稳定的幂等键,比如
idempotencyKey = 任务名 + 目标对象ID + 日期
这样即使重试、即使并发,服务端也能识别出"这是同一个请求"。没有幂等键的重试,本质是在赌运气。
四、加一样东西:心跳
上面三条是"发现问题",但还有个更隐蔽的场景:任务根本没被调度。
cron 被改掉了、机器重启后服务没起来、容器被回收——这些情况下,脚本连"失败"的机会都没有。
所以我现在会给每个任务加一个"心跳":不管成功失败,只要跑过就写一行。看心跳文件的时间戳,就能区分"跑了但失败"和"压根没跑"。
这两种情况,排查方向完全相反。
五、一个反直觉的结论
做这些"额外工作"花的时间,比我预想的多——大概占整个脚本开发的三分之一。
但它换来的东西很实在:
- 我不需要每天手动确认"它今天跑了吗"
- 出问题时我打开日志就能定位,不用重现现场
- 我可以放心地把间隔从每天改成每周检查一次
换句话说:可观测性不是运维的专利,任何"你不在场时也要正常工作"的东西都需要它。
六、最小可用清单
如果你也想给自己的小脚本加这三样东西,这是我现在的起步配置,二十行以内能写完:
- 状态文件:
last_success、consecutive_failures - 分级日志:step + 结果 + 关键输入摘要
- 失败快照:出问题时把当时的页面/响应存一份
至于告警,我甚至没上监控系统——我只是在每天例行看一眼"哪个任务的 last_success 超期了"。对个人项目来说,这已经够了。
自动化的价值不在于"不用管",而在于"你管的时候能一眼看到问题在哪"。
- 点赞
- 收藏
- 关注作者
评论(0)