首页 文章

获取导致异常的异常描述和堆栈跟踪,全部作为字符串

提问于
浏览
304

我在Python中看到了很多关于堆栈跟踪和异常的帖子 . 但还没找到我需要的东西 .

我有一大堆Python 2.7代码可能引发异常 . 我想 grab 它并分配给 string 它的完整描述和导致错误的堆栈跟踪(我们只是在控制台上看到的所有内容) . 我需要这个字符串将其打印到GUI中的文本框 .

像这样的东西:

try:
    method_that_can_raise_an_exception(params)
except Exception as e:
    print_to_textbox(complete_exception_description(e))

问题是: what is the function complete_exception_description?

8 回答

  • 6

    使用Python 3,以下代码将使用 traceback.format_exc() 完全按照格式获取 Exception 对象:

    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__)))
    

    优点是只需要 Exception 对象(由于记录的 __traceback__ 属性),因此可以更容易地作为参数传递给另一个函数以进行进一步处理 .

  • 23

    请参阅 traceback 模块,特别是 format_exc() 函数 . Here .

    import traceback
    
    try:
        raise ValueError
    except ValueError:
        tb = traceback.format_exc()
    else:
        tb = "No error"
    finally:
        print tb
    
  • 0
    >>> import sys
    >>> import traceback
    >>> try:
    ...   5 / 0
    ... except ZeroDivisionError, e:
    ...   type_, value_, traceback_ = sys.exc_info()
    >>> traceback.format_tb(traceback_)
    ['  File "<stdin>", line 2, in <module>\n']
    >>> 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() 收集 traceback 模块中的信息和函数以对其进行格式化 . Here是格式化它的一些示例 .

    整个异常字符串位于:

    >>> ex = traceback.format_exception(type_, value_, traceback_)
    >>> ex
    ['Traceback (most recent call last):\n', '  File "<stdin>", line 2, in <module>\n', 'ZeroDivisionError: integer division or modulo by zero\n']
    
  • 32

    获取导致异常的异常描述和堆栈跟踪,全部作为字符串

    要创建一个相当复杂的堆栈跟踪来演示我们获得完整的堆栈跟踪:

    def raise_error():
        raise RuntimeError('something bad happened!')
    
    def do_something_that_might_error():
        raise_error()
    

    记录完整的堆栈跟踪

    最佳做法是为模块设置 Logger . 它将知道模块的名称并能够更改级别(以及其他属性,例如处理程序)

    import logging
    logging.basicConfig(level=logging.DEBUG)
    logger = logging.getLogger(__name__)
    

    我们可以使用此 Logger 来获取错误:

    try:
        do_something_that_might_error()
    except Exception as error:
        logger.exception(error)
    

    哪些日志:

    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!
    

    所以我们得到的输出与出错时的输出相同:

    >>> 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!
    

    获取字符串

    如果你真的只想要字符串,请使用 traceback.format_exc 函数,演示如何在此处记录字符串:

    import traceback
    try:
        do_something_that_might_error()
    except Exception as error:
        just_the_string = traceback.format_exc()
        logger.debug(just_the_string)
    

    哪些日志:

    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!
    
  • 5

    对于那些使用 Python-3 的人

    使用 traceback 模块和 exception.__traceback__ 可以提取堆栈跟踪,如下所示:

    • 使用 traceback.extract_stack() 抓取 current 堆栈跟踪

    • 删除最后三个元素(因为那些是堆栈中的条目让我进入我的调试功能)

    • 使用 traceback.extract_tb() 从异常对象追加 __traceback__

    • 使用 traceback.format_list() 格式化全部内容

    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) + '\n  {} {}'.format(excp.__class__,excp)
    

    一个简单的演示:

    def foo():
        try:
            something_invalid()
        except Exception as e:
            print(exception_to_string(e))
    
    def bar():
        return foo()
    

    当我们调用 bar() 时,我们得到以下输出:

    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
    
  • 30

    您也可以考虑使用内置的Python模块cgitb来获得一些非常好的,格式良好的异常信息,包括局部变量值,源代码上下文,函数参数等 .

    比如这个代码......

    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)
    

    我们得到这个异常输出......

    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
    
  • 0

    我的2美分:

    import sys, traceback
    try: 
      ...
    except Exception, e:
      T, V, TB = sys.exc_info()
      print ''.join(traceback.format_exception(T,V,TB))
    
  • 455

    我定义了以下助手类:

    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 = "\n".join(traceback.format_exception(etype, value, tb))
            return False
          return True
    

    以后我可以这样使用:

    with TracedExeptions():
      #some-code-which-might-throw-any-exception
    

    以后可以像这样使用它:

    def log_err(ex):
      if hasattr(ex, 'traceString'):
        print("ERROR:{}".format(ex.traceString));
      else:
        print("ERROR:{}".format(ex));
    

    (背景:由于将 PromiseException 一起使用,我很沮丧,不幸的是,它将一个地方引发的异常传递给另一个地方的on_rejected处理程序,因此很难从原始位置获取回溯)

相关问题