搞清楚 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连接池实现示例程序
Nov 26 Python
Python队列的定义与使用方法示例
Jun 24 Python
python实现分页效果
Oct 25 Python
Python实现定时备份mysql数据库并把备份数据库邮件发送
Mar 08 Python
朴素贝叶斯分类算法原理与Python实现与使用方法案例
Jun 26 Python
Python中__slots__属性介绍与基本使用方法
Sep 05 Python
对Python Pexpect 模块的使用说明详解
Feb 14 Python
python实现智能语音天气预报
Dec 02 Python
使用Python paramiko模块利用多线程实现ssh并发执行操作
Dec 05 Python
新手学python应该下哪个版本
Jun 11 Python
python属于解释型语言么
Jun 15 Python
学会Python数据可视化必须尝试这7个库
Jun 16 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 Session变量不能传送到下一页的解决方法
2009/11/27 PHP
php获取随机数组列表的方法
2014/11/13 PHP
Ajax 数据请求的简单分析
2011/04/05 Javascript
jquery中dom操作和事件的实例学习-表单验证
2011/11/30 Javascript
jquery中插件实现自动添加用户的具体代码
2013/11/15 Javascript
JS获取文件大小方法小结
2015/12/08 Javascript
原生js获取元素样式的简单方法
2016/08/06 Javascript
es7学习教程之Decorators(修饰器)详解
2017/07/21 Javascript
js学习总结之DOM2兼容处理this问题的解决方法
2017/07/27 Javascript
详解基于iview-ui的导航栏路径(面包屑)配置
2019/02/22 Javascript
vue spa应用中的路由缓存问题与解决方案
2019/05/31 Javascript
js设置默认时间跨度过程详解
2019/07/17 Javascript
echarts.js 动态生成多个图表 使用vue封装组件操作
2020/07/19 Javascript
浅谈es6中的元编程
2020/12/01 Javascript
js实现圆形菜单选择器
2020/12/03 Javascript
python查看zip包中文件及大小的方法
2015/07/09 Python
python采用django框架实现支付宝即时到帐接口
2016/05/17 Python
Windows下python3.7安装教程
2018/07/31 Python
Python图像处理之简单画板实现方法示例
2018/08/30 Python
使用python实现离散时间傅里叶变换的方法
2019/09/02 Python
把vgg-face.mat权重迁移到pytorch模型示例
2019/12/27 Python
如何使用python实现模拟鼠标点击
2020/01/06 Python
pytorch 实现删除tensor中的指定行列
2020/01/13 Python
python GUI库图形界面开发之PyQt5工具栏控件QToolBar的详细使用方法与实例
2020/02/28 Python
Python try except else使用详解
2021/01/12 Python
recorder.js 基于Html5录音功能的实现
2020/05/26 HTML / CSS
马来西亚与新加坡长途巴士售票网站:BusOnlineTicket.com
2018/11/05 全球购物
String和StringBuffer的区别
2015/08/13 面试题
求职信范文英文版
2014/01/05 职场文书
小学三年级数学教学反思
2014/01/31 职场文书
学习两会精神心得范文
2014/03/17 职场文书
红色故事演讲稿
2014/05/22 职场文书
浪漫婚礼主题活动策划方案
2014/09/15 职场文书
法学专业毕业实习自我鉴定2014
2014/09/27 职场文书
2014年幼儿园老师工作总结
2014/12/05 职场文书
mysql分组后合并显示一个字段的多条数据方式
2022/01/22 MySQL