最常用方式是except中调用traceback.format_exc()获取完整堆栈字符串,须在异常上下文有效时使用;print_exc()适合快速调试但需避免finally中误用;TracebackException适用于Python 3.10+定制化输出;logging.error(..., exc_info=True)是记录异常的最佳实践。

用traceback.format_exc()获取完整堆栈字符串
直接调用traceback.format_exc()是最常用、最轻量的方式,它会返回当前异常的完整堆栈字符串(含文件名、行号、函数名和错误类型),适合写入日志或调试输出。
注意:这个函数只能在except块内调用,且必须在异常上下文还有效时——也就是不能在except之外、或被del e后调用,否则返回空字符串。
- 在
except Exception as e:块里直接用,不要先print(e)再调,避免误以为e还在作用域里 - 如果捕获的是
BaseException(比如KeyboardInterrupt),format_exc()仍可用,但部分系统级异常可能不带 traceback - 返回值是
str,可直接传给logging.error()或写入文件,无需额外解码
用traceback.print_exc()直接打印到stderr
当只想快速看错在哪、不打算做后续处理时,traceback.print_exc()最省事——它等价于print(traceback.format_exc()),但默认输出到sys.stderr。
常见误用是把它放在finally块里,结果报No exception to re-raise;它必须在异常未被清除的except中调用。
立即学习“Python免费学习笔记(深入)”;
- 加参数
file=open('error.log', 'a')可重定向输出,但要注意文件打开模式和编码(推荐encoding='utf-8') - 参数
limit=2能限制只显示最内层2层调用,适合屏蔽无关的框架代码 - 不建议在生产日志中混用
print_exc()和logging,容易导致日志格式不统一、时间戳缺失
用traceback.TracebackException精细控制输出格式
Python 3.10+ 引入了traceback.TracebackException,适合需要定制化展示(比如过滤敏感路径、提取特定帧、转成 JSON)的场景。
它不依赖当前异常上下文,可以基于已捕获的exc_info构造,更可控也更安全。
- 先用
exc_info = sys.exc_info()拿到(type, value, tb)三元组,再传给TracebackException(*exc_info) -
.format()返回生成器,需用''.join(exc.format())转字符串;.stack是StackSummary对象,可遍历每帧的filename、lineno、name - 对
__cause__和__context__链式异常支持更好,chain=True(默认)会递归展开所有关联异常
记录到日志时别漏掉exc_info=True
用logging模块记录异常时,最简方式不是拼接字符串,而是传exc_info=True参数——它会自动调用traceback.format_exception()并格式化进日志行。
手动拼接str(e) + '\n' + traceback.format_exc()看似灵活,实则丢掉了日志处理器的格式能力(如颜色、结构化字段、异步写入)。
-
logger.error('请求失败', exc_info=True)会在日志末尾追加标准 traceback,且保持与%(asctime)s %(levelname)s等格式一致 - 若想只记录当前异常、不追溯
__cause__,可显式传exc_info=(type, value, tb),其中tb来自sys.exc_info()[2] - 在多线程/协程中,
exc_info=True仍是线程安全的,因为sys.exc_info()返回的是当前线程的异常上下文
真正容易被忽略的是异常发生时的上下文污染:比如在except里又抛出新异常,旧的tb就可能被覆盖;或者用raise new_exc from old_exc时没注意__traceback__是否被正确继承。这时候靠sys.exc_info()拿原始三元组,比依赖隐式上下文更可靠。


















