Weave 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 Queue Monitor Laravel Package

romanzipp/laravel-queue-monitor

Laravel Queue Monitor tracks queue jobs in your database, recording status, duration, attempts, exceptions, and payloads. It adds a dashboard for searching, filtering, and inspecting failed or completed jobs, helping you debug and monitor queue performance without extra infrastructure.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal setup (5.x):

composer require romanzipp/laravel-queue-monitor
php artisan vendor:publish --provider="romanzipp\QueueMonitor\Providers\QueueMonitorProvider" --tag=migrations
php artisan vendor:publish --provider="romanzipp\QueueMonitor\Providers\QueueMonitorProvider" --tag=config
php artisan migrate
php artisan queue:restart

Version 5.0 requires a new migration (queued_at column) even if you already ran the 4.x one. Publish the migrations tag again and migrate before restarting workers.

Then dispatch a test job and inspect the queue_monitor table or the package UI if enabled. With 5.x, a job should appear as soon as it is dispatched (queued), not only when a worker picks it up.

Where to look first:

  • config/queue-monitor.php — global monitoring toggle, queue allowlist, database connection/table, retention/pruning, UI settings, and the new monitor_queued_jobs flag.
  • The published migration — columns such as job_id, job_uuid, job_class, queue, status, attempt, retried, progress, queued_at, started_at, finished_at, exception, and data.
  • romanzipp\QueueMonitor\Models\QueueMonitor — the main query surface with scopes/accessors for failed, processing, succeeded, recent, etc.
  • The service provider (romanzipp\QueueMonitor\Providers\Queue MonitorProvider) — confirms which Laravel queue events (JobQueued, JobProcessing, JobProcessed, JobFailed, etc.) are listened to and how records are created/updated.
  • The README — dashboard route, middleware/auth configuration, and available queue-monitor:* artisan commands.

First practical use case: answer “what failed recently and why?” without digging through worker logs:

use romanzipp\QueueMonitor\Models\QueueMonitor;

QueueMonitor::failed()
    ->where('started_at', '>=', now()->subDay())
    ->latest()
    ->get();

Or use the bundled dashboard if enabled, typically mounted at /queue-monitor — verify the exact route and middleware in the package config/routes.

Implementation Patterns

Global monitoring vs selective monitoring

By default the package can monitor all queue jobs. That is useful for small/medium apps, but noisy for high-throughput systems. For day-to-day use:

  • Keep global monitoring on when you want broad visibility and can filter by queue/class in queries or the UI.
  • Turn global monitoring off and opt in with the IsMonitored trait when only critical jobs matter.
  • Use the queue allowlist in config to exclude internal, high-volume, or low-value queues.

Queued job monitoring (new in 5.0)

5.0 adds a monitor_queued_jobs config option. When enabled, a monitor record is created the moment a job is dispatched, with queued_at populated. This is genuinely useful for spotting jobs that are stuck in the queue rather than failing:

// Jobs dispatched but never picked up (or waiting too long)
QueueMonitor::whereNotNull('queued_at')
    ->whereNull('started_at')
    ->where('queued_at', '<=', now()->subMinutes(30))
    ->get();

Practical implications:

  • Enabling this increases write volume — one insert per dispatch, on top of the update per state transition. Evaluate on high-throughput systems.
  • Queued-but-never-started records let you detect backlog, paused workers, and misconfigured queue connections before they become user-visible failures.
  • For a “wait time” metric, compare queued_at to started_at on the same record. Both columns only exist reliably from 5.x onward.
  • If you don’t need dispatch-time visibility, leave monitor_queued_jobs false and keep the previous behavior.

Enrich job records with context

Add the trait to jobs where you want friendly names, searchable metadata, or progress:

use Illuminate\Contracts\Queue\ShouldQueue;
use romanzipp\QueueMonitor\Traits\IsMonitored;

class ImportCustomers implements ShouldQueue
{
    use IsMonitored;

    public function monitorName(): ?string
    {
        return 'Import customers';
    }

    public function monitorData(): array
    {
        return [
            'file_id' => $this->fileId,
            'tenant_id' => $this->tenantId,
        ];
    }

    public function handle(): void
    {
        $this->monitorProgress(10);
        // ...
        $this->monitorProgress(100);
    }
}

Keep monitorData() small and JSON-serializable. Store IDs, not Eloquent models or closures.

Build operational queries around scopes

