voidstate
voidstate

Reputation: 7990

Best Practice: Where to Add Email Error Logging in a Laravel 5 App

I have the following code in my AppServiceprovider::boot method, whcih ensures I get an email whenever anything is logged with a warning or greater severity level.

$message = \Swift_Message::newInstance( 'An Error has Occurred in XXX' )
    ->setTo( env('ERROR_EMAIL_TO') )
    ->setFrom( env('ERROR_EMAIL_FROM') )
    ->setReplyTo( env('ERROR_EMAIL_REPLY_TO') )
    ->setContentType( 'text/html' );
$swiftMailer = \Mail::getSwiftMailer();
$handler = new SwiftMailerHandler( $swiftMailer, $message, Logger::WARNING );
$handler->setFormatter( new HtmlFormatter() );
\Log::getMonolog()->pushHandler( $handler );

But while this works, I can't help but feel that it's in the wrong place.

Where would you add this code to a Laravel web app?

Upvotes: 0

Views: 560

Answers (1)

Amo
Amo

Reputation: 2944

How about using middleware? I've written some middleware to log all requests, and responses in the past for APIs that could just as easily dispatch e-mails to inform a user of errors (this was pretty much the use case of why I set it up).

Using the terminate() method in your middleware class, will allow you to perform logic after a response has been sent to the user - so your e-mails shouldn't slow down the experience for the end user.

namespace App\Http\Middleware;
use Closure;

class LogRequestAndResponseMiddleware
{
/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 */
public function handle($request, Closure $next)
{
    return $next($request);
}

public function terminate($request, $response)
{
    // Send out an e-mail to you here
}

I think this could be also allow you to a good time to refactor the code, which will help move the logic outside of the middleware and into its own area of responsibility.

In this instance, I'm thinking that I currently want to be informed via e-mail, but I may at some point in the future want to send an event via a Websocket instead.

Therefore, I'd wrap up the logic using a contract and implement it accordingly:

interface ErrorNotificationContract
{
    public function inform($user, $message)
}

class EmailErrorNotification implements ErrorNotificationContract
{
    protected $mail;

    public function __construct(Mail $mail)
    {
        $this->mail = $mail;
    }

    public function inform($user, $message)
    {
         // Your send e-mail logic.
    }
}

You can then register this using a service provider. A side effect is that you get the added benefits of:

  • Dependency injection in the EmailErrorNotification (better testability)
  • Better decoupled code
  • Implementations that can be changed very easily - just create a new class that implements the ErrorNotificationContract

In your middleware you could then do:

public function terminate($request, $response)
{
    // ...

    $errorNotifier->inform('[email protected]', 'something bad happened');
}

Upvotes: 2

Related Questions