PHP中日志自定义异常的最佳实践

时间:2018-02-28 20:52:08

标签: php logging exception-handling custom-exceptions exception-logging

我有一个自定义异常(可能会在其他自定义异常中进一步扩展)。我的项目需要记录发生的所有customExceptions(及其所有后代)。我有一个记录器,可以记录customException(和其他任何东西)。其中一种方法是在处理异常时显式记录异常,如下所示。

try{
    //some exception occur
}
catch(customeException $e)
{
     $log->logException($e);
     $e->showMessage(); // or do anything that we have to do with the error.
}

因为我们正在记录所有的customExceptions,我能想到的另一种方式是,更新customException构造函数并在构造函数中记录异常。这样,它确保记录所有customException。但是,如果我们走这条道路,我的问题是:

  1. 如何将记录器注入customException?
  2. 是否违反SRP原则?
  3. 是否会考虑OOP意义上的不良做法或最佳做法是什么?

2 个答案:

答案 0 :(得分:1)

我认为将记录器注入CustomException是不对的,因为(正如你所指出的那样)它会破坏SRP并增加异常类的复杂性。

我建议你将Exception与ExceptionHandler分开。异常类应该只包含“什么(和哪里)出错”的信息。 ExceptionHandler负责记录异常(并在需要时执行其他一些异常工作)。

因此,您可以设置一个全局ExceptionHandler(使用set_exception_handlerset_error_handler或某些基于框架的异常处理机制,如symfony's ExceptionListener),这将捕获所有未处理的异常。

<?php

class ExceptionHandler {
  /**
   * @var Logger
   */
  private $logger;

  public function __construct(Logger $logger)
  {
    $this->logger = $logger;
  }

  public function handle(Throwable $e)
  {
    $this->logger->logException($e);
  }
} 

在应用程序代码中,您仍然可以抛出并捕获异常。我认为有4种常见情况。

完全可恢复的例外

这是处理可恢复异常的一般方法 - 例如当您根本不想失败时,但在发生此类异常时需要执行某些操作。

<?php

try {
  $methodThatThrowsException();
}
catch (DoesNotMatterException $e) {
  // do some stuff and continue the execution
  // note, that this exception won't be logged 
}

可恢复记录的异常

与上一个相同,但您要记录此异常。

<?php

try {
  $methodThatThrowsException();
}
catch (NonCriticalExceptionThatShouldBeLogged $e) {
  $this->exceptionHandler->handle($e); // log exception
  // do some stuff and continue the execution
}

“终结者”

的不可恢复的例外情况

您想要执行某些特定的业务逻辑然后失败。您可以捕获异常,处理它然后再次抛出它。全局异常处理程序将处理此异常并将其记录下来。

<?php 

try {
  $methodThatThrowsException();
}
catch (CriticalException $e) {
  // do some stuff like cleanup/transaction rollback
  throw $e;
}    

不可恢复的例外情况

如果您只想记录异常并失败,您可以抛出此异常,全局异常处理程序将捕获并记录它。

<?php

$methodThatThrowsException();

// ExceptionHandler::handle will be executed

答案 1 :(得分:0)

只要服务相关且可以维护SRP或执行诸如错误日志记录之类的一般系统操作,就可以将它们注入其他服务中。但是,异常并不是真正的服务,它仅应与基于可抛出的异常数据管理有关。

由于以下原因,我认为登录异常类也是一个问题:

  1. 无法控制要使用的日志类型,警告,错误,严重等。除非您将其弄乱/复杂
  2. 无法控制是否登录。在某些情况下可能会发生异常,但这是一个已知的情况,需要 没有错误处理等
  3. 您必须将各种其他日志记录数据传递给异常,根据您要记录的数据,这可能有点气味

在捕获异常时,将其记录下来,然后抛出一个新异常,以便调用者也可以捕获,记录,抛出。在您的整个应用程序中重复此过程,以便每个需要捕捉潜在异常的thinga方法调用,记录并引发与该类相关的名为自己的异常。

当您到达顶部时,例如控制器,不要抛出而是用漂亮的消息渲染视图。