Python 中如何记录未捕获的异常?
Python 记录未捕获异常:设置 sys.excepthook 自定义全局异常处理器,将异常信息写入日志。threading.excepthook 处理子线程异常。本文给出完整实现。
核心:覆盖 sys.excepthook 钩子函数,在其中调用 logger 记录异常详情。这样即使代码没有 try-except,崩溃前也会留下完整的错误日志。
完整实现
import sys
import logging
logging.basicConfig(filename='error.log', level=logging.DEBUG)
logger = logging.getLogger(__name__)
def handle_unhandled_exception(exc_type, exc_value, exc_traceback):
if issubclass(exc_type, KeyboardInterrupt):
# Ctrl+C 不记录,走默认行为
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
logger.critical("未捕获的异常", exc_info=(exc_type, exc_value, exc_traceback))
sys.excepthook = handle_unhandled_exception
# 测试:这行代码的异常会被记录到 error.log
raise ValueError("这是一个测试异常")
子线程的异常
sys.excepthook 只捕获主线程的未处理异常。Python 3.8+ 对子线程有独立的钩子:
import threading
def handle_thread_exception(args):
logger.error(f"线程 {args.thread.name} 异常", exc_info=(args.exc_type, args.exc_value, args.exc_traceback))
threading.excepthook = handle_thread_exception
装饰器方式(函数级)
对特定函数记录异常:
import functools
def log_exceptions(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception:
logger.exception(f"{func.__name__} 执行出错")
raise
return wrapper
@log_exceptions
def process_data():
...
观测云对照
生产环境推荐用观测云 APM 的 Python 探针,自动捕获未处理异常并关联到请求 Trace,包含堆栈和上下文,不需要手动设置钩子。
常见问题(FAQ)
Q:excepthook 里记录完程序会退出吗?
A:会。excepthook 在异常导致程序终止前被调用,你只能在其中记录日志,无法阻止退出。
Q:logging.exception() 和 logging.error(exc_info=True) 有什么区别?
A:logging.exception() 自动附带当前异常的堆栈(必须在 except 块中调用),等价于 logging.error(..., exc_info=True)。
Q:异步代码(asyncio)的异常呢?
A:asyncio 有自己的异常处理:loop.set_exception_handler() 或在协程中加 try-except。未处理的 Task 异常在事件循环关闭时才报告。