捕获异常变量详细信息-cgitb模块
1.概述
当我们捕获异常时正常输出的异常上下文信息只有调用的模块、类、方法、如果想查看异常时变量值是看不到的。我们只有debug程序才能找到异常原因。
使用cgitb模块能够输出发生异常时所有信息,包括程序执行每个步骤变量信息,方便我们从异常信息中分析出原因。省去了debug过程,提高排查问题的效率。
2.cgitb模块使用
2.1.开启详细异常信息追踪
1.默认Python输出的异常信息
Python 的默认异常处理器会把错误发生位置的保准错误输出流打印出来。基本上也包含了足够的信息让我们理解为什么会引起错误以及随后修复它。
def func2(a, divisor):
return a / divisor
def func1(a, b):
c = b - 5
return func2(a, c)
func1(1, 5)
运行之后会有一个不容易发现错误出现原因异常信息,这里只能看到异常原因是division by zero。但是我们想知道它是如何发生的,就分析不到了因为我们看不到程序每一步运行时divisor变量的值,需要我们在程序中debug查找出错原因。
Traceback (most recent call last):
File "cgitb_basic_traceback.py", line 18, in <module>
func1(1, 5)
File "cgitb_basic_traceback.py", line 16, in func1
return func2(a, c)
File "cgitb_basic_traceback.py", line 11, in func2
return a / divisor
ZeroDivisionError: division by zero
2.开启详细异常信息
如果某函数或方法包含大量的注释,空格或其他无关代码导致它的上下文很长。默认的 5 行上下文可能就不够提供足够指示。所以显示在屏幕上之后很可能没有足够的上下文来让我们理解错误的发生。我们可以给 cgitb 指定更多的上下文值来解决这个问题。给 enable() 的 context 指定一个整数即可控制显示出来的上下文代码量。
我们设置为 12 之后就发现 self.a 和 self.b 也牵扯其中。
import cgitb
cgitb.enable(format='text', context=12)
class BrokenClass:
"""This class has an error.
"""
def __init__(self, a, b):
"""Be careful passing arguments in here.
"""
self.a = a
self.b = b
self.c = self.a * self.b
# Really
# long
# comment
# goes
# here.
self.d = self.a / self.b
return
o = BrokenClass(1, 0)
运行结果
python3 cgitb_with_classes.py
ZeroDivisionError
Python 3.6.4: .../bin/python3
Sun Mar 18 16:20:19 2018
A problem occurred in a Python script. Here is the sequence of
function calls leading up to the error, in the order they
occurred.
.../cgitb_with_classes.py in <module>()
21 self.a = a
22 self.b = b
23 self.c = self.a * self.b
24 # Really
25 # long
26 # comment
27 # goes
28 # here.
29 self.d = self.a / self.b
30 return
31
32 o = BrokenClass(1, 0)
o undefined
BrokenClass = <class '__main__.BrokenClass'>
.../cgitb_with_classes.py in
__init__(self=<__main__.BrokenClass object>, a=1, b=0)
21 self.a = a
22 self.b = b
23 self.c = self.a * self.b
24 # Really
25 # long
26 # comment
27 # goes
28 # here.
29 self.d = self.a / self.b
30 return
31
32 o = BrokenClass(1, 0)
self = <__main__.BrokenClass object>
self.d undefined
self.a = 1
self.b = 0
ZeroDivisionError: division by zero
__cause__ = None
__class__ = <class 'ZeroDivisionError'>
__context__ = None
__delattr__ = <method-wrapper '__delattr__' of
ZeroDivisionError object>
__dict__ = {}
__dir__ = <built-in method __dir__ of ZeroDivisionError
object>
__doc__ = 'Second argument to a division or modulo operation
was zero.'
__eq__ = <method-wrapper '__eq__' of ZeroDivisionError
object>
__format__ = <built-in method __format__ of
ZeroDivisionError object>
__ge__ = <method-wrapper '__ge__' of ZeroDivisionError
object>
__getattribute__ = <method-wrapper '__getattribute__' of
ZeroDivisionError object>
__gt__ = <method-wrapper '__gt__' of ZeroDivisionError
object>
__hash__ = <method-wrapper '__hash__' of ZeroDivisionError
object>
__init__ = <method-wrapper '__init__' of ZeroDivisionError
object>
__init_subclass__ = <built-in method __init_subclass__ of
type object>
__le__ = <method-wrapper '__le__' of ZeroDivisionError
object>
__lt__ = <method-wrapper '__lt__' of ZeroDivisionError
object>
__ne__ = <method-wrapper '__ne__' of ZeroDivisionError
object>
__new__ = <built-in method __new__ of type object>
__reduce__ = <built-in method __reduce__ of
ZeroDivisionError object>
__reduce_ex__ = <built-in method __reduce_ex__ of
ZeroDivisionError object>
__repr__ = <method-wrapper '__repr__' of ZeroDivisionError
object>
__setattr__ = <method-wrapper '__setattr__' of
ZeroDivisionError object>
__setstate__ = <built-in method __setstate__ of
ZeroDivisionError object>
__sizeof__ = <built-in method __sizeof__ of
ZeroDivisionError object>
__str__ = <method-wrapper '__str__' of ZeroDivisionError
object>
__subclasshook__ = <built-in method __subclasshook__ of type
object>
__suppress_context__ = False
__traceback__ = <traceback object>
args = ('division by zero',)
with_traceback = <built-in method with_traceback of
ZeroDivisionError object>
The above is a description of an error in a Python program.
Here is
the original traceback:
Traceback (most recent call last):
File "cgitb_with_classes.py", line 32, in <module>
o = BrokenClass(1, 0)
File "cgitb_with_classes.py", line 29, in __init__
self.d = self.a / self.b
ZeroDivisionError: division by zero
2.2.记录 Traceback
大多数场景中,打印出标准错误的追踪详情是很好的解决方式。不过在生产系统中,把它记录下来更加棒一些。enable() 函数有一个 logdir 参数,可以指定错误的记录目录。指定了目录后,每条异常都会被记录到给定的目录中。
import os
LOGDIR = os.path.join(os.path.dirname(__file__), 'LOGS')
if not os.path.exists(LOGDIR):
os.makedirs(LOGDIR)
cgitb.enable(
logdir=LOGDIR,
display=False,
format='text',
)
def func(a, divisor):
return a / divisor
func(1, 0)python
运行结果
<p>A problem occurred in a Python script.
.../LOGS/tmpq7icvee3.txt contains the description of this error.
$ ls LOGS
tmpq7icvee3.txt
$ cat LOGS/*.txt
ZeroDivisionError
Python 3.6.4: .../bin/python3
Sun Mar 18 16:20:19 2018
A problem occurred in a Python script. Here is the sequence of
function calls leading up to the error, in the order they
occurred.
.../cgitb_log_exception.py in <module>()
24
25 def func(a, divisor):
26 return a / divisor
27
28 func(1, 0)
func = <function func>
.../cgitb_log_exception.py in func(a=1, divisor=0)
24
25 def func(a, divisor):
26 return a / divisor
27
28 func(1, 0)
a = 1
divisor = 0
ZeroDivisionError: division by zero
__cause__ = None
__class__ = <class 'ZeroDivisionError'>
__context__ = None
__delattr__ = <method-wrapper '__delattr__' of
ZeroDivisionError object>
__dict__ = {}
__dir__ = <built-in method __dir__ of ZeroDivisionError
object>
__doc__ = 'Second argument to a division or modulo operation
was zero.'
__eq__ = <method-wrapper '__eq__' of ZeroDivisionError
object>
__format__ = <built-in method __format__ of
ZeroDivisionError object>
__ge__ = <method-wrapper '__ge__' of ZeroDivisionError
object>
__getattribute__ = <method-wrapper '__getattribute__' of
ZeroDivisionError object>
__gt__ = <method-wrapper '__gt__' of ZeroDivisionError
object>
__hash__ = <method-wrapper '__hash__' of ZeroDivisionError
object>
__init__ = <method-wrapper '__init__' of ZeroDivisionError
object>
__init_subclass__ = <built-in method __init_subclass__ of
type object>
__le__ = <method-wrapper '__le__' of ZeroDivisionError
object>
__lt__ = <method-wrapper '__lt__' of ZeroDivisionError
object>
__ne__ = <method-wrapper '__ne__' of ZeroDivisionError
object>
__new__ = <built-in method __new__ of type object>
__reduce__ = <built-in method __reduce__ of
ZeroDivisionError object>
__reduce_ex__ = <built-in method __reduce_ex__ of
ZeroDivisionError object>
__repr__ = <method-wrapper '__repr__' of ZeroDivisionError
object>
__setattr__ = <method-wrapper '__setattr__' of
ZeroDivisionError object>
__setstate__ = <built-in method __setstate__ of
ZeroDivisionError object>
__sizeof__ = <built-in method __sizeof__ of
ZeroDivisionError object>
__str__ = <method-wrapper '__str__' of ZeroDivisionError
object>
__subclasshook__ = <built-in method __subclasshook__ of type
object>
__suppress_context__ = False
__traceback__ = <traceback object>
args = ('division by zero',)
with_traceback = <built-in method with_traceback of
ZeroDivisionError object>
The above is a description of an error in a Python program.
Here is
the original traceback:
Traceback (most recent call last):
File "cgitb_log_exception.py", line 28, in <module>
func(1, 0)
File "cgitb_log_exception.py", line 26, in func
return a / divisor
ZeroDivisionError: division by zero