仅在生产中捕获异常是一个好主意吗?

时间:2015-07-22 10:42:21

标签: exception-handling language-agnostic

我继承了一个包含如下代码的代码库(注意:示例代码是PHP):

try {
    // Do something which doesn't intentionally throw exceptions.
} catch (\Exception $e) {
    $this->log->log($e->getMessage());
    $this->product->setError($e->getMessage());
    return false;
}

基本上,代码正在捕获异常。记录它,以及静默失败(除了日志消息)。

这种行为似乎在生产中有意义,但使开发变得更加困难(因为必须在日志文件中查找堆栈跟踪,而不是将其打印到控制台)。所以我想出了以下功能:

private function tryCatch ($func) {

    // Bind closure, so that $this allows it to access class properties
    if (is_object($func) && ($func instanceof Closure)) {
        \Closure::bind($func, $this, "static");
    }

    if (\App::environment('test')) {
        return $func();
    } else {
        try {
            return $func();
        } catch (\Exception $e) {
            $this->log->log($e->getMessage());
            $this->product->setError($e->getMessage());

            return false;
        }
    }

}

然后可以这样使用:

$this->tryCatch(function () {
    // Do something
});

此代码特殊情况下的'test'环境,它调用传入函数而不进行异常处理(因此任何异常都保持未处理状态)。在每个其他环境(例如生产环境)中,它将生成的try-catch块中的传入闭包包装起来,就像代码最初的行为一样。

这个解决方案解决了我的问题,但它看起来有点hacky,让我感到有点唠叨,感觉这不是一个好主意。

我的问题:我有什么理由不这样做吗?或者有更好的方法来处理这种情况吗?

1 个答案:

答案 0 :(得分:8)

不要试图重新发明关于异常的轮子。只有一种情况应该catch例外:

如果您有其他计划如何处理,则抓住例外

异常意味着您的代码遇到了一个异常情况,它无法继续工作,别无选择,只能放弃。这是放弃函数/模块/执行上下文并向调用者发出更高信号的完美好方法。这正是例外所做的事情。

在开发期间,您希望能够在其所有丑陋的荣耀中看到异常,以便能够进行调试。在生产中,您希望您的用户看到异常,而是向他们展示一个漂亮的错误屏幕和/或有各种各样的花里胡哨,这通知管理员/开发人员/ CTO /谁。

这意味着,在生产中,您只需要一个全局错误处理程序,如果发生意外的,未捕获的异常,它将作出相应的响应。应该抛出异常并且(不)在开发中完全捕获,您不需要两个完全独立的代码路径。可以通过一些引导脚本有条件地使用set_exception_handler来设置此全局错误处理程序;或者甚至更好,您适当地配置您的Web服务器以提供有用的错误页面。配置Web服务器是最好的方法,因为这是一个特定于系统的设置(仅限生产),无需更改任何有关代码的内容。

实际编写try..catch的唯一时间是,如果子系统可能出现故障且您有备份计划的合理原因。 E.g:

try {
    $file = download_file_from_url($url);
    echo "Cool, got your file.";
} catch (HttpNotFound $e) {
    echo "Hey user, that file doesn't exist.";
} catch (HttpEmptyResponse $e) {
    echo "Hey user, that file seems empty.";
}
..

在这种情况下,失败的HTTP下载是预期结果,可以很好地处理异常,因此这是一个很好的用例。但是,即使他们没有代表期望的结果,也不要反复尝试抓住所有人