捕获所有异常的缺点(在最高程序级别,之后重新抛出,仅为记录而退出?)

5 投票
3 回答
751 浏览
提问于 2025-04-16 00:51

我在一台远程机器上运行了一个长时间的程序,想确保两件事:(1) 如果程序因为某个异常而终止,我能记录下这个异常;(2) 如果程序终止了,有人能收到通知。有人觉得我现在用的方法有什么缺点吗?或者有没有更好的建议?

我看过Python的文档和很多关于异常的帖子,明白了使用笼统的except语句通常是不好的。在子程序和模块中,我总是用except来处理特定的、预期中的异常,但在程序的最高层有一个“兜底”的except语句似乎很有用,这样我可以在程序退出之前记录下异常。

你觉得怎么样?

import traceback
try:
    # main program code here
except BaseException:
    tb = traceback.format_exc()
    msg = "Exiting program due to exception:" + tb
    LogToFile(msg)         # custom logging function
    SendAlertEmail(msg)    # warn admin that program terminated
    raise                  # program exits with the existing exception

注意,我使用的是BaseException而不是Exception,因为如果有人在终端按下Ctrl-C,我想把这个作为程序退出的原因记录下来(并提醒管理员程序已经退出)。不过我想我也可以使用:

except Exception, KeyboardInterrupt:

3 个回答

0

“我看过Python的文档,也读了很多关于异常处理的帖子,明白了使用笼统的except语句通常不是个好主意。”

这样做的原因是,你可能只预料到三种异常,但最后却忽视了第四种你从没想到过的异常。如果你的处理程序会无条件地重新抛出异常,这样就可以避免这个问题——你会看到那些意想不到的异常——所以这其实是对一般规则的一个合理例外。

4

想象一下,如果出现错误是因为磁盘空间不够。如果日志记录也写在同一个分区上

LogToFile(msg)

就会引发一个错误,这样就连邮件也发不出去。只要在每个地方加上简单的尝试/捕获,就可以避免这个问题

tb = traceback.format_exc()
msg = "Exiting program due to exception:" + tb
try:
    LogToFile(msg)         # custom logging function
except:
    pass
try:
    SendAlertEmail(msg)    # warn admin that program terminated
except:
    pass
raise                  # program exits with the existing exception
7

其实没有什么特别的缺点,但有一个很棒的替代方案——sys.excepthook

在你用的这个版本中,可以考虑使用简单的 except:,然后用 sys.exc_info() 来获取异常信息;这样可以确保你能捕捉到所有的异常——即使在一些奇怪的情况下,有些模块抛出的异常不是 BaseException 的子类。比如:

>>> class X: pass
... 
>>> raise X
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.X: <__main__.X instance at 0xc9ad0>

正如你看到的,确实有可能抛出一些 except BaseException: 捕捉不到的异常——这就是为什么简单的 except: 仍然存在(特别是为了像你这样的特殊用途!)。

无论你是使用这个钩子,还是自己实现一个,考虑一下(也许根据配置选项或环境设置)不要把所有细节都告诉最终用户(这样可以提升用户体验!),只给他们一个有意义的总结(让用户放心,所有问题的细节都已经记录下来了,等等)。

撰写回答