搞清楚 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 相关文章推荐
python实现从网络下载文件并获得文件大小及类型的方法
Apr 28 Python
python使用PIL模块实现给图片打水印的方法
May 22 Python
python使用clear方法清除字典内全部数据实例
Jul 11 Python
浅谈python中set使用
Jun 30 Python
python的re正则表达式实例代码
Jan 24 Python
django 发送邮件和缓存的实现代码
Jul 18 Python
使用Python进行目录的对比方法
Nov 01 Python
python 类之间的参数传递方式
Dec 20 Python
python基于property()函数定义属性
Jan 22 Python
Jupyter notebook如何修改平台字体
May 13 Python
Python logging模块进行封装实现原理解析
Aug 07 Python
Pycharm创建python文件自动添加日期作者等信息(步骤详解)
Feb 03 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
PHP iconv 解决utf-8和gb2312编码转换问题
2010/04/12 PHP
Zend Framework中的简单工厂模式 图文
2012/07/10 PHP
php绘图中显示不出图片的原因及解决
2014/03/05 PHP
PHP中使用json数据格式定义字面量对象的方法
2014/08/20 PHP
PHP实现动态web服务器方法
2015/07/29 PHP
Yii使用migrate命令执行sql语句的方法
2016/03/15 PHP
PHP.vs.JAVA
2016/04/29 PHP
PHP数组函数知识汇总
2016/05/12 PHP
js 剪切板的用法(clipboardData.setData)与js match函数介绍
2013/11/19 Javascript
js中函数调用的两种常用方法使用介绍
2014/07/17 Javascript
JavaScript中length属性的使用方法
2015/06/05 Javascript
jQuery根据元素值删除数组元素的方法
2015/06/24 Javascript
jquery判断checkbox是否选中及改变checkbox状态的实现方法
2016/05/26 Javascript
有关文件上传 非ajax提交 得到后台数据问题
2016/10/12 Javascript
Bootstrap基本样式学习笔记之表格(2)
2016/12/07 Javascript
JavaScript继承与聚合实例详解
2019/01/22 Javascript
nodejs微信开发之授权登录+获取用户信息
2019/03/17 NodeJs
说说如何使用Vuex进行状态管理(小结)
2019/04/14 Javascript
vue.js实现只能输入数字的输入框
2019/10/19 Javascript
详解Vue.js 响应接口
2020/07/04 Javascript
如何准确判断请求是搜索引擎爬虫(蜘蛛)发出的请求
2015/10/13 Python
Python中的os.path路径模块中的操作方法总结
2016/07/07 Python
Python即时网络爬虫项目启动说明详解
2018/02/23 Python
Python函数参数分类原理详解
2020/05/28 Python
python matplotlib绘制三维图的示例
2020/09/24 Python
python smtplib发送多个email联系人的实现
2020/10/09 Python
网上常见的一份Linux面试题(多项选择部分)
2014/09/09 面试题
送给程序员的20个Java集合面试问题
2014/08/06 面试题
新年寄语大全
2014/04/12 职场文书
自我管理的活动方案
2014/08/25 职场文书
讲党性心得体会
2014/09/03 职场文书
礼貌问候语大全
2015/11/10 职场文书
大队委员竞选演讲稿
2015/11/20 职场文书
创业计划书之酒厂
2019/10/14 职场文书
MySQL COUNT函数的使用与优化
2021/05/10 MySQL
python热力图实现的完整实例
2022/06/25 Python