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.
Install the package:
composer require spatie/laravel-failed-job-monitor
For Slack support:
composer require guzzlehttp/guzzle
Publish the config:
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/services/...
Verify Horizon is configured (if using Laravel Horizon):
Ensure horizon.path is set in your config/horizon.php (default: horizon).
Trigger a failing job to test notifications:
use App\Jobs\FailingJob;
dispatch(new FailingJob());
Check your email/Slack for the notification.
Job Failure Handling: The package automatically hooks into Laravel's job failure system. When a job fails, it triggers the notification pipeline.
Notification Channels:
Notification class to add new channels (e.g., Discord, Teams).Dynamic Filtering:
Use notificationFilter to conditionally suppress notifications:
// config/failed-job-monitor.php
'notificationFilter' => [App\Filters\FailedJobFilter::class, 'shouldNotify'],
Example filter:
public static function shouldNotify(Notification $notification): bool {
return !app()->environment('local'); // Skip in local
}
Custom Notifications: Override the default notification class:
// config/failed-job-monitor.php
'notification' => App\Notifications\CustomFailedJobNotification::class,
Example custom notification:
use Spatie\FailedJobMonitor\Notification as BaseNotification;
class CustomFailedJobNotification extends BaseNotification {
public function toMail($notifiable) {
return (new MailMessage)
->subject('Custom Job Failure Alert')
->line('Job failed: ' . $this->failedJob->job);
}
}
Notifiable Customization:
Replace the default Notifiable class to use custom logic (e.g., dynamic recipients):
// config/failed-job-monitor.php
'notifiable' => App\Notifiables\DynamicRecipientNotifiable::class,
Horizon Compatibility:
The package includes a link to Horizon in emails (if configured). Ensure horizon.path is set in config/horizon.php.
// config/horizon.php
'path' => env('HORIZON_PATH', 'horizon'),
Environment-Specific Config:
Disable notifications in local environments:
FAILED_JOB_MONITOR_ENABLED=${APP_ENV !== 'local'}
Batch Processing: For high-volume queues, consider throttling notifications to avoid alert fatigue:
// In your filter
public static function shouldNotify(Notification $notification): bool {
return cache()->forget("last_failed_job_alert_{$notification->failedJob->id}");
}
Configuration Serialization:
Avoid closures in notificationFilter—use static methods in classes instead to prevent config:cache errors.
Slack Webhook Validation: Ensure the Slack webhook URL is correct and accessible from your queue worker’s environment (e.g., Docker, serverless).
Horizon Path Mismatch:
If Horizon is configured with a custom path (e.g., /admin/horizon), update horizon.path in config/horizon.php to avoid broken links in emails.
Queue Worker Isolation:
Queue workers must have access to the same environment variables (e.g., .env) as your application. Use queue:work with --env=production to match your deployment.
Failed Job Retries:
If jobs are retried, notifications will fire on each failure. Use notificationFilter to deduplicate:
public static function shouldNotify(Notification $notification): bool {
return !cache()->has("failed_job_{$notification->failedJob->id}");
}
No Notifications?: Check:
FAILED_JOB_MONITOR_ENABLED is true.queue:work --once).failed_jobs table exists (run php artisan queue:failed-table if missing).Slack Notifications Failing: Verify the webhook URL and test with:
curl -X POST -H 'Content-type: application/json' --data '{"text":"Test"}' YOUR_WEBHOOK_URL
Custom Notifications Not Triggering:
Ensure your custom class extends Spatie\FailedJobMonitor\Notification and is properly registered in the config.
Rich Email Content: Customize the email template by publishing views:
php artisan vendor:publish --tag=failed-job-monitor-views
Edit resources/views/vendor/failed-job-monitor/email.blade.php.
Slack Formatting: Use Slack’s block kit for rich messages:
public function toSlack($notifiable, array $data) {
return [
'blocks' => [
[
'type' => 'section',
'text' => [
'type' => 'mrkdwn',
'text' => '*Job Failed* :rotating_light:'
]
],
// Add more blocks...
]
];
}
Rate Limiting: Add rate limiting to avoid notification spam:
public static function shouldNotify(Notification $notification): bool {
return now()->diffInMinutes(cache()->get("last_alert")) > 5;
}
Dynamic Recipients:
Use the Notifiable class to fetch recipients dynamically (e.g., from a database):
class DynamicRecipientNotifiable implements ShouldQueue {
public function routeNotificationForMail() {
return User::where('role', 'admin')->pluck('email');
}
}
Testing:
Use Laravel’s fake() for notifications in tests:
$this->fake();
$this->assertNothingSent();
// Trigger a failing job
$this->assertSent(FailedJobNotification::class);
Performance: For large-scale systems, batch failed job processing to reduce database load:
// In your filter
public static function shouldNotify(Notification $notification): bool {
return now()->startOfHour()->lt($notification->failedJob->failed_at);
}
How can I help you explore Laravel packages today?