Get exception description and stack trace which caused an exception, all as a string
我在Python中看到了很多关于堆栈跟踪和异常的帖子。 但还没找到我需要的东西。
我有一大堆Python 2.7代码可能引发异常。 我想抓住它并将一个字符串分配给它的完整描述和导致错误的堆栈跟踪(我们只是在控制台上看到的所有内容)。 我需要这个字符串将其打印到GUI中的文本框中。
像这样的东西:
1 2 3 4 | try: method_that_can_raise_an_exception(params) except Exception as e: print_to_textbox(complete_exception_description(e)) |
问题是:函数
请参见
1 2 3 4 5 6 7 8 9 10 | import traceback try: raise ValueError except ValueError: tb = traceback.format_exc() else: tb ="No error" finally: print tb |
让我们创建一个相当复杂的堆栈跟踪,以证明我们获得了完整的堆栈跟踪:
1 2 3 4 5 | def raise_error(): raise RuntimeError('something bad happened!') def do_something_that_might_error(): raise_error() |
记录完整的堆栈跟踪
最佳做法是为模块设置记录器。它将知道模块的名称并能够更改级别(以及其他属性,例如处理程序)
1 2 3 | import logging logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) |
我们可以使用此记录器来获取错误:
1 2 3 4 | try: do_something_that_might_error() except Exception as error: logger.exception(error) |
哪些日志:
1 2 3 4 5 6 | ERROR:__main__:something bad happened! Traceback (most recent call last): File"<stdin>", line 2, in <module> File"<stdin>", line 2, in do_something_that_might_error File"<stdin>", line 2, in raise_error RuntimeError: something bad happened! |
所以我们得到的输出与出错时的输出相同:
1 2 3 4 5 6 | >>> do_something_that_might_error() Traceback (most recent call last): File"<stdin>", line 1, in <module> File"<stdin>", line 2, in do_something_that_might_error File"<stdin>", line 2, in raise_error RuntimeError: something bad happened! |
只获得字符串
如果你真的只想要字符串,请使用
1 2 3 4 5 6 | import traceback try: do_something_that_might_error() except Exception as error: just_the_string = traceback.format_exc() logger.debug(just_the_string) |
哪些日志:
1 2 3 4 5 | DEBUG:__main__:Traceback (most recent call last): File"<stdin>", line 2, in <module> File"<stdin>", line 2, in do_something_that_might_error File"<stdin>", line 2, in raise_error RuntimeError: something bad happened! |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | >>> import sys >>> import traceback >>> try: ... 5 / 0 ... except ZeroDivisionError as e: ... type_, value_, traceback_ = sys.exc_info() >>> traceback.format_tb(traceback_) [' File"<stdin>", line 2, in <module> '] >>> value_ ZeroDivisionError('integer division or modulo by zero',) >>> type_ <type 'exceptions.ZeroDivisionError'> >>> >>> 5 / 0 Traceback (most recent call last): File"<stdin>", line 1, in <module> ZeroDivisionError: integer division or modulo by zero |
您可以使用sys.exc_info()来收集
以下是一些格式化示例。
整个异常字符串位于:
1 2 3 4 5 6 | >>> ex = traceback.format_exception(type_, value_, traceback_) >>> ex ['Traceback (most recent call last): ', ' File"<stdin>", line 2, in <module> ', 'ZeroDivisionError: integer division or modulo by zero '] |
使用Python 3,以下代码将格式化
1 2 3 4 5 6 | import traceback try: method_that_can_raise_an_exception(params) except Exception as ex: print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__))) |
优点是只需要
对于那些使用Python-3的人
使用
-
使用
traceback.extract_stack() 获取当前堆栈跟踪 - 删除最后三个元素(因为这些是堆栈中的条目,让我进入我的调试功能)
-
使用
traceback.extract_tb() 从异常对象追加__traceback__ -
使用
traceback.format_list() 格式化整个事物
1 2 3 4 5 6 | import traceback def exception_to_string(excp): stack = traceback.extract_stack()[:-3] + traceback.extract_tb(excp.__traceback__) # add limit=?? pretty = traceback.format_list(stack) return ''.join(pretty) + ' {} {}'.format(excp.__class__,excp) |
一个简单的演示:
1 2 3 4 5 6 7 8 | def foo(): try: something_invalid() except Exception as e: print(exception_to_string(e)) def bar(): return foo() |
当我们调用
1 2 3 4 5 6 7 8 | File"./test.py", line 57, in <module> bar() File"./test.py", line 55, in bar return foo() File"./test.py", line 50, in foo something_invalid() <class 'NameError'> name 'something_invalid' is not defined |
您还可以考虑使用内置的Python模块cgitb来获取一些非常好的,格式良好的异常信息,包括局部变量值,源代码上下文,函数参数等。
例如这个代码......
1 2 3 4 5 6 7 8 9 10 11 | import cgitb cgitb.enable(format='text') def func2(a, divisor): return a / divisor def func1(a, b): c = b - 5 return func2(a, c) func1(1, 5) |
我们得到这个异常输出......
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | ZeroDivisionError Python 3.4.2: C:\tools\python\python.exe Tue Sep 22 15:29:33 2015 A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred. c:\TEMP\cgittest2.py in <module>() 7 def func1(a, b): 8 c = b - 5 9 return func2(a, c) 10 11 func1(1, 5) func1 = <function func1> c:\TEMP\cgittest2.py in func1(a=1, b=5) 7 def func1(a, b): 8 c = b - 5 9 return func2(a, c) 10 11 func1(1, 5) global func2 = <function func2> a = 1 c = 0 c:\TEMP\cgittest2.py in func2(a=1, divisor=0) 3 4 def func2(a, divisor): 5 return a / divisor 6 7 def func1(a, b): 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> __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"cgittest2.py", line 11, in <module> func1(1, 5) File"cgittest2.py", line 9, in func1 return func2(a, c) File"cgittest2.py", line 5, in func2 return a / divisor ZeroDivisionError: division by zero |
如果您希望在未处理异常时获得相同的信息,您可以执行类似的操作。做
1 2 3 4 | try: ... except Exception as e: print(traceback.print_tb(e.__traceback__)) |
我正在使用Python 3.7。
我的2美分:
1 2 3 4 5 6 | import sys, traceback try: ... except Exception, e: T, V, TB = sys.exc_info() print ''.join(traceback.format_exception(T,V,TB)) |
我定义了以下助手类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import traceback class TracedExeptions(object): def __init__(self): pass def __enter__(self): pass def __exit__(self, etype, value, tb): if value : if not hasattr(value, 'traceString'): value.traceString =" ".join(traceback.format_exception(etype, value, tb)) return False return True |
以后我可以这样使用:
1 2 | with TracedExeptions(): #some-code-which-might-throw-any-exception |
以后可以像这样使用它:
1 2 3 4 5 | def log_err(ex): if hasattr(ex, 'traceString'): print("ERROR:{}".format(ex.traceString)); else: print("ERROR:{}".format(ex)); |
(背景:由于将