搞清楚 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实现简单的TCP代理服务器
Oct 08 Python
Python中的异常处理简明介绍
Apr 13 Python
用Python给文本创立向量空间模型的教程
Apr 23 Python
Python 实现 贪吃蛇大作战 代码分享
Sep 07 Python
梯度下降法介绍及利用Python实现的方法示例
Jul 12 Python
详解python实现读取邮件数据并下载附件的实例
Aug 03 Python
python使用pyqt写带界面工具的示例代码
Oct 23 Python
使用PM2+nginx部署python项目的方法示例
Nov 07 Python
在Python中使用filter去除列表中值为假及空字符串的例子
Nov 18 Python
Python logging模块异步线程写日志实现过程解析
Jun 30 Python
Python绘制组合图的示例
Sep 18 Python
Python绘制词云图之可视化神器pyecharts的方法
Feb 23 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 应用程序安全防范技术研究
2009/09/25 PHP
PHP URL路由类实例
2013/11/12 PHP
浅谈Coreseek、Sphinx-for-chinaese、Sphinx+Scws的区别
2016/12/15 PHP
php伪静态验证码不显示的解决方案
2019/09/26 PHP
laravel实现Auth认证,登录、注册后的页面回跳方法
2019/09/30 PHP
一个可以兼容IE FF的加为首页与加入收藏实现代码
2009/11/02 Javascript
jquery form表单序列化为对象的示例代码
2014/03/05 Javascript
JavaScript File API实现文件上传预览
2016/02/02 Javascript
JS留言功能的简单实现案例(推荐)
2016/06/23 Javascript
JS获取input file绝对路径的方法(推荐)
2016/08/02 Javascript
js 颜色选择插件
2017/01/23 Javascript
使用jQuery卸载全部事件的思路详解
2017/04/03 jQuery
Vue的elementUI实现自定义主题方法
2018/02/23 Javascript
vue实现文件上传功能
2018/08/13 Javascript
微信小程序功能之全屏滚动效果的实现代码
2018/11/22 Javascript
微信小程序框架的页面布局代码
2019/08/17 Javascript
layui动态渲染生成select的option值方法
2019/09/23 Javascript
基于redis的小程序登录实现方法流程分析
2020/05/25 Javascript
vue实现图片裁剪后上传
2020/12/16 Vue.js
[51:36]Optic vs Newbee 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
Python中利用sqrt()方法进行平方根计算的教程
2015/05/15 Python
Python实现上下班抢个顺风单脚本
2018/02/07 Python
python多维数组切片方法
2018/04/13 Python
PyCharm-错误-找不到指定文件python.exe的解决方法
2019/07/01 Python
Python如何筛选序列中的元素的方法实现
2019/07/15 Python
Python在Matplotlib图中显示中文字体的操作方法
2019/07/29 Python
PyTorch的torch.cat用法
2020/06/28 Python
win10安装python3.6的常见问题
2020/07/01 Python
无需JS和jQuery代码实现CSS3鼠标浮动放大图片
2016/11/21 HTML / CSS
canvas 实现 github404动态效果的示例代码
2017/11/15 HTML / CSS
经济信息管理专业大学生求职信
2013/09/27 职场文书
函授本科毕业生自我鉴定
2013/10/16 职场文书
毕业晚会主持词
2014/03/24 职场文书
安全保卫工作竞聘材料
2014/08/25 职场文书
上手简单,功能强大的Python爬虫框架——feapder
2021/04/27 Python
redis三种高可用方式部署的实现
2021/05/11 Redis