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-phpHandler 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-phpMailable
<?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-phpBlade
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-phpNewer 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-phpEnd.