spatie/laravel-failed-job-monitor
Send instant notifications when Laravel queued jobs fail. Uses Laravel’s notification system with built-in Mail and Slack support, configurable via env/config, and easy install/publish. Great for monitoring production queues and alerting the right people.
Installation:
composer require spatie/laravel-failed-job-monitor
php artisan vendor:publish --tag=failed-job-monitor-config
Configure .env:
FAILED_JOB_MONITOR_ENABLED=true
FAILED_JOB_CHANNELS=mail,slack
FAILED_JOB_EMAILS=admin@example.com
FAILED_JOB_SLACK_WEBHOOK_URL=https://hooks.slack.com/...
First Use Case:
Dispatch a job that may fail (e.g., SendEmailJob). If it fails, the package will automatically trigger notifications via configured channels (mail/Slack).
Job Dispatch:
dispatch(new SendEmailJob($user));
FailedJob event via a service provider.Notification Customization:
// app/Notifications/CustomFailedJobNotification.php
use Spatie\FailedJobMonitor\Notification;
class CustomFailedJobNotification extends Notification {
public function toMail($notifiable) {
return (new MailMessage)
->subject('Job Failed: ' . $this->failedJob->job)
->line('The job failed with exception: ' . $this->failedJob->exception);
}
}
Update config:
'notification' => \App\Notifications\CustomFailedJobNotification::class,
Dynamic Recipients:
Notifiable class to fetch recipients dynamically (e.g., from a database):
// app/Notifiables/CustomFailedJobNotifiable.php
class CustomFailedJobNotifiable implements ShouldQueue {
public function route($notifiable, $notification) {
return ['email' => $this->getAdminEmail()];
}
}
Update config:
'notifiable' => \App\Notifiables\CustomFailedJobNotifiable::class,
Filtering Notifications:
// app/Notifications/FailedJobFilter.php
class FailedJobFilter {
public static function filter(Spatie\FailedJobMonitor\Notification $notification): bool {
return !in_array($notification->failedJob->job, ['SendWelcomeEmailJob']);
}
}
Update config:
'notificationFilter' => [\App\Notifications\FailedJobFilter::class, 'filter'],
Environment-Specific Config:
local environments:
FAILED_JOB_MONITOR_ENABLED=${APP_ENV !== 'local'}
Horizon Integration:
horizon:failed-jobs).toMail method:
->action('View in Horizon', url(config('horizon.path').'/failed-jobs'));
Slack Rich Messages:
public function toSlack($notifiable, array $data) {
return [
'text' => "Job failed: {$this->failedJob->job}",
'attachments' => [
[
'title' => 'Exception',
'text' => $this->failedJob->exception,
'color' => '#ff0000',
],
],
];
}
Rate Limiting:
throttle middleware to avoid notification spam:
// app/Http/Middleware/ThrottleFailedJobs.php
public function handle($request, Closure $next) {
return $next($request)->throttle([
'failed_job_notifications' => 1,
'perMinute' => 5,
]);
}
Configuration Caching:
notificationFilter (not serializable). Use static methods in classes instead.'notificationFilter' => fn($notification) => true,
With:
'notificationFilter' => [\App\Filters\JobFilter::class, 'shouldNotify'],
Exception Handling:
try {
$notification->send($notifiable);
} catch (\Exception $e) {
Log::error('Failed to send job failure notification', ['exception' => $e]);
}
Queue Workers:
.env file (e.g., php artisan queue:work --env=production).Horizon-Specific Issues:
Verify Failed Jobs:
failed_jobs table for pending notifications:
SELECT * FROM failed_jobs WHERE failed_at > NOW() - INTERVAL 1 HOUR;
Log Notifications:
Log::debug('Sending notification for job', [
'job' => $this->failedJob->job,
'exception' => $this->failedJob->exception,
]);
Test Locally:
phpunit.xml:
<env key="FAILED_JOB_MONITOR_ENABLED" value="true"/>
<env key="MAIL_MAILER" value="log"/>
Custom Channels:
Notification class:
public function via($notifiable) {
return ['mail', 'pagerduty'];
}
Dynamic Job Filtering:
public static function filter(Notification $notification): bool {
return $notification->failedJob->payload['data']['priority'] === 'high';
}
Retries:
if (!$notification->send($notifiable)) {
$notifiable->failed($notification);
// Requeue after delay
$notifiable->queueAfter(5);
}
Template Overrides:
php artisan vendor:publish --tag=failed-job-monitor-views
Override resources/views/vendor/failed-job-monitor/email.blade.php.Environment-Specific Recipients:
'mail' => [
'to' => env('FAILED_JOB_EMAILS', 'admin@example.com'),
'cc' => env('FAILED_JOB_CC_EMAILS', 'team@example.com'),
],
Slack Formatting: Use Slack’s block kit for rich notifications:
public function toSlack($notifiable) {
return [
'blocks' => [
[
'type' => 'section',
'text' => [
'type' => 'mrkdwn',
'text' => '*Job Failed* :rotating_light:',
],
],
[
'type' => 'section',
'fields' => [
['type' => 'mrkdwn', 'text' => '*Job*', 'value' => $this->failedJob->job],
['type' => 'mrkdwn', 'text' => '*Exception*', 'value' => $this->failedJob->exception],
],
],
],
];
}
Monitoring:
Track notification delivery failures in Sentry or Laravel’s listeners log:
try {
$notification->send($notifiable);
} catch (\Exception $e) {
Sentry::captureException($e);
}
How can I help you explore Laravel packages today?