搞清楚 Python traceback的具体使用方法


Posted in Python onMay 13, 2019

1. Python中的异常栈跟踪

之前在做Java的时候,异常对象默认就包含stacktrace相关的信息,通过异常对象的相关方法printStackTrace()和getStackTrace()等方法就可以取到异常栈信息,能打印到log辅助调试或者做一些别的事情。但是到了Python,在2.x中,异常对象可以是任何对象,经常看到很多代码是直接raise一个字符串出来,因此就不能像Java那样方便的获取异常栈了,因为异常对象和异常栈是分开的。而多数Python语言的书籍上重点在于描述Python中如何构造异常对象和raise 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()

2. 使用cgitb来简化异常调试

如果平时开发喜欢基于log的方式来调试,那么可能经常去做这样的事情,在log里面发现异常之后,因为信息不足,那么会再去额外加一些debug log来把相关变量的值输出。调试完毕之后再把这些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为什么会这么?牛磕芑袢≌饷聪晗傅某龃硇畔ⅲ科涫邓?墓ぷ髟?硗??氖褂梅绞揭谎?募虻ィ??皇歉哺橇四?系?ys.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对象中还有很多神奇的属性,就不一一探索了。

3. 使用logging模块来记录异常

在使用Java的时候,用log4j记录异常很简单,只要把Exception对象传递给log.error方法就可以了,但是在Python中就不行了,如果直接传递异常对象给log.error,那么只会在log里面出现一行异常对象的值。

在Python中正确的记录Log方式应该是这样的:

logging.exception(ex)
logging.error(ex, exc_info=1) # 指名输出栈踪迹, logging.exception的内部也是包了一层此做法
logging.critical(ex, exc_info=1) # 更加严重的错误级别

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Windows系统配置python脚本开机启动的3种方法分享
Mar 10 Python
使用Python的urllib2模块处理url和图片的技巧两则
Feb 18 Python
新手如何快速入门Python(菜鸟必看篇)
Jun 10 Python
Python数据结构之顺序表的实现代码示例
Nov 15 Python
python增加矩阵维度的实例讲解
Apr 04 Python
pyqt5 lineEdit设置密码隐藏,删除lineEdit已输入的内容等属性方法
Jun 24 Python
Tensorflow 自定义loss的情况下初始化部分变量方式
Jan 06 Python
python同时遍历两个list用法说明
May 02 Python
Python基于Tkinter编写crc校验工具
May 06 Python
Python中如何添加自定义模块
Jun 09 Python
简单了解如何封装自己的Python包
Jul 08 Python
django使用多个数据库的方法实例
Mar 04 Python
Python3+OpenCV2实现图像的几何变换(平移、镜像、缩放、旋转、仿射)
May 13 #Python
Python 通过打码平台实现验证码的实现
May 13 #Python
利用python和百度地图API实现数据地图标注的方法
May 13 #Python
一篇文章彻底搞懂Python中可迭代(Iterable)、迭代器(Iterator)与生成器(Generator)的概念
May 13 #Python
为什么你还不懂得怎么使用Python协程
May 13 #Python
Python玩转加密的技巧【推荐】
May 13 #Python
11个Python3字典内置方法大全与示例汇总
May 13 #Python
You might like
星际争霸中的对战模式介绍
2020/03/04 星际争霸
PHP5.0正式发布 不完全兼容PHP4 新增多项功能
2006/10/09 PHP
PHP 多维数组排序(usort,uasort)
2010/06/30 PHP
PH P5.2至5.5、5.6的新增功能详解
2014/07/14 PHP
php使用glob函数快速查询指定目录文件的方法
2014/11/15 PHP
PHP中定义数组常量(array常量)的方法
2014/11/17 PHP
php中base64_decode与base64_encode加密解密函数实例
2014/11/24 PHP
PHP迭代与递归实现无限级分类
2017/08/28 PHP
javascript replace()正则替换实现代码
2010/02/26 Javascript
javascript中字符串拼接需注意的问题
2010/07/13 Javascript
js和as的稳定传值问题解决
2013/07/14 Javascript
fckeditor粘贴Word时弹出窗口取消的方法
2014/10/30 Javascript
Javascript中3个需要注意的运算符
2015/04/02 Javascript
JQuery实现超链接鼠标提示效果的方法
2015/06/10 Javascript
JS实现鼠标滑过链接改变网页背景颜色的方法
2015/10/20 Javascript
TypeScript入门-基本数据类型
2017/03/28 Javascript
利用JS做网页特效_大图轮播(实例讲解)
2017/08/09 Javascript
vue+vue-validator 表单验证功能的实现代码
2017/11/13 Javascript
vue实现城市列表选择功能
2018/07/16 Javascript
jquery实现动态添加附件功能
2018/10/23 jQuery
mongodb初始化并使用node.js实现mongodb操作封装方法
2019/04/02 Javascript
判断js数据类型的函数实例详解
2019/05/23 Javascript
js图片无缝滚动插件使用详解
2020/05/26 Javascript
keep-alive不能缓存多层级路由菜单问题解决
2020/03/10 Javascript
JS实现省市县三级下拉联动
2020/04/10 Javascript
JavaScript实现网页跨年倒计时
2020/12/02 Javascript
在Python下使用Txt2Html实现网页过滤代理的教程
2015/04/11 Python
Python中利用sorted()函数排序的简单教程
2015/04/27 Python
TensorFlow安装及jupyter notebook配置方法
2017/09/08 Python
Linux(Redhat)安装python3.6虚拟环境(推荐)
2018/05/05 Python
python opencv实现运动检测
2018/07/10 Python
python画折线图的程序
2018/07/26 Python
英国No.1体育用品零售商:SportsDirect.com
2019/10/16 全球购物
音乐器材管理制度
2014/01/31 职场文书
业绩考核岗位职责
2014/02/01 职场文书
领导个人查摆剖析材料
2014/10/29 职场文书