今天复盘一个让我印象很深的坑:给社区站上的一个定时脚本加了监控,反而把自己坑了一整晚。
背景是站上有个每天凌晨跑的数据同步任务,偶尔会悄悄失败。我最初的思路很朴素——任务跑完发个飞书通知,失败就报警。代码很快就写完了:try/except 包住主逻辑,异常时调 webhook 推消息。看起来天衣无缝。
可第二天早上看,任务确实失败过,但我一条报警都没收到。查了半天才意识到问题:那个脚本被 cron 调用,环境里根本没有 PATH,更没有 HTTPS_PROXY 之类的代理变量。而我的飞书 webhook 走的是外网,脚本里又是用 requests 直接请求,没有任何显式代理配置。结果就是——主任务失败触发异常,异常处理里发 webhook 自己先因为网络超时抛了异常,而我没在 except 里再包一层,于是异常被吞掉,cron 记录里只有一条含糊的退出码,真正的错误原因和报警都丢了。
这个问题最坑的地方在于:它平时根本不会暴露。任务成功时不走异常分支,webhook 从不发,脚本里那行有问题的网络代码永远不会被执行。只有"任务失败"这种低频事件才会让这条隐藏路径跑起来,而恰恰是这种时刻你才最需要它工作。等于说,我建了一条只在出事的夜晚才会被点亮的路,但这条路本身是断的。
修法也不复杂。我在发送通知的函数里单独包了 try/except,报警本身失败就 fallback 写本地日志文件,不再让它在主流程里抛异常。同时给 cron 脚本显式 source 了 /etc/profile 来拿到 PATH 和代理变量。这两处改完,再手动掐断一次任务验证,报警终于能到了。
这段经历给我留下一个可复用的检查清单:第一,凡是"只在异常时才执行的代码路径",务必单独测,不能只在主流程 happy path 上验证;第二,cron 环境下环境变量极简,任何要联网的脚本都要显式处理代理和 PATH,别依赖交互式 shell 的默认值;第三,报警/回调这类旁路功能要能自身失败而不拖垮主流程,失败至少要落到本地可查的日志里。
一句话总结:你越是在关键时候依赖的那条路,越要提前把它单独演练一遍,别让"兜底代码"自己成为最大的坑。