Use the model as an observability API rather than writing raw SQL:

QueueMonitor::processing()->get();
QueueMonitor::failed()->last24h()->count();
QueueMonitor::where('queue', 'emails')->succeeded()->count();
QueueMonitor::where('job_class', ImportCustomers::class)->latest()->first();

Use job_class_map in config to give internal class names readable labels in the dashboard/UI.

Retrying jobs from the monitor

4.0 introduced job retry support with the job_uuid and retried columns; 5.0 builds on that. When reconstructing an incident, filter by job_uuid to trace a single logical job across its retries and original dispatch:

QueueMonitor::where('job_uuid', $uuid)->orderBy('attempt')->get();

This is the reliable way to link a queued record (5.x) to its later processing/failure records.

Alerting and health checks

Schedule a lightweight check every few minutes:

$failed = QueueMonitor::failed()
    ->where('started_at', '>=', now()->subMinutes(10))
    ->count();

if ($failed > 0) {
    // Slack / email / PagerDuty notification
}

Include the job class, queue, attempt, exception excerpt, and job_id in the alert. Avoid dumping the full exception or data payload into chat if it may contain PII.

With queued monitoring on, add a backlog check alongside failure alerting (see pattern above).

Retention and pruning

The monitor table grows with every monitored job, every progress update, and — with 5.0 — every dispatch. Schedule the package purge command, or prune via the model:

php artisan queue-monitor:purge

For high-volume queues, keep retention short — 7 to 30 days is usually enough for debugging. Longer retention belongs in a dedicated observability system.

Database portability

5.0 adds PostgreSQL and SQLite support. This makes it viable for local SQLite-first development and Postgres production deployments. Configure the monitor connection/table explicitly if your app uses multiple databases.

Dashboard and custom UI

If the bundled UI is enabled, protect it before exposing it outside local/staging:

  • Put it behind auth or an admin gate via the package middleware/config.
  • Disable the UI entirely if you only need programmatic access.
  • If you need a custom view, query QueueMonitor from your own controller and reuse its scopes rather than duplicating status logic.

Gotchas and Tips

  • 5.0 requires a new migration. The queued_at column is added in 5.x. Run vendor:publish --tag=migrations and php artisan migrate before restarting workers, or inserts will fail.
  • Provider namespace is romanzipp\QueueMonitor\Providers\QueueMonitorProvider. If your publish commands or manual registrations reference the old service-provider path, update them during the 5.0 upgrade.
  • monitor_queued_jobs is on by default in 5.x-style config. Read the published config and decide explicitly — it affects write volume and record counts across every dashboard and alert query you already have.
  • Workers must be restarted after install/config changes. php artisan queue:restart ensures long-running workers load the package listeners and new config.
  • Monitoring has write overhead. Each monitored job creates/updates rows; progress updates are database writes; queued monitoring adds an insert per dispatch. Call monitorProgress() at milestones, not on every loop iteration.
  • Progress scale must be consistent. Use the documented range from the package trait/model (commonly 0–100) and do not mix 0–1 and 0–100 values.
  • Understand retry semantics before debugging. Retries may appear as updated attempt counts or multiple records; use job_uuid (4.x+) plus attempt to reconstruct what happened.
  • The queue backend remains the source of truth. Queue Monitor is an observability layer; Laravel’s failed_jobs table and queue backend still govern retries and failure handling.
  • Queued vs processing timing can surprise you. With 5.0 a record may exist with queued_at set and started_at null. Confirm in your version when a job first appears — especially for delayed jobs, sync drivers, and jobs that fail before handle().
  • monitorData() must be serializable. Avoid models, resources, binary data, and large payloads. Store foreign keys and summaries instead.
  • Exception and payload columns can get large. Prune aggressively and avoid SELECT * on high-volume dashboards or alert queries.
  • Database connection mismatch is a common deployment bug. If workers use a separate database connection, set the monitor connection explicitly and run migrations there — this is now relevant for Postgres/SQLite setups too.
  • Protect the dashboard. Job names, exception messages, and custom data can leak sensitive information. Default-open UI routes are a staging/local convenience, not a production default.
  • Pin and test the package version. 5.0 brings DB-portability and queued-monitoring changes; review the upgrade guide and re-run your monitor queries against a staging database before upgrading in production. Because the package is model/event-listener based, replacing it with a small custom listener is still feasible if maintenance becomes a problem.
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.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox