Python 异常栈跟踪与调试:traceback、cgitb 及 logging 模块详解
本文详细解析 Python 中处理异常栈跟踪的多种方法。从基础的 traceback 模块入手,讲解 print_exc 与 print_exception 的区别及 sys.exc_info() 的用法;接着介绍 cgitb 模块如何通过覆盖 excepthook 简化开发期调试;最后阐述在 logging 模块中正确记录异常栈信息的规范做法,帮助开发者提升 Python 程序调试效率。
Python 异常栈跟踪基础
1. Python 中的异常栈跟踪
在 Java 开发中,异常对象默认包含 stacktrace 信息,通过 printStackTrace() 和 getStackTrace() 等方法即可获取栈信息,便于打印日志辅助调试。然而,Python 2.x 中异常对象可以是任意类型,常见代码直接 raise 字符串,导致无法像 Java 那样方便地获取异常栈,因为异常对象与栈信息是分离的。多数 Python 书籍侧重于构造异常对象及 try/except/finally 的使用,对调试关键的 stacktrace 涉及较少。
Python 中处理异常栈的核心模块是 traceback,它提供了 print_exception、format_exception 等常用工具函数用于输出异常栈信息。
def func(a, b):
return a / b
if __name__ == '__main__':
import sys
import traceback
try:
func(1, 0)
except Exception as e:
print "print exc"
traceback.print_exc(file=sys.stdout)
输出结果示例如下:
print exc Traceback (most recent call last): File "./teststacktrace.py", line 7, in <module> func(1, 0) File "./teststacktrace.py", line 2, in func return a / b
实际上,traceback.print_exc() 是 traceback.print_exception() 的简写形式,二者获取异常数据均通过 sys.exc_info() 函数实现。
def func(a, b):
return a / b
if __name__ == '__main__':
import sys
import traceback
try:
func(1, 0)
except Exception as e:
print "print_exception()"
exc_type, exc_value, exc_tb = sys.exc_info()
print 'the exc type is:', exc_type
print 'the exc value is:', exc_value
print 'the exc tb is:', exc_tb
traceback.print_exception(exc_type, exc_value, exc_tb)
输出结果示例如下:
print_exception() the exc type is: <type 'exceptions.ZeroDivisionError'> the exc value is: integer division or modulo by zero the exc tb is: <traceback object at 0x104e7d4d0> Traceback (most recent call last): File "./teststacktrace.py", line 7, in <module> func(1, 0) File "./teststacktrace.py", line 2, in func return a / b ZeroDivisionError: integer division or modulo by zero
sys.exc_info() 返回一个元组,包含三个元素:exc_type(异常类型)、exc_value(异常值)和 exc_tb(traceback 对象,包含出错行号、位置等数据)。print_exception 函数负责整理并输出这些数据。traceback 模块还提供 extract_tb 函数,用于更详细地解析 traceback 对象中的数据。
def func(a, b):
return a / b
if __name__ == '__main__':
import sys
import traceback
try:
func(1, 0)
except:
_, _, exc_tb = sys.exc_info()
for filename, linenum, funcname, source in traceback.extract_tb(exc_tb):
print "%-23s:%s '%s' in %s()" % (filename, linenum, source, funcname)
输出结果示例如下:
samchimac:tracebacktest samchi$ python ./teststacktrace.py ./teststacktrace.py :7 'func(1, 0)' in <module>() ./teststacktrace.py :2 'return a / b' in func()
使用 cgitb 简化调试
2. 使用 cgitb 来简化异常调试
若习惯通过日志调试,常遇到信息不足需手动添加 debug log 的情况,调试完再移除。Python 库中的 cgitb 模块可简化此过程,它能输出异常上下文中所有相关变量的信息,无需手动添加 debug log。
cgitb 的使用非常简洁:
def func(a, b):
return a / b
if __name__ == '__main__':
import cgitb
cgitb.enable(format='text')
import sys
import traceback
func(1, 0)
运行后将得到详细数据:
A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred.
/Users/samchi/Documents/workspace/tracebacktest/teststacktrace.py in <module>() 4 import cgitb 5 cgitb.enable(format='text') 6 import sys 7 import traceback 8 func(1, 0) func = <function func>
/Users/samchi/Documents/workspace/tracebacktest/teststacktrace.py in func(a=1, b=0) 2 return a / b 3 if __name__ == '__main__': 4 import cgitb 5 cgitb.enable(format='text') 6 import sys a = 1 b = 0
无需再手动 log.debug("a=%d" % a)。个人建议 cgitb 不适合线上环境,但非常适合开发调试,非常方便。cgitb 之所以能获取详细出错信息,是因为它覆盖了默认的 sys.excepthook 函数。sys.excepthook 是一个全局异常拦截器,可尝试自行修改它:
def func(a, b):
return a / b
def my_exception_handler(exc_type, exc_value, exc_tb):
print "i caught the exception:", exc_type
while exc_tb:
print "the line no:", exc_tb.tb_lineno
print "the frame locals:", exc_tb.tb_frame.f_locals
exc_tb = exc_tb.tb_next
if __name__ == '__main__':
import sys
sys.excepthook = my_exception_handler
import traceback
func(1, 0)
输出结果示例如下:
i caught the exception: <type 'exceptions.ZeroDivisionError'> the line no: 14 the frame locals: {'my_exception_handler': <function my_exception_handler at 0x100e04aa0>, '__builtins__': <module '__builtin____' (built-in)>, '__file__': './teststacktrace.py', 'traceback': <module 'traceback' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/traceback.pyc'>, '__package__': None, 'sys': <module 'sys' (built-in)>, 'func': <function func at 0x100e04320>, '__name__': '__main__', '__doc__': None} the line no: 2 the frame locals: {'a': 1, 'b': 0}
可见,这并无神奇之处,只是从 stack frame 对象中获取了相关变量的值。frame 对象还有其他属性,此处不再一一探索。
使用 logging 记录异常
3. 使用 logging 模块来记录异常
在 Java 中,使用 log4j 记录异常只需将 Exception 对象传递给 log.error 方法。但在 Python 中,直接传递异常对象给 log.error 仅会在日志中显示一行异常对象的值。
在 Python 中记录日志的正确方式如下:
logging.exception(ex)
logging.error(ex, exc_info=1) # 指名输出栈踪迹, logging.exception的内部也是包了一层此做法
logging.critical(ex, exc_info=1) # 更加严重的错误级别
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













