Skip to main content

Monitoring Laravel Queued Jobs with Lifecycle Events

Arihant Jain
Jr. Software Engineer

When it comes to scheduled task monitoring, tools like Healthchecks.io do a stellar job. But what about queued jobs — those background tasks running via Redis, Amazon SQS, or other drivers in Laravel?

Out of the box, Laravel doesn’t offer a dashboard or built-in UI to track job status. But the good news is: Laravel provides a robust job event system that allows you to hook into every phase of a job's lifecycle. By tapping into these events, you can build your own real-time job monitoring system with just a bit of code.


Laravel’s Built-in Job Lifecycle Events

Laravel fires several events during the lifecycle of a queued job. Here’s what you get out of the box:

EventDescription
JobQueuedFired when a job is added to the queue
JobProcessingFired just before the job starts executing
JobProcessedFired when the job finishes successfully
JobFailedFired if the job throws an exception
JobTimedOutFired when a job times out
JobExceptionOccurredFired for unhandled exceptions

These events give us fine-grained visibility into every queued job. By listening to them, we can log activity, measure performance, or alert on failures — all without changing our job classes.


Setting Up Job Tracking in Laravel

Here’s a step-by-step guide to building a minimal yet powerful job tracking system using these native events.


1. Create a Table to Track Job Statuses

In a migration file:

Schema::create('job_statuses', function (Blueprint $table) {
$table->id();
$table->string('job_id')->unique();
$table->string('name');
$table->string('status'); // queued, processing, completed, failed
$table->timestamp('queued_at')->nullable();
$table->timestamp('started_at')->nullable();
$table->timestamp('completed_at')->nullable();
$table->text('exception')->nullable();
$table->timestamps();
});

2. Create a Listener to Track Events

app/Listeners/TrackJobStatus.php:

namespace App\Listeners;

use Illuminate\Queue\Events\JobQueued;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobFailed;
use App\Models\JobStatus;

class TrackJobStatus
{
public function handle($event)
{
$jobId = method_exists($event->job, 'uuid') ? $event->job->uuid() : null;

if (!$jobId) return;

if ($event instanceof JobQueued) {
JobStatus::updateOrCreate(
['job_id' => $jobId],
['name' => $name, 'status' => 'queued', 'queued_at' => now()]
);
}

if ($event instanceof JobProcessing) {
JobStatus::where('job_id', $jobId)
->update(['status' => 'processing', 'started_at' => now()]);
}

if ($event instanceof JobProcessed) {
JobStatus::where('job_id', $jobId)
->update(['status' => 'completed', 'completed_at' => now()]);
}

if ($event instanceof JobFailed) {
JobStatus::where('job_id', $jobId)
->update([
'status' => 'failed',
'completed_at' => now(),
'exception' => $event->exception->getMessage(),
]);
}
}
}

3. Register the Listener in EventServiceProvider

Add the following to your EventServiceProvider:

protected $listen = [
\Illuminate\Queue\Events\JobQueued::class => [
\App\Listeners\TrackJobStatus::class,
],
\Illuminate\Queue\Events\JobProcessing::class => [
\App\Listeners\TrackJobStatus::class,
],
\Illuminate\Queue\Events\JobProcessed::class => [
\App\Listeners\TrackJobStatus::class,
],
\Illuminate\Queue\Events\JobFailed::class => [
\App\Listeners\TrackJobStatus::class,
],
];

Laravel will now automatically dispatch your listener whenever these events occur.


What You Gain

With this lightweight system in place, you can:

  • Monitor jobs that are queued, processing, or completed
  • Gain insight into job execution times
  • Catch and log failures with exception messages
  • Build a live dashboard of job activity using Inertia.js, Livewire, or any frontend stack
  • Trigger Slack/email alerts when critical jobs fail

You’re now equipped to build custom observability around one of Laravel’s most powerful background features — without relying on third-party infrastructure.


Final Thoughts

Laravel’s event system isn’t just for jobs — it’s a powerful architectural tool that gives you hooks into the core of the framework. By simply listening to the events Laravel already fires, you can build clean, decoupled systems for monitoring, alerting, and more.

Pair this queue monitoring setup with Healthchecks.io (for cron tasks) and you’ve got full coverage over your background workload — for free.


Bonus Tip: Extend the Listener

You can easily enhance this system to:

  • Track payload details like user ID, batch ID, or job tags
  • Visualize job durations over time
  • Clean up old records with a scheduled command

Let Laravel do the heavy lifting — you just have to listen.