搞清楚 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从MP3文件获取id3的方法
Jun 15 Python
Python简单连接MongoDB数据库的方法
Mar 15 Python
win系统下为Python3.5安装flask-mongoengine 库
Dec 20 Python
Python实现压缩文件夹与解压缩zip文件的方法
Sep 01 Python
Win10下python 2.7.13 安装配置方法图文教程
Sep 18 Python
padas 生成excel 增加sheet表的实例
Dec 11 Python
Python多进程写入同一文件的方法
Jan 14 Python
感知器基础原理及python实现过程详解
Sep 30 Python
numpy.ndarray 实现对特定行或列取值
Dec 05 Python
Python+Selenium+phantomjs实现网页模拟登录和截图功能(windows环境)
Dec 11 Python
Django-migrate报错问题解决方案
Apr 21 Python
Python脚本调试工具安装过程
Jan 11 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通过header实现文本文件下载的代码
2010/08/08 PHP
php数组转换js数组操作及json_encode的用法详解
2013/10/26 PHP
PHP PDO fetch 模式各种参数的输出结果一览
2015/01/07 PHP
Yii数据读取与跳转参数传递用法实例分析
2016/07/12 PHP
PHP操作redis实现的分页列表,新增,删除功能封装类与用法示例
2018/08/04 PHP
不用写JS也能使用EXTJS视频演示
2008/12/29 Javascript
jquery实现简单的表单验证
2015/11/17 Javascript
Vue.js动态添加、删除选题的实例代码
2016/09/30 Javascript
Js动态设置rem来实现移动端字体的自适应代码
2016/10/14 Javascript
10道典型的JavaScript面试题
2017/03/22 Javascript
微信小程序教程系列之视图层的条件渲染(10)
2017/04/19 Javascript
vue2实现搜索结果中的搜索关键字高亮的代码
2018/08/29 Javascript
小程序实现授权登陆的解决方案
2018/12/02 Javascript
vscode配置vue下的es6规范自动格式化详解
2019/03/20 Javascript
原生JavaScript之es6中Class的用法分析
2020/02/23 Javascript
javascript设计模式 ? 代理模式原理与用法实例分析
2020/04/16 Javascript
javascript中call,apply,bind的区别详解
2020/12/11 Javascript
[49:12]完美世界DOTA2联赛PWL S2 Magma vs GXR 第二场 11.29
2020/12/02 DOTA
python迭代dict的key和value的方法
2018/07/06 Python
Python 2.7中文显示与处理方法
2018/07/16 Python
使用Python抓取豆瓣影评数据的方法
2018/10/17 Python
使用Template格式化Python字符串的方法
2019/01/22 Python
pandas 对日期类型数据的处理方法详解
2019/08/08 Python
Python Request爬取seo.chinaz.com百度权重网站的查询结果过程解析
2019/08/13 Python
Python 识别12306图片验证码物品的实现示例
2020/01/20 Python
python网络编程:socketserver的基本使用方法实例分析
2020/04/09 Python
意大利比基尼品牌:MISS BIKINI
2019/11/02 全球购物
实习生自荐信范文分享
2013/11/27 职场文书
职业规划书如何设计?
2014/01/09 职场文书
施工质量承诺书范文
2014/05/30 职场文书
小平您好观后感
2015/06/09 职场文书
广播体操比赛主持词
2015/06/29 职场文书
python opencv通过4坐标剪裁图片
2021/06/05 Python
Redis Cluster 集群搭建你会吗
2021/08/04 Redis
教你部署vue项目到docker
2022/04/05 Vue.js
我国拿下天问一号火星着陆区附近 22 个地理实体命名:平乐、西柏坡、古田、漠河等
2022/04/29 数码科技