Poor man's exception email notifications for Laravel
Exception HandlingIf you still haven't caught on to using a proper exception management for your Laravel apps like FlareApp.io, you might want to follow this poor man's guide which will email you every time an exception takes place. It's a very primitive form of getting notified, but maybe for some super budget conscious situation it will be suitable.
I'm documenting it here because it's quite a lot of boilerplate and moving parts. To be honest, I don't recommend this even for a hobby project, but here goes anyway:
For this elaborate contraption you'll need at least:
- A config file to where the exceptions are supposed to be sent
- The custom Handler code
- A mailable and a blade
- The blade needs to be independent of Laravel, because when the exception occurs you don't have access to the framework
- Finally, you'll have to register the handler in `bootstrap/app.php`, which differs quite a bit between Laravel versions.
Config File
Save as `config/exceptions.php`:
<?php return [ 'recipients' => [ 'user1@example.com', 'user2@example.com', ] ]; lang-php
Handler Code
<?php namespace App\Exceptions; use App\Mail\ExceptionOccurredMail; use Exception; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; use Throwable; class Handler extends ExceptionHandler { /** * Register the exception handling callbacks for the application. */ public function register(): void { $this->reportable(function (Throwable $e) { $this->sendEmail($e); }); } /** * Send an email every time an exception occurs. */ protected function sendEmail(Throwable $exception): void { try { $emailRecipients = config('exception.recipients', ['default-email@example.com']); Mail::to($emailRecipients)->send(new ExceptionOccurredMail($exception)); } catch (Exception $exception) { // Log a critical exception to avoid an infinite loop of exceptions Log::critical('Critical error to avoid infinite loops: Failed to send exception email: '.$exception->getMessage()); } } } lang-php
Mailable
<?php namespace App\Mail; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailables\Content; use Illuminate\Mail\Mailables\Envelope; use Illuminate\Queue\SerializesModels; class ExceptionOccurredMail extends Mailable { use SerializesModels; public \Throwable $exception; /** * Create a new message instance. * * @return void */ public function __construct(\Throwable $exception) { $this->exception = $exception; } public function build() { return $this->with([ 'exception' => $this->exception, ]); } /** * Get the message envelope. */ public function envelope(): Envelope { return new Envelope( subject: config('app.name') . ' Exception Occurred', ); } /** * Get the message content definition. */ public function content(): Content { return new Content( view: 'emails.exception', ); } /** * Get the attachments for the message. */ public function attachments(): array { return []; } } lang-php
Blade
Note the vanilla format since you won't have access to the rest of the Blade templating in the framework.
<!DOCTYPE html> <html lang="en"> <head> <title>Exception Occurred</title> </head> <body> <h1>A {{ config('app.name') }} exception has occurred</h1> <p>Exception message: {{ $exception->getMessage() }}</p> <p>File: {{ $exception->getFile() }}</p> <p>Line: {{ $exception->getLine() }}</p> </body> </html> lang-html
`bootstrap/app.php` modifications
Here are two variants of the Handler code in `bootstrap/app.php`:
Older version
<?php ... $app->singleton( Illuminate\Contracts\Debug\ExceptionHandler::class, App\Exceptions\Handler::class ); /* |-------------------------------------------------------------------------- | Return The Application |-------------------------------------------------------------------------- | | This script returns the application instance. The instance is given to | the calling script so we can separate the building of the instances | from the actual running of the application and sending responses. | */ return $app; lang-php
Newer version
<?php use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { // }) ->withExceptions(function (Exceptions $exceptions) { App\Exceptions\Handler::class; })->create(); lang-php
End.