Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice of log custom exceptions in PHP

I have a custom exception (which is further extended in may other custom exceptions). My project requires log all the customExceptions (and all of its decendents) that occurs. I have a logger that can log customException (and anything else). One of the way of doing so is explicitly log the exception whenever it is being handled as follows.

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

Since we are logging all the customExceptions, the other way I can think of, updating the customException constructor and log the exception right there inside the constructor. In that way, it ensure that all the customException is logged. However, if we go down to this path, my questions is:

  1. How to inject the logger to the customException?
  2. Will it be against the SRP principle?
  3. Will it be consider bad practice in OOP sense or what is the best practice on this?
like image 360
Md Monjur Ul Hasan Avatar asked Feb 28 '18 20:02

Md Monjur Ul Hasan


1 Answers

I think that injecting logger into the CustomException is not right, cause (as you pointed) it breaks SRP and increase the complexity of your exceptions classes.

I'll suggest you to separate Exception from ExceptionHandler. Exception class should only contain information about "what (and where) went wrong". ExceptionHandler is responsible for logging exception (and doing some other work with exception if needed).

So you can setup one global ExceptionHandler (using set_exception_handler and set_error_handler or some framework-based exception handling mechanism like symfony's ExceptionListener), that will catch all unhandled exceptions.

<?php

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

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

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

In application code you can still throw and catch exceptions. I think there are 4 common situations.

Fully recoverable exceptions

This is general way for handling recoverable exceptions – such situations when you don't want to fail at all, but you need to do something when such exception occurs.

<?php

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

Recoverable logged exception

The same as previous, but you want to log this exception.

<?php

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

Non-recoverable exceptions with "finalizer"

You want to execute some specific business logic and then fail. You can catch the exception, process it and then throw it again. Global exception handler will handle this exception and log it.

<?php 

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

Non-recoverable exceptions

If you want to just log the exception and fail, you can just throw this exception and global exception handler will catch and log it.

<?php

$methodThatThrowsException();

// ExceptionHandler::handle will be executed
like image 88
Ivan Kalita Avatar answered Oct 20 '22 01:10

Ivan Kalita