关于语言不可知:仅在生产中捕获异常是一个好主意吗?

Is it a good idea to only catch exceptions in production?

我继承了一个代码库,其中包含以下代码(注意:示例代码是php):

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

因此,从本质上讲,代码正在捕获异常。记录它,并且无提示地失败(除了日志消息)。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
        }
    }

}

然后可以这样使用:

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

此代码用于特殊情况下的"测试"环境,它调用传入函数而不进行异常处理(因此任何异常都将保持未处理状态)。在每个其他环境(例如生产环境)中,它将传入的闭包包装在生产环境中的try-catch块中,其行为与代码最初的行为相同。

这个解决方案解决了我的问题,但它似乎有点老土,让我对人工智能有一种不好的感觉。

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


不要试图在例外情况下重新发明轮子。有一个而且只有一个场景,在这个场景中,您应该排除一个例外:

如果你有一个可供选择的计划,那就抓住一个例外。

异常意味着您的代码遇到了一个异常情况,在这种情况下,它无法继续工作,除了放弃它别无选择。这是放弃函数/模块/执行上下文并向调用方发出更高级别的信号的一个非常好的方法。例外就是这样。

在开发过程中,您希望看到异常在所有丑陋的荣耀中都能够调试。在生产中,您不希望用户看到异常,而是向他们展示一个漂亮的错误屏幕和/或发出各种各样的铃声和哨声,通知管理员/开发人员/CTO/任何人。

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

您真正应该编写try..catch的唯一时间是,是否存在子系统可能失败的合理原因以及您是否有备份计划。例如。:

1
2
3
4
5
6
7
8
9
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下载是一个预期的结果,可以很好地处理异常,因此这是一个很好的用例。但即使它们不能代表预期的结果,也不要自反地试图抓住它们。