logger.exception不打印堆栈是因为它必须在except块内调用以获取sys.exc_info()中的异常上下文,否则退化为普通error;未捕获异常需通过替换sys.excepthook实现自动记录。

为什么 logger.exception 不打印堆栈,只输出一行日志?
因为 logger.exception 必须在异常处理上下文中(即 except 块内)调用,它依赖 Python 的 sys.exc_info() 获取当前异常上下文。如果在 try/except 外调用,或异常已被清除(比如手动调用了 sys.exc_clear()),logger.exception 就只会当作普通 logger.error 用,不带 traceback。
常见错误写法:
try:
1 / 0
except ZeroDivisionError:
pass # 异常被吞掉,没记录
logger.exception("出错了") # ❌ 此时已无活跃异常,无堆栈
正确做法是紧接在 except 内调用:
try:
1 / 0
except ZeroDivisionError:
logger.exception("除零错误") # ✅ 自动附加当前异常的完整 traceback
如何让未捕获异常(如主线程崩溃)也自动进 logging?
Python 默认把未处理异常输出到 sys.stderr,不会触发任何 logger。要让它进 logging,必须替换全局异常钩子:sys.excepthook。
实操建议:
- 定义一个符合签名
func(type, value, traceback)的函数 - 在函数里调用
logger.critical或logger.error,并用traceback.format_exception()拼出字符串 - 最后别忘了调用原钩子(可选,用于保留默认 stderr 输出)
示例:
import sys
import logging
import traceback
<p>logger = logging.getLogger(<strong>name</strong>)
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)</p><p>def handle_uncaught_exception(exc_type, exc_value, exc_traceback):
if issubclass(exc_type, KeyboardInterrupt):
sys.<strong>excepthook</strong>(exc_type, exc_value, exc_traceback)
return
logger.critical("未捕获异常", exc_info=(exc_type, exc_value, exc_traceback))</p><p>sys.excepthook = handle_uncaught_exception</p>
logger.exception 和 logger.error(..., exc_info=True) 有啥区别?
本质完全一样:logger.exception(msg) 是 logger.error(msg, exc_info=True) 的语法糖,两者都强制使用当前异常上下文。
但要注意三点:
-
logger.exception会**强制将日志级别设为 ERROR**,不能传level=CRITICAL等参数 -
exc_info=True在logger.error中更灵活——你可以在非except块中显式传入异常三元组,比如logger.error("msg", exc_info=(t, v, tb)) - 若你已在
except块中捕获了异常并想记录其他信息(如请求 ID、用户 ID),推荐用logger.error("msg", exc_info=True, extra={"req_id": "abc"}),exception方法不支持extra参数(除非重载)
配置 root logger 后,logger.exception 还不输出堆栈?检查这三处
即使代码写对,也可能因配置问题看不到 traceback。重点排查:
-
logger实例是否设置了propagate=False,且其 handler 没有绑定 formatter?没有 formatter 时,exc_info字段不会被渲染 - handler 的
level是否 >= ERROR?比如 handler 设了level=logging.WARNING,那 ERROR 日志就被过滤掉了 - 是否误用了
basicConfig多次?第二次调用会失效(它只在 logger 未配置时生效),导致 handler 实际没加到 root logger 上
最稳妥的初始化方式(避免 basicConfig 干扰):
logging.getLogger().setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
logging.getLogger().addHandler(handler)
未处理异常的自动捕获和 exception 的堆栈输出,核心不在“怎么写”,而在“在哪写”和“谁在管”。尤其注意 sys.excepthook 替换时机、handler 的 level 与 formatter 绑定状态——这些地方一漏,日志就静默丢堆栈。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











