搞清楚 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 self,cls,decorator的理解
Jul 13 Python
简单谈谈Python中的反转字符串问题
Oct 24 Python
解读python logging模块的使用方法
Apr 17 Python
PyQt5实现从主窗口打开子窗口的方法
Jun 19 Python
python爬虫解决验证码的思路及示例
Aug 01 Python
Python使用字典实现的简单记事本功能示例
Aug 15 Python
python 实现绘制整齐的表格
Nov 18 Python
python实现引用其他路径包里面的模块
Mar 09 Python
jupyter notebook 调用环境中的Keras或者pytorch教程
Apr 14 Python
Django中文件上传和文件访问微项目的方法
Apr 27 Python
Python2.x与3​​.x版本有哪些区别
Jul 09 Python
pytorch简介
Nov 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
JS与PHP向函数传递可变参数的区别实例代码
2011/05/18 PHP
php实现图片添加水印功能
2014/02/13 PHP
php可应用于面包屑导航的迭代寻找家谱树实现方法
2015/02/02 PHP
php array_walk 对数组中的每个元素应用用户自定义函数详解
2016/11/18 PHP
php intval函数用法总结
2019/04/14 PHP
Jquery ui css framework
2010/06/28 Javascript
ASP.NET中基于JQUERY的高性能的TreeView补充
2011/02/23 Javascript
基于jquery的多功能软键盘插件
2012/07/25 Javascript
JS函数实现动态添加CSS样式表文件
2012/12/15 Javascript
利用jQuery实现可输入搜索文字的下拉框
2013/10/23 Javascript
jquery中$(#form :input)与$(#form input)的区别
2014/08/18 Javascript
javascript定义变量时加var与不加var的区别
2014/12/22 Javascript
实现前后端数据交互方法汇总
2015/04/07 Javascript
js中window.open的参数及注意注意事项
2016/07/06 Javascript
jQuery实现可拖动进度条实例代码
2017/06/21 jQuery
Vue2.x Todo之自定义指令实现自动聚焦的方法
2019/01/08 Javascript
vue组件间通信六种方式(总结篇)
2019/05/15 Javascript
教你搭建按需加载的Vue组件库(小结)
2019/07/29 Javascript
javascript事件监听与事件委托实例详解
2019/08/16 Javascript
JS数组方法slice()用法实例分析
2020/01/18 Javascript
[44:39]2014 DOTA2国际邀请赛中国区预选赛 NE VS CNB
2014/05/21 DOTA
Python解决N阶台阶走法问题的方法分析
2017/12/28 Python
python 按不同维度求和,最值,均值的实例
2018/06/28 Python
浅析python的优势和不足之处
2018/11/20 Python
详解Django-auth-ldap 配置方法
2018/12/10 Python
使用Python操作FTP实现上传和下载的方法
2019/04/01 Python
python3.4中清屏的处理方法
2020/07/06 Python
Python识别处理照片中的条形码
2020/11/16 Python
idealfit英国:世界领先的女性健身用品和运动衣物品牌
2017/11/25 全球购物
校班主任推荐信范文
2013/12/03 职场文书
给客户的道歉信
2014/01/13 职场文书
银行委托书范本
2014/04/04 职场文书
工地质量标语
2014/06/12 职场文书
开发房地产协议书
2014/09/14 职场文书
2016年感恩教师节活动总结
2016/04/01 职场文书
2019财务毕业实习报告
2019/06/27 职场文书