首页 文章

如何在不停止程序的情况下打印完整的回溯?

提问于
浏览
543

我正在编写一个程序来解析10个网站,查找数据文件,保存文件,然后解析它们以生成可以在NumPy库中使用的数据 . 这个文件通过坏链接,格式不良的XML,缺少条目以及其他我尚未归类的东西会遇到 tons 错误 . 我最初制作这个程序来处理这样的错误:

try:
    do_stuff()
except:
    pass

但现在我想记录错误:

try:
    do_stuff()
except Exception, err:
    print Exception, err

请注意,这是打印到日志文件以供以后查看 . 这通常会打印非常无用的数据 . 我想要的是打印错误触发时打印的完全相同的行,没有try-except拦截异常,但我不希望它暂停我的程序,因为它嵌套在一系列for循环中,我想看完成了 .

10 回答

  • 2

    你想要traceback模块 . 它可以让你像Python一样打印堆栈转储 . 特别是,print_last函数将打印最后一个异常和堆栈跟踪 .

  • 567

    其他一些答案已经指出traceback模块 .

    请注意,对于 print_exc ,在某些极端情况下,您将无法获得您期望的结果 . 在Python 2.x中:

    import traceback
    
    try:
        raise TypeError("Oups!")
    except Exception, err:
        try:
            raise TypeError("Again !?!")
        except:
            pass
    
        traceback.print_exc()
    

    ...将显示最后一个异常的回溯:

    Traceback (most recent call last):
      File "e.py", line 7, in <module>
        raise TypeError("Again !?!")
    TypeError: Again !?!
    

    If you really need to access the original traceback 一个解决方案是缓存从局部变量exc_info返回的异常信息,并使用print_exception显示:

    import traceback
    import sys
    
    try:
        raise TypeError("Oups!")
    except Exception, err:
        try:
            exc_info = sys.exc_info()
    
            # do you usefull stuff here
            # (potentially raising an exception)
            try:
                raise TypeError("Again !?!")
            except:
                pass
            # end of useful stuff
    
    
        finally:
            # Display the *original* exception
            traceback.print_exception(*exc_info)
            del exc_info
    

    生产环境 :

    Traceback (most recent call last):
      File "t.py", line 6, in <module>
        raise TypeError("Oups!")
    TypeError: Oups!
    

    尽管如此,很少有陷阱:

    将回溯返回值分配给处理异常的函数中的局部变量将导致循环引用 . 这将阻止同一函数中的局部变量或回溯引用的任何内容被垃圾回收 . [...]如果你确实需要回溯,请确保在使用后删除它(最好用try ... finally语句完成)

    • 但是,来自同一个文档:

    从Python 2.2开始,当启用垃圾收集并且它们变得无法访问时,这些周期会自动回收,但是避免创建周期仍然更有效 .


    另一方面,通过允许您访问与异常关联的回溯,Python 3产生了一个不太令人惊讶的结果:

    import traceback
    
    try:
        raise TypeError("Oups!")
    except Exception as err:
        try:
            raise TypeError("Again !?!")
        except:
            pass
    
        traceback.print_tb(err.__traceback__)
    

    ...将显示:

    File "e3.py", line 4, in <module>
        raise TypeError("Oups!")
    
  • 179

    traceback.format_exc()sys.exc_info()会产生更多信息,如果这是你想要的 .

    import traceback
    import sys
    
    try:
        do_stuff()
    except Exception:
        print(traceback.format_exc())
        # or
        print(sys.exc_info()[0])
    
  • 2

    如果您正在调试并且只想查看当前堆栈跟踪,则只需调用:

    traceback.print_stack()

    没有必要手动引发异常只是为了再次捕获它 .

  • 6

    如何在不停止程序的情况下打印完整的回溯?

    如果您不想在出错时暂停程序,则需要使用try / except来处理该错误:

    try:
        do_something_that_might_error()
    except Exception as error:
        handle_the_error(error)
    

    要提取完整的回溯,我们将使用标准库中的 traceback 模块:

    import traceback
    

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

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

    印刷

    要打印完整的回溯,请使用 traceback.print_exc 方法:

    try:
        do_something_that_might_error()
    except Exception as error:
        traceback.print_exc()
    

    哪个印刷品:

    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!
    

    优于打印,记录:

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

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

    在这种情况下,您将需要 logger.exception 函数:

    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!
    

    或者你可能只想要字符串,在这种情况下,你需要 traceback.format_exc 函数代替:

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

    哪些日志:

    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!
    

    结论

    对于所有三个选项,我们看到输出与出错时相同:

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

    要获得精确的堆栈跟踪,作为一个字符串,如果没有try / except可以提升它,只需将它放在捕获有问题的异常的except块中 .

    desired_trace = traceback.format_exc(sys.exc_info())
    

    以下是如何使用它(假设 flaky_func 已定义, log 调用您最喜欢的日志记录系统):

    import traceback
    import sys
    
    try:
        flaky_func()
    except KeyboardInterrupt:
        raise
    except Exception:
        desired_trace = traceback.format_exc(sys.exc_info())
        log(desired_trace)
    

    grab 并重新提升 KeyboardInterrupt 是一个好主意,这样你仍然可以使用Ctrl-C杀死程序 . 记录超出了问题的范围,但一个很好的选择是logging . systraceback模块的文档 .

  • 368

    除了@Aaron Hall 's answer, if you are logging, but don' t想要使用 logging.exception() (因为它记录在ERROR级别),你可以使用较低级别并传递 exc_info=True . 例如

    try:
        do_something_that_might_error()
    except Exception:
        logger.info('General exception noted.', exc_info=True)
    
  • 5

    您需要将try / except放在最可能发生错误的内环中,即

    for i in something:
        for j in somethingelse:
            for k in whatever:
                try:
                    something_complex(i, j, k)
                except Exception, e:
                    print e
            try:
                something_less_complex(i, j)
            except Exception, e:
                print e
    

    ... 等等

    换句话说,您将需要在try / except中包含可能失败的语句可能,尽可能在最内圈 .

  • 0

    关于this answer评论的评论: print(traceback.format_exc()) 对我来说比 traceback.print_exc() 更好 . 对于后者, hello 有时奇怪"mixed"带有回溯文本,就像两个人都想同时写入stdout或stderr一样,产生奇怪的输出(至少在从文本编辑器内部构建并查看"Build results"中的输出时)面板) .

    回溯(最近一次调用最后一次):文件“C:\ Users \ User \ Desktop \ test.py”,第7行,在地狱do_stuff()文件“C:\ Users \ User \ Desktop \ test.py”,行4,在do_stuff 1/0中ZeroDivisionError:整数除法或以零为模o [以0.1s结束]

    所以我使用:

    import traceback, sys
    
    def do_stuff():
        1/0
    
    try:
        do_stuff()
    except Exception:
        print(traceback.format_exc())
        print('hello')
    
  • 65

    首先,不要使用 print 进行日志记录,有稳定,经过验证且经过深思熟虑的stdlib模块:logging . 你绝对应该使用它 .

    其次,当有本地和简单的方法时,不要试图搞乱无关的工具 . 就这个:

    log = logging.getLogger(__name__)
    
    try:
        call_code_that_fails()
    except MyError:
        log.exception('Any extra info you want to see in your logs')
    

    而已 . 你现在完成了 .

    任何对事情如何深入感兴趣的人的解释

    log.exception 做的实际上只是调用 log.error (也就是具有级别 ERROR 的日志事件)然后打印回溯 .

    为什么它更好?

    嗯,这里有一些注意事项:

    • 这是对的;

    • 很简单;

    • 很简单 .

    为什么没有人不应该使用treceback,也不应该用exc_info = True调用logger,也不能用sys.exc_info弄脏他/她的手?

    好吧,因为!它们都存在于不同的目的 . 例如, traceback.print_exc 的输出与解释器本身产生的回溯略有不同 . 如果你打算使用它,你会混淆任何会用你的日志敲打他的人 .

    传递 exc_info=True 来记录电话是不合适的 . 但是,当您捕获可恢复的错误并且想要使用回溯记录它们时(例如,使用,例如 INFO 级别),它非常有用,因为 log.exception 只生成一个级别的日志 - ERROR .

    而你绝对应该尽可能地避免弄乱 sys.exc_info . 它不是公共接口,它是内部接口 - 如果你肯定知道你在做什么,你可以使用它 . 它不仅用于打印异常 .

相关问题