应设置sys.excepthook全局异常钩子捕获未处理异常,配合threading.excepthook处理子线程、asyncio.set_exception_handler监听协程任务异常,并在日志中使用exc_info=true和上下文信息确保告警可追溯。

Python 异常没被 try 住,进程就挂了怎么办
线上服务里,一个没捕获的 ValueError 或 KeyError 就可能让整个 worker 进程退出,连日志都来不及打。这不是靠写得更“严谨”能解决的,得靠兜底机制。
最直接有效的是设置全局异常钩子:sys.excepthook。它会在未被捕获的异常冒泡到解释器顶层时触发,比 try/except 更底层,也更可靠。
-
sys.excepthook不会拦截SystemExit、KeyboardInterrupt,这点要心里有数 - 不能在多线程里用它捕获子线程异常——子线程需单独设
threading.excepthook(Python 3.8+) - 别在 hook 里做耗时操作(比如发 HTTP 告警),容易卡死解释器;建议只记日志 + 触发异步告警队列
import sys
def global_exception_handler(exc_type, exc_value, exc_traceback):
logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
alert_queue.put({"type": "critical", "error": str(exc_value)})
sys.excepthook = global_exception_handler
告警太频繁,logging 配置错在哪
刚加完告警,发现每秒几十条 ConnectionResetError 刷屏,不是代码有问题,是日志级别和过滤没对上。
关键不在“要不要记录”,而在“什么条件才值得告警”。高频但可恢复的异常(如短时网络抖动)不该进告警通道。
立即学习“Python免费学习笔记(深入)”;
- 用
logging.Filter子类实现白名单/频率限制,比如 5 分钟内同类型异常只告警一次 -
logger.warning()和logger.error()都会进 error 级别日志,但告警逻辑应只响应error及以上 - 避免在
except块里无差别调logger.exception()—— 它会强制打印 traceback,而有些业务异常(如参数校验失败)根本不需要栈信息
asyncio 任务崩溃静默消失,怎么捞
用 asyncio.create_task() 启的协程,一旦抛异常又没 await,就会被丢进黑洞:不报错、不记录、不告警。
根本原因是 asyncio 把未处理的 task 异常压进了 asyncio.Task 对象内部,除非显式检查或监听,否则永远不浮现。
- 启动任务时务必用
asyncio.create_task(..., name="xxx"),方便后续排查 - 全局监听:给
asyncio.get_event_loop()绑set_exception_handler,专门处理未 await 的 task 异常 - 别依赖
try/except包裹create_task()调用本身——它只捕获创建过程的错误,不捕获 task 内部执行的异常
def task_exception_handler(loop, context):
if "exception" in context:
logger.error("Task exception", exc_info=context["exception"])
else:
logger.error("Async task error", extra=context)
loop.set_exception_handler(task_exception_handler)
告警内容只有 "Exception",查不到根因
很多告警消息就一行 "Exception: something went wrong",点开日志发现 traceback 被截断,或者根本没带上上下文变量。
异常信息的价值不在“发生了什么”,而在“发生时周围是什么状态”。空泛的提示等于没告警。
- 自定义异常类时,重写
__str__,把关键字段(如user_id、order_id)拼进去,而不是只抛原生异常 - 在
except块里补全上下文:用logger.error("Failed to process %s", item_id, exc_info=True),别只写logger.error(str(e)) - 避免用
raise Exception("xxx"),优先用语义明确的异常类型(ValidationError、RateLimitExceeded),告警系统可据此分流
兜底和告警不是加一层装饰就能生效的事——异常路径越隐蔽,越要提前想好它从哪来、往哪去、带什么走。漏掉一个 sys.excepthook,或少写一个 exc_info=True,就可能让一次故障多拖十分钟。










