在处理另一个异常时,我应该如何处理记录器抛出的异常?

我正在处理一个 PHP 项目,在该项目中我使用 Monolog 捕获异常并记录错误并返回一个用户友好的页面作为响应。


该项目目前处于初期阶段,所以我只是StreamHandler在公众无法访问的应用程序目录中使用 Monolog 的类将错误记录到文件中,随着我的进步,我意识到如果出现某种 IO 错误,这可能会失败等等我还将登录到数据库(可能是 ElasticSearch)并通过电子邮件向管理员发送严重错误。


当我使用 时StreamHandler,我可以看到它在无法打开文件时抛出异常。


现在,我应该如何处理这种异常情况,如果日志记录机制本身失败,我应该如何记录它?


我可以让另一个记录器处理异常,该记录器在这种危急情况下发送电子邮件,但同样,我如何处理邮件程序抛出的异常?


我认为页面会充满太多的 try-catch 块,并且记录器散布在整个页面中,这看起来非常难看。


是否有一种优雅、干净的解决方案,它不涉及在大型项目中使用的太多嵌套的 try-catch 块?(也欢迎不受欢迎的意见)


这里有一些代码供参考:


try

{

    $routes = require_once(__DIR__.'/Routes.php');


    $router = new RouteFactory($routes, $request, \Skletter\View\ErrorPages::class);

    $router->buildPaths('Skletter\Controller\\', 'Skletter\View\\');


    $app = new Application($injector);

    $app->run($request, $router);

}

catch (InjectionException | InvalidErrorPage | NoHandlerSpecifiedException $e)

{

    $log = new Logger('Resolution');

    try

    {

        $log->pushHandler(new StreamHandler(__DIR__ . '/../app/logs/error.log', Logger::CRITICAL));

        $log->addCritical($e->getMessage(),

            array(

                'Stack Trace' => $e->getTraceAsString()

            ));

    }

    catch (Exception $e)

    {

        echo "No access to log file: ". $e->getMessage();

        // Should I handle this exception by pushing to db or emailing?

        // Can possibly introduce another nested try-catch block 

    }

    finally

    {

        /**

         * @var \Skletter\View\ErrorPageView $errorPage

         */

        $errorPage = $injector->make(\Skletter\View\ErrorPages::class);

        $errorPage->internalError($request)->send();

    }

}


呼啦一阵风
浏览 194回答 3
3回答

有只小跳蛙

记录异常和通知它们是整个项目必须全局解决的两个任务。它们不应该用帮助try-catch块来解决,因为通常try-catch应该使用帮助块来尝试解决产生异常的具体本地定位问题(例如,修改数据或尝试重复执行)或执行操作以恢复应用状态。关于异常的日志记录和通知是应该使用全局异常处理程序解决的任务。PHP 有一个本地机制来配置一个带有set_exception_handler函数的异常处理程序。例如:function handle_exception(Exception $exception){    //do something, for example, store an exception to log file}set_exception_handler('handle_exception');配置处理程序后,所有抛出的异常都将使用handle_exception()函数进行处理。例如:function handle_exception(Exception $exception){    echo $exception->getMessage();}set_exception_handler('handle_exception');// some codethrow Exception('Some error was happened');此外,您始终可以使用帮助restore_exception_handler函数禁用当前异常处理程序。在您的情况下,您可以创建一个简单的异常处理程序类,该类将包含日志记录方法和通知方法,并实现一种机制来处理将选择必要方法的异常。例如:class ExceptionHandler{        /**     * Store an exception into a log file              * @param Exception $exception the exception that'll be sent     */    protected function storeToLog(Exception $exception)    {}    /**     * Send an exception to the email address     * @param Exception $exception the exception that'll be sent     */    protected function sendToEmail(Exception $exception)    {}    /**     * Do some other actions with an exception     * @param Exception $exception the exception that'll be handled     */    protected function doSomething(Exception $exception)    {}    /**     * Handle an exception     * @param Exception $exception the exception that'll be handled     */    public function handle(Exception $exception)    {        try {            // try to store the exception to log file            $this->storeToLog($exception);        } catch (Exception $exception) {            try {                // if the exception wasn't stored to log file                 // then try to send the exception via email                $this->sendToEmail($exception);            } catch (Exception $exception) {                // if the exception wasn't stored to log file                 // and wasn't send via email                 // then try to do something else                $this->doSomething($exception);            }        }    }}之后,您可以注册此处理程序$handler = new ExceptionHandler();set_exception_handler([$handler, 'handle']);$routes = require_once(__DIR__.'/Routes.php');$router = new RouteFactory($routes, $request, \Skletter\View\ErrorPages::class);$router->buildPaths('Skletter\Controller\\', 'Skletter\View\\');$app = new Application($injector);$app->run($request, $router);

UYOU

如果我是你,我会在“kernel.exception”事件中捕获该异常,原因很简单:你的记录器无处不在。只需编写一个监听器,测试类似的东西if ($e instanceof MONOLOG_SPECIFIC_EXCEPTION) {   // handle exception here}如果您也使用命令,请对“console.exception”事件执行相同的操作。

喵喔喔

您可以尝试一些解决方案。您可以将记录器包装在您自己的另一个类中,处理那里的所有异常和可能的错误,并使用您的类进行日志记录。有时无法捕获所有错误,异常未得到处理并且发生极少数情况(即 IO 错误),您可以决定接受它。在这种情况下,您可以使用您自己提出的解决方案。使用 ELK 包,您将能够根据您感兴趣的参数配置手表监视器。
打开App,查看更多内容
随时随地看视频慕课网APP