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

Nic*_*rns 2 language-agnostic exception-handling

我继承了一个代码库,其中包含如下代码(注意:示例代码是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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

然后可以像这样使用:

$this->tryCatch(function () {
    // Do something
});
Run Code Online (Sandbox Code Playgroud)

这段代码特殊地描述了它在没有异常处理的情况下调用函数传递的'test'环境(因此任何异常都保持未处理状态).在每个其他环境(例如生产)中,它将传入的闭包在生产中的try-catch块中包装,就像代码最初的行为一样.

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

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

dec*_*eze 8

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

如果您有替代计划如何处理它,请抓住例外.

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

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

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

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

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.";
}
..
Run Code Online (Sandbox Code Playgroud)

在这种情况下,失败的HTTP下载是预期的结果,并且可以很好地处理异常,因此这是一个很好的用例.但即使它们不代表预期的结果,也不要反思性地试图抓住它们.

  • “仅记录”应该由全局错误处理程序进行。如果这就是您所做的全部,那么您不应该针对这种特定情况手动执行此操作,而应该采取一些措施*始终记录所有错误*。仅当异常是“预期结果”并且您在这种情况下可以做其他事情时,才应该捕获该异常。如果 `$this->product->setError` 做了一些有用的事情来从异常中“恢复”,那么这是一个有效的用例。 (2认同)