Paulund
2018-08-25 #laravel

Send An Alert When Laravel Jobs Fail

Laravel Jobs allow you to queue up processes to be done at a later date, this is normally reserved for longer running tasks or tasks that need to communicate with a third party.

Common tasks you send to be queued could be sending an email for example, if you use a third party SMTP API to send your email this could be a slow process. It's also a process that can run in the background and doesn't affect the user experience of the users of your app.

As Jobs run in the background on a queue there is no instant response to when and how they failed, therefore we need to find a way of alerting you to the failed jobs and why they failed.

In this tutorial we're going to investigate how we can send a slack notification when a job fails in the queue.

Creating A Job

To create a new job in Laravel you can use the artisan make command

php artisan make:job SendEmail

This will create a file in App\Jobs\SendEmail where you write the code for your job.

Dealing With Failed Jobs

There are two different ways of handling failed jobs you can either handle them singularly or you can handle it globally.

If you want to handle the failure of jobs differently per job you can add a failed method to your job class.

    /**
     * The job failed to process.
     *
     * @param  Exception  $exception
     * @return void
     */
    public function failed(Exception $exception)
    {
        // Send user notification of failure, etc...
    }

The second approach is to handle all failed jobs through the same process, which is the option we're going to use on this tutorial.

When a queue fails it will despatch a new failing event which we can hook into from the AppServiceProvider.php. From this failed event we're going to use the Laravel Notifications class to send the alert to the slack channel.

Create The Notification Class

Laravel notifications classes provide a nice way to alert your users to certain event, this inbuilt functionality allows you to send alerts via multiple channels such as mail, database, broadcast, nexmo, and slack.

Notifications can be created from the artisan make command.

php artisan make:notification SlackFailedJob

In this notification we're going to have one channel for slack.

There is a nice interface in Laravel to sending Slack messages, we will use this to construct the slack message and attach the exception to the message.

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\SlackAttachment;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Events\JobFailed;

class SlackFailedJob extends Notification implements ShouldQueue
{
    use Queueable;

    /**
     * @var JobFailed
     */
    public $event;

    /**
     * Create a new notification instance.
     *
     * @param JobFailed $event
     */
    public function __construct(JobFailed $event)
    {
        $this->event = $event;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['slack'];
    }

    /**
     * Get the Slack representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return SlackMessage
     */
    public function toSlack($notifiable)
    {
        return (new SlackMessage)
            ->content('A job failed at '.config('app.name'))
            ->attachment(function (SlackAttachment $attachment) use ($notifiable) {
                $attachment->fields([
                    'Exception message' => $notifiable->event->exception->getMessage(),
                    'Job class' => $notifiable->event->job->resolveName(),
                    'Job body' => $notifiable->event->job->getRawBody(),
                    'Exception' => $notifiable->event->exception->getTraceAsString(),
                ]);
            });
    }
}

Hook Into Queue Failing Event

With the job created and the notification created we can now hook into the failed queue event to send the notification.

For this you need to add the following in your AppServiceProvider.php into your boot function.

public function boot()
{
  Queue::failing(function (JobFailed $event) use ($slackUrl) {
    Notification::route('slack', $slackUrl)->notify(new SlackFailedJob($event));
  });
}

This will send through a JobFailed $event then we use the Notification facade sending it via a slack route, which needs to be sent to the webhook URL of the slack channel you want to use. Then you add the notify method and pass in the notification class we created earlier.

Now whenever a job in your application fails you'll get a quick alert into your slack channel with the exception message that caused the error.