logging.exception()是记录异常堆栈的首选方法,必须在except块中调用,自动捕获当前异常并输出完整traceback;若在except外需延迟记录,则显式使用exc_info=True或exc_info元组,并确保日志格式含%(exc_text)s。

logging.exception() 是写入异常堆栈的首选方法
直接调用 logging.exception() 是最简单也最可靠的方式——它自动捕获当前异常(即 sys.exc_info()),并把消息和完整堆栈跟踪(traceback)一起写入日志,级别固定为 ERROR。它必须在 except 块中使用,否则会记录空 traceback。
常见错误是误用 logging.error() 并手动传入 exc_info=True,虽然可行,但多写参数且易遗漏;更糟的是用 logging.error(str(e)),这只会记录异常字符串,完全丢失堆栈。
示例:
try:
1 / 0
except ZeroDivisionError:
logging.exception("除零操作发生")
输出包含:时间、级别、消息、“Traceback (most recent call last):”及完整堆栈。
在 except 外记录最近一次异常要用 exc_info=True
如果异常已被捕获并处理,但你想延迟记录(比如统一兜底或异步上报),就不能再用 logging.exception(),而要显式传参 exc_info=True 给 logging.error() 或其他级别函数。
注意:此时必须确保当前上下文仍有活跃异常(即刚从 except 块出来),否则 exc_info=True 会记录 None 或旧异常。若中间穿插了其他函数调用,推荐显式保存:exc_info = sys.exc_info(),然后传 exc_info=exc_info。
要点:
-
exc_info=True依赖 Python 的隐式异常上下文,跨函数容易丢失 - 不加
exc_info参数时,logging.error()永远不会打印堆栈 -
exc_info=(type, value, traceback)元组形式可用于重放特定异常信息
自定义格式需保留 %(exc_text)s 才能显示堆栈
如果你改过日志格式(比如用 logging.basicConfig(format=...) 或 Formatter),默认的 %(message)s 不会包含堆栈;必须显式加入 %(exc_text)s 占位符,否则 exception() 或 exc_info=True 产生的堆栈会被静默丢弃。
典型错误配置:
logging.basicConfig(format="%(asctime)s %(levelname)s %(message)s") # → 堆栈不会出现
正确写法:
logging.basicConfig(format="%(asctime)s %(levelname)s %(message)s %(exc_text)s")
注意:%(exc_text)s 在无异常时为空字符串,不影响正常日志;但它不能被省略,也不能替换成 %(stack_info)s(后者是源码上下文,非 traceback)。
多线程/协程中 exception() 仍安全,但别混用 logger 实例
logging.exception() 在多线程下是线程安全的,每个线程的 sys.exc_info() 独立,不会串扰。但如果你为不同线程创建了独立的 Logger 实例并各自配置 handler,要确认它们共享同一 formatter(尤其含 %(exc_text)s),否则可能部分线程不输出堆栈。
协程(asyncio)场景需格外小心:异常可能发生在不同 task 中,sys.exc_info() 在 await 切换后已失效。此时应捕获后立即记录,或用 loop.set_exception_handler() 配合 logging.error(..., exc_info=True) 显式传入异常对象。
容易忽略的一点:handler 的 stream(如 sys.stderr)本身不是线程安全的,但 logging 模块内部已加锁,无需额外同步——除非你绕过 logging 直接写文件或网络流。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











