Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Failed Job Monitor Laravel Package

spatie/laravel-failed-job-monitor

Laravel package that notifies you when queued jobs fail. Uses Laravel’s notification system and supports email and Slack out of the box, with configurable notifiables and custom notification classes for your own alerting needs.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:

    composer require spatie/laravel-failed-job-monitor
    

    For Slack support:

    composer require guzzlehttp/guzzle
    
  2. Publish the config:

    php artisan vendor:publish --tag=failed-job-monitor-config
    
  3. Configure .env:

    FAILED_JOB_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...
    MAIL_MAILER=smtp
    MAIL_FROM_ADDRESS="noreply@example.com"
    
  4. Verify channels in config/failed-job-monitor.php:

    'channels' => ['mail', 'slack'], // or ['mail'] if Slack is unused
    'mail' => [
        'to' => ['admin@example.com'],
    ],
    

First Use Case

Trigger a test failure:

// In a job class
public function handle()
{
    throw new \Exception("Test failure for monitoring");
}

Dispatch it:

php artisan queue:work

Check your email/Slack for the notification.


Implementation Patterns

Core Workflow

  1. Job Failure Handling: The package hooks into Laravel’s FailedJob event. When a job fails, it automatically triggers the configured notification.

  2. Notification Customization:

    • Override the notification class (e.g., for custom templates or data):
      // config/failed-job-monitor.php
      'notification' => \App\Notifications\CustomFailedJobNotification::class,
      
      Example custom notification:
      namespace App\Notifications;
      use Spatie\FailedJobMonitor\Notification as BaseNotification;
      
      class CustomFailedJobNotification extends BaseNotification
      {
          public function toMail($notifiable)
          {
              return (new MailMessage)
                  ->subject('Job Failed: ' . $this->job->name)
                  ->line('Job ID: ' . $this->job->id)
                  ->action('View in Horizon', url('/horizon/jobs/' . $this->job->id));
          }
      }
      
  3. Dynamic Recipients: Use environment variables for flexibility:

    FAILED_JOB_MONITOR_MAIL_TO=admin@example.com,backup@example.com
    FAILED_JOB_MONITOR_SLACK_WEBHOOK_URL=https://...
    

    Update config/failed-job-monitor.php to read from env:

    'mail' => [
        'to' => explode(',', env('FAILED_JOB_MONITOR_MAIL_TO', 'admin@example.com')),
    ],
    'slack' => [
        'webhook_url' => env('FAILED_JOB_MONITOR_SLACK_WEBHOOK_URL'),
    ],
    
  4. Filtering Failures: Skip notifications for specific jobs or exceptions:

    // config/failed-job-monitor.php
    'notificationFilter' => [\App\Filters\FailedJobFilter::class, 'shouldNotify'],
    

    Example filter:

    namespace App\Filters;
    use Spatie\FailedJobMonitor\Notification;
    
    class FailedJobFilter
    {
        public static function shouldNotify(Notification $notification): bool
        {
            return !str_contains($notification->job->name, 'SendEmail');
        }
    }
    
  5. Integration with Horizon: Add a link to Horizon in notifications (if using Horizon):

    // In your custom notification
    ->action('View in Horizon', config('horizon.path') . '/jobs/' . $this->job->id)
    

Advanced Patterns

  • Multi-Channel Notifications: Combine email and Slack dynamically:

    'channels' => ['mail', 'slack'],
    

    Or conditionally:

    'notificationFilter' => function (Notification $notification) {
        return app()->environment('production') ? true : false;
    },
    
  • Job-Specific Notifications: Attach metadata to jobs and use it in notifications:

    // In your job
    public function handle()
    {
        $this->job->payload['custom_data'] = ['user_id' => 123];
        // ...
    }
    

    Access in notification:

    $this->job->payload['custom_data']['user_id']
    
  • Rate Limiting: Avoid notification spam by throttling:

    // In your custom notification
    use Illuminate\Notifications\Messages\MailMessage;
    use Illuminate\Support\Facades\RateLimiter;
    
    public function toMail($notifiable)
    {
        if (RateLimiter::tooManyAttempts($notifiable->getKeyName(), 5)) {
            return null; // Skip notification
        }
        return (new MailMessage)->line('Job failed!');
    }
    

Gotchas and Tips

Common Pitfalls

  1. Config Caching Issues:

    • After updating config/failed-job-monitor.php, clear the config cache:
      php artisan config:clear
      
    • Avoid closures in notificationFilter (use static methods instead to avoid serialization errors).
  2. Horizon Compatibility:

    • If using Horizon, ensure the horizon.path config is set in config/horizon.php:
      'path' => 'horizon',
      
    • The package hides Horizon links if Horizon is not installed (release 4.3.2+).
  3. Slack Webhook Validation:

    • Test Slack webhooks with tools like webhook.site before deploying.
    • Ensure the webhook URL is HTTPS and accessible from your server.
  4. Job Payload Serialization:

    • Failed jobs are serialized. Complex objects (e.g., closures, resources) may not appear in notifications. Use simple data types or JSON-serializable payloads.
  5. Queue Worker Restarts:

    • If workers crash silently, restart them:
      php artisan queue:restart
      
    • Monitor worker logs for exceptions:
      tail -f storage/logs/laravel.log
      

Debugging Tips

  • Check Failed Jobs Table:
    php artisan tinker
    >> \Illuminate\Support\Facades\DB::table('failed_jobs')->latest()->first();
    
  • Log Notifications: Add debug logs to your custom notification class:
    \Log::debug('Failed job notification triggered', [
        'job' => $this->job,
        'exception' => $this->exception,
    ]);
    
  • Test Locally: Use a local SMTP server (e.g., MailHog) or Slack test channels to verify notifications before deploying.

Extension Points

  1. Custom Notifiable: Extend \Spatie\FailedJobMonitor\Notifiable to add new channels (e.g., PagerDuty, SMS):

    namespace App\Notifiables;
    use Spatie\FailedJobMonitor\Notifiable as BaseNotifiable;
    
    class CustomNotifiable extends BaseNotifiable
    {
        public function routeNotificationForPagerDuty()
        {
            return 'your-pagerduty-integration-key';
        }
    }
    

    Update config:

    'notifiable' => \App\Notifiables\CustomNotifiable::class,
    
  2. Dynamic Notification Data: Pass additional context to notifications:

    // In your job's handle() method
    app(\Spatie\FailedJobMonitor\FailedJobMonitor::class)
        ->notifyWith(['custom_key' => 'custom_value']);
    

    Access in notification:

    $this->customData['custom_key']
    
  3. Retry Logic: Combine with shouldRetry in jobs to avoid duplicate notifications:

    public function shouldRetry()
    {
        return false; // Disable retries to ensure notification is sent
    }
    
  4. Environment-Specific Config: Use Laravel’s config caching with environment-specific files:

    cp config/failed-job-monitor.php config/failed-job-monitor-local.php
    

    Update config/app.php:

    'config' => [
        'failed-job-monitor' => env('APP_ENV') === 'local'
            ? __DIR__ . '/failed-job-monitor-local.php'
            : __DIR__ . '/failed-job-monitor.php',
    ],
    

Performance Considerations

  • Avoid Heavy Notifications: Keep notification logic lightweight (e.g., avoid querying databases in toMail).
  • Batch Processing: For high-volume queues, consider batching notifications (e.g., send daily digests instead of per-failure alerts).
  • Queue Notifications: Dispatch notifications asynchronously to avoid blocking the queue worker:
    // In your custom notification logic
    dispatch(new SendFailedJobNotification($notifiable, $this));
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport