生产环境异常必须触发告警动作而非仅打日志;需在except块中显式调用告警函数,区分ERROR/WARNING级别,全局excepthook仅作兜底,多线程、Celery、asyncio需独立配置告警入口,并实施告警收敛去重。

用 logging 捕获并告警异常,不是只打日志
Python 默认的异常堆栈只输出到 stderr,不记录、不通知、不分类,生产环境必须接管。关键不是“记录下来”,而是“触发告警动作”——比如发邮件、调企业微信 webhook、写入告警平台。
常见错误是把 logging.exception() 当成告警终点,其实它只是格式化了 traceback,没做任何外发动作。
- 在
except块里调用logging.error()或logging.exception()后,必须紧接着执行告警逻辑(如调用send_alert()) - 避免在日志 handler 里隐式发告警——比如自定义
Handler发邮件,容易阻塞主线程或重复告警 - 区分告警级别:
ERROR级异常要告警,WARNING级通常只记日志不告警,除非业务明确定义为需关注事件
sys.excepthook 全局兜底,但不能替代局部 try/except
未捕获异常(如主线程顶层抛出、线程内未处理异常)会走到 sys.excepthook。这是最后一道防线,但仅适合记录 + 基础通知,不适合做业务级响应。
典型误用:用它代替函数内的 try/except,结果异常发生时无法拿到上下文变量、无法重试、无法降级。
立即学习“Python免费学习笔记(深入)”;
使用模板与程序分离的方式构建,依靠专门设计的数据库操作类实现数据库存取,具有专有错误处理模块,通过 Email 实时报告数据库错误,除具有满足购物需要的全部功能外,成新商城购物系统还对购物系统体系做了丰富的扩展,全新设计的搜索功能,自定义成新商城购物系统代码功能代码已经全面优化,杜绝SQL注入漏洞前台测试用户名:admin密码:admin888后台管理员名:admin密码:admin888
- 设置前先保存原 hook:
original_hook = sys.excepthook,便于调试时临时恢复 - 在新 hook 中调用
logging.critical()记录完整 traceback,再调用轻量告警(如写文件 + 触发 systemd notify) - 多线程场景下,
threading.excepthook(Python 3.8+)也得单独设,否则子线程异常静默丢失
异步任务(Celery / asyncio)里的异常告警要独立配置
Celery 的 task_failure 信号、asyncio 的 asyncio.get_event_loop().set_exception_handler() 是两个常被忽略的告警入口。它们和主线程的 excepthook 互不影响。
比如 Celery 任务里抛了异常,但没配 @signals.task_failure.connect,就只会进 broker 的失败队列,不会触发你的告警逻辑。
- Celery:用
@signals.task_failure.connect拿到exc、task_id、args,避免在on_failure方法里重复序列化参数 - asyncio:
loop.set_exception_handler()的 handler 函数必须是同步的,不能await,告警调用要用loop.run_in_executor()包一层 - FastAPI 等框架的后台任务(
BackgroundTasks)不走 Celery,得靠手动try/except+ 显式告警
告警收敛与去重:别让一次故障刷屏 100 条
一个数据库连接超时,可能在 5 秒内触发 20 个请求异常,如果每条都发企微消息,运维会立刻 mute 群聊。
最简可行方案不是上 Prometheus + Alertmanager,而是加一层内存级计数 + 时间窗口。
- 用
functools.lru_cache(maxsize=128)缓存异常类型 + 关键参数哈希值,5 分钟内相同组合只告警一次 - 对高频异常(如
ConnectionError),先写入本地/tmp/alert_cooldown.db(用shelve),检查上次告警时间戳 - 避免用全局 dict 存状态——gunicorn 多 worker 下不共享,得换 Redis 或文件锁







