PHPackages                             maherelgamil/periscope - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Caching](/categories/caching)
4. /
5. maherelgamil/periscope

ActiveLibrary[Caching](/categories/caching)

maherelgamil/periscope
======================

A universal queue monitor for Laravel — see into any queue driver (Redis, database, SQS, Beanstalkd).

v0.5.3(2mo ago)383[9 issues](https://github.com/maherelgamil/periscope/issues)MITPHPPHP ^8.2CI passing

Since Apr 17Pushed 2mo agoCompare

[ Source](https://github.com/maherelgamil/periscope)[ Packagist](https://packagist.org/packages/maherelgamil/periscope)[ Docs](https://github.com/maherelgamil/periscope)[ RSS](/packages/maherelgamil-periscope/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)Dependencies (8)Versions (15)Used By (0)

Periscope
=========

[](#periscope)

[![Latest Version on Packagist](https://camo.githubusercontent.com/20188dc294e990fd50d14836fe6bc375b80745a69a069038441a7734a28b32e8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d61686572656c67616d696c2f7065726973636f70652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/maherelgamil/periscope)[![Tests](https://camo.githubusercontent.com/5b84916332b342ef71c837f71e70e71b39d8af452f8407ddb41244d26ef9dda1/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6d61686572656c67616d696c2f7065726973636f70652f74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/maherelgamil/periscope/actions/workflows/tests.yml)[![Total Downloads](https://camo.githubusercontent.com/514c58961da77a9837d234c0380cbec41ce402a035add64485b1f5a05f7dd002/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6d61686572656c67616d696c2f7065726973636f70652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/maherelgamil/periscope)[![License](https://camo.githubusercontent.com/9dc4ac729c4634c861f3923f674a5d64cc022e548dd414e72bca350ec589f714/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6d61686572656c67616d696c2f7065726973636f70652e7376673f7374796c653d666c61742d737175617265)](LICENSE)

> See into any queue.

Periscope is a universal queue monitor, and manage for Laravel — a driver-agnostic alternative to Laravel Horizon. It works with **any** queue driver: Redis, database, SQS, Beanstalkd, and more, because it collects telemetry through Laravel's built-in queue events rather than reading driver-specific internals.

[![Periscope Overview](docs/screenshots/overview.png)](docs/screenshots/overview.png)

[![Periscope dashboard walkthrough](docs/screenshots/demo.gif)](docs/screenshots/demo.gif)

Contents
--------

[](#contents)

- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Running workers](#running-workers)
- [Scheduling](#scheduling)
- [Authorization](#authorization)
- [Metrics endpoint](#metrics-endpoint-prometheus--json)
- [Alerts](#alerts)
- [Tags](#tags)
- [Batches](#batches)
- [Scheduled commands](#scheduled-commands)
- [Performance percentiles](#performance-percentiles)
- [Commands](#commands)
- [Screenshots](#screenshots)
- [Testing](#testing)
- [Contributing](#contributing)
- [License](#license)

Features
--------

[](#features)

- **Universal** — works with `redis`, `database`, `sqs`, `beanstalkd`, and `sync` queue drivers
- **Real-time dashboard** built on Livewire 4 + Tailwind 4 — no CDN, ships with a compiled CSS bundle
- **Per-attempt tracking** — every retry recorded with its runtime and exception, shown as a timeline on the job detail page
- **Grouped exceptions** — aggregate by class + message so you see "this `RuntimeException` hit 47 times" instead of 47 rows, with drill-down to individual occurrences
- **Batch tracking** — live progress for `Bus::batch()` jobs with cancel action
- **Scheduled command monitoring** — every `Schedule::command()` run recorded with runtime and failure reason
- **Performance percentiles** — p50 / p95 / p99 of runtime and wait, per queue
- **Memory tracking** — peak memory captured per attempt
- **Alert history** — every fired rule persisted, searchable, and dismissible
- **Live queue depth** via driver adapters — pending, delayed, and reserved counts on the Queues page
- **Throughput chart** — last 60 minutes of processed vs failed jobs, polled every 10s
- **Worker pools** — config-driven supervisors with optional auto-balance that moves processes between queues based on backlog depth
- **Lifecycle control** — `start` / `pause` / `continue` / `terminate` for deploy-friendly operation
- **Failed job management** — search, filter, retry, forget, and bulk actions
- **Alerts** — failure spike, long wait, stale worker; delivered via mail, Slack, or webhook
- **Prometheus + JSON metrics endpoint** for external monitoring stacks (Grafana, Datadog, etc.)
- **Authorization gate** — `viewPeriscope` ability, scoped to `local` by default
- **Rolling metrics** — per-minute buckets rolled up into hourly, with configurable retention per tier
- **Tag filtering** via Laravel's `Queueable::tags()`

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 11, 12, or 13
- Livewire 4

Installation
------------

[](#installation)

```
composer require maherelgamil/periscope
php artisan periscope:install
php artisan migrate
```

Visit `/periscope` in your browser. The dashboard is gated to `local` environment by default — see [Authorization](#authorization) to expose it elsewhere.

Running workers
---------------

[](#running-workers)

Workers must be launched through Periscope so heartbeats show up on the Workers page.

### A single worker

[](#a-single-worker)

```
php artisan periscope:supervise redis --queue=default,emails
```

Same flags as `queue:work` (`--tries`, `--timeout`, `--memory`, `--max-jobs`, `--max-time`, etc.).

### Config-driven pools (recommended)

[](#config-driven-pools-recommended)

Define supervisors in `config/periscope.php`:

```
'supervisors' => [
    'default' => [
        'connection' => 'redis',
        'queue' => ['default', 'emails'],
        'processes' => 4,
        'tries' => 1,
        'timeout' => 60,
        'sleep' => 1,
    ],
    'notifications' => [
        'connection' => 'redis',
        'queue' => ['notifications'],
        'processes' => 2,
        'nice' => 10, // deprioritize with Unix nice (ignored on Windows)
    ],
],
```

Start every pool with:

```
php artisan periscope:start
```

The master process respawns any child that crashes and shuts children down cleanly on SIGTERM / SIGINT. Run with `--supervisor=default` to start only a specific pool.

### Auto-balance mode

[](#auto-balance-mode)

Set `balance: 'auto'` and Periscope allocates processes per queue proportional to live queue depth on each cycle, clamped between `min_processes` and `max_processes`:

```
'default' => [
    'connection' => 'redis',
    'queue' => ['high', 'default', 'low'],
    'balance' => 'auto',
    'min_processes' => 1,
    'max_processes' => 10,
],
```

When all queues are empty, each queue runs `min_processes` workers. When backlog grows, processes are moved toward the busiest queues automatically.

### Deploy-friendly lifecycle

[](#deploy-friendly-lifecycle)

```
# Stop spawning new workers; let running ones drain
php artisan periscope:pause

# Resume
php artisan periscope:continue

# Shut the master down entirely (use this in your deploy script)
php artisan periscope:terminate
```

Scheduling
----------

[](#scheduling)

Add the housekeeping commands to `routes/console.php`:

```
use Illuminate\Support\Facades\Schedule;

Schedule::command('periscope:workers:sweep')->everyMinute();
Schedule::command('periscope:snapshot')->hourly();
Schedule::command('periscope:alerts:check')->everyFiveMinutes();
Schedule::command('periscope:prune')->daily();
```

Authorization
-------------

[](#authorization)

Running `periscope:install` scaffolds `app/Providers/PeriscopeServiceProvider.php` in your application (and registers it in `bootstrap/providers.php`). Edit the `gate()` method to decide who can access the dashboard in non-local environments:

```
protected function gate(): void
{
    Gate::define('viewPeriscope', function ($user) {
        return in_array($user?->email, [
            'you@example.com',
        ]);
    });
}
```

If you don't override it, access is restricted to the `local` environment. The metrics and health endpoints bypass this gate — see below.

Metrics endpoint (Prometheus / JSON)
------------------------------------

[](#metrics-endpoint-prometheus--json)

Periscope exposes aggregated telemetry at:

- `/periscope/metrics` — Prometheus text format (scrape target)
- `/periscope/metrics.json` — JSON for custom integrations

Both bypass the dashboard authorization by default. Protect them for production with your own middleware (IP allowlist, token guard):

```
// config/periscope.php
'metrics' => [
    'enabled' => env('PERISCOPE_METRICS_ENABLED', true),
    'middleware' => ['web', \App\Http\Middleware\AllowPrometheus::class],
],
```

Or disable entirely:

```
PERISCOPE_METRICS_ENABLED=false
```

Example Prometheus scrape config:

```
scrape_configs:
  - job_name: periscope
    metrics_path: /periscope/metrics
    static_configs:
      - targets: ['your-app.test']
```

**Exposed metrics** (each labelled with `connection` and `queue` where applicable):

MetricTypeDescription`periscope_jobs_processed_total`counterSuccessfully processed jobs`periscope_jobs_failed_total`counterJobs that failed`periscope_jobs_queued_total`counterJobs pushed onto a queue`periscope_runtime_ms_sum`counterCumulative runtime (ms)`periscope_wait_ms_sum`counterCumulative wait time (ms)`periscope_queue_pending`gaugeJobs ready to process (live from driver)`periscope_queue_delayed`gaugeJobs scheduled for the future`periscope_queue_reserved`gaugeJobs currently claimed by a worker`periscope_workers{status}`gaugeWorker counts by status`periscope_jobs_current{status}`gaugeMonitored jobs by current statusAlerts
------

[](#alerts)

Three rules ship in the box:

- `failure_spike` — more than N failed jobs in M minutes
- `long_wait` — average wait exceeds a threshold
- `stale_worker` — any worker has missed its heartbeat window

Each rule has a per-rule cooldown to prevent flooding. Configure thresholds in `config/periscope.php` and wire channels with env vars:

```
PERISCOPE_ALERT_CHANNELS=mail,slack,webhook
PERISCOPE_ALERT_MAIL=ops@example.com
PERISCOPE_ALERT_SLACK_WEBHOOK=https://hooks.slack.com/services/...
PERISCOPE_ALERT_WEBHOOK_URL=https://your-app.test/webhooks/periscope
```

Webhook payload shape:

```
{
    "key": "failure_spike",
    "title": "Queue failure spike",
    "message": "23 job(s) failed in the last 5 minutes (threshold: 10).",
    "severity": "error",
    "context": {"count": 23, "minutes": 5, "threshold": 10},
    "fired_at": "2026-04-17T20:30:00+00:00",
    "source": "periscope"
}
```

Schedule `periscope:alerts:check` every few minutes to evaluate the rules.

Tags
----

[](#tags)

Any `ShouldQueue` job can expose tags by implementing a `tags()` method:

```
class SendInvoice implements ShouldQueue
{
    use Queueable;

    public function __construct(public Invoice $invoice) {}

    public function tags(): array
    {
        return ['invoices', "customer:{$this->invoice->customer_id}"];
    }
}
```

Tags show on the job detail page and can be filtered on the Jobs page via the Tag input.

Batches
-------

[](#batches)

Periscope reads Laravel's native `job_batches` table, so there's no extra setup beyond running Laravel's own `php artisan make:queue-batches-table` + migration. The Batches page shows:

- live progress bar (completed / total)
- pending and failed counts
- cancel button for in-flight batches

Any job that uses the `Batchable` trait and is dispatched via `Bus::batch([...])` appears automatically.

Scheduled commands
------------------

[](#scheduled-commands)

Periscope subscribes to Laravel's scheduler events (`ScheduledTaskStarting`, `Finished`, `Failed`, `Skipped`) so every run of `Schedule::command(...)` is recorded with its runtime, exit status, and cron expression. The Schedules page shows the last N runs with failure messages for diagnosing cron issues.

No configuration needed — if your app uses the Laravel scheduler, it just works.

Performance percentiles
-----------------------

[](#performance-percentiles)

The Performance page computes `p50 / p95 / p99` of both runtime and wait time for every queue that had completed jobs in the selected window (1 hour, 6 hours, 24 hours, or 7 days). Percentiles are calculated in PHP from a sorted sample so they work against every supported queue driver without needing database-specific percentile functions.

Exception grouping
------------------

[](#exception-grouping)

The Exceptions page aggregates failures by `exception_class + message`. Click any row to drill into the `/exceptions/show` page, which lists every occurrence in the window, the affected jobs, and a sample stack trace you can click through.

Alert history
-------------

[](#alert-history)

Every fired alert is persisted to the `periscope_alerts` table so you can audit what rules triggered, when, and which channels received them. The Alerts page lists all firings and lets you dismiss rows you've triaged.

Commands
--------

[](#commands)

CommandPurpose`periscope:install`Publish config, migrations, and compiled assets`periscope:supervise {connection}`Run a single `queue:work` with heartbeat reporting`periscope:start`Boot all supervisors from config (respawns children, handles SIGTERM)`periscope:pause`Stop spawning new workers; drain running ones`periscope:continue`Resume after a pause`periscope:terminate`Signal the master to shut down`periscope:workers:sweep`Mark workers stale past their heartbeat window`periscope:snapshot`Roll minute metrics into hourly buckets`periscope:prune`Delete old jobs and metrics per retention config`periscope:alerts:check`Evaluate alert rules and dispatch notificationsRun any command with `--help` for full flag reference.

Screenshots
-----------

[](#screenshots)

Click to expand### Job detail — attempts timeline, memory, tags, payload

[](#job-detail--attempts-timeline-memory-tags-payload)

[![Job detail with attempts history](docs/screenshots/job-detail.png)](docs/screenshots/job-detail.png)

### Jobs — filter by status, queue, tag, or search

[](#jobs--filter-by-status-queue-tag-or-search)

[![Jobs list with status, queue, and tag filters](docs/screenshots/jobs.jpg)](docs/screenshots/jobs.jpg)

### Exceptions — grouped by class + message

[](#exceptions--grouped-by-class--message)

[![Exception grouping showing occurrences and affected jobs](docs/screenshots/exceptions.jpg)](docs/screenshots/exceptions.jpg)

### Failed jobs — search, bulk retry / forget

[](#failed-jobs--search-bulk-retry--forget)

[![Failed jobs page with bulk actions](docs/screenshots/failed.jpg)](docs/screenshots/failed.jpg)

### Alerts — history of every fired rule

[](#alerts--history-of-every-fired-rule)

[![Alert history with severity and channels](docs/screenshots/alerts.png)](docs/screenshots/alerts.png)

### Schedules — every scheduled command run

[](#schedules--every-scheduled-command-run)

[![Scheduled command runs](docs/screenshots/schedules.png)](docs/screenshots/schedules.png)

### Batches — Laravel batches with live progress

[](#batches--laravel-batches-with-live-progress)

[![Batch progress bars and cancel actions](docs/screenshots/batches.png)](docs/screenshots/batches.png)

### Performance — p50 / p95 / p99 per queue

[](#performance--p50--p95--p99-per-queue)

[![Performance percentiles table](docs/screenshots/performance.png)](docs/screenshots/performance.png)

### Workers — live heartbeats

[](#workers--live-heartbeats)

[![Worker heartbeats with host, pid, queues, and status](docs/screenshots/workers.png)](docs/screenshots/workers.png)

### Queues — live driver sizes

[](#queues--live-driver-sizes)

[![Live queue depth from driver adapters](docs/screenshots/queues.png)](docs/screenshots/queues.png)

Testing
-------

[](#testing)

```
composer install
./vendor/bin/pest
```

The suite uses Orchestra Testbench with in-memory SQLite. CI runs the full matrix of PHP 8.2 / 8.3 / 8.4 × Laravel 11 / 12 / 13 on every push.

Contributing
------------

[](#contributing)

Bug reports and pull requests are welcome. Please run `./vendor/bin/pint` and `./vendor/bin/pest` before opening a PR.

Security issues: please email  rather than filing a public issue.

Credits
-------

[](#credits)

- [Maher ElGamil](https://github.com/maherelgamil)
- All contributors

License
-------

[](#license)

The MIT License (MIT). See [LICENSE](LICENSE) for the full text.

###  Health Score

33

—

LowBetter than 72% of packages

Maintenance63

Regular maintenance activity

Popularity12

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

14

Last Release

87d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/36c2155f42ace990363515c144c4903ae8eca6d69742f74ad329ec4feb721709?d=identicon)[maherbusnes](/maintainers/maherbusnes)

---

Top Contributors

[![maherelgamil](https://avatars.githubusercontent.com/u/6294478?v=4)](https://github.com/maherelgamil "maherelgamil (52 commits)")

---

Tags

beanstalkddashboardhorizonlaravellaravel-packagelivewirephpprometheusqueuequeue-monitorredissqstailwindcsslaravelredisqueuebeanstalkdsqsdashboardmonitorhorizon

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/maherelgamil-periscope/health.svg)

```
[![Health](https://phpackages.com/badges/maherelgamil-periscope/health.svg)](https://phpackages.com/packages/maherelgamil-periscope)
```

###  Alternatives

[laravel/horizon

Dashboard and code-driven configuration for Laravel queues.

4.2k95.4M321](/packages/laravel-horizon)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M136](/packages/laravel-pulse)[tallstackui/tallstackui

TallStackUI is a powerful suite of Blade components that elevate your workflow of Livewire applications.

728176.2k14](/packages/tallstackui-tallstackui)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[moonshine/moonshine

Laravel administration panel

1.3k253.1k86](/packages/moonshine-moonshine)[illuminate/broadcasting

The Illuminate Broadcasting package.

7127.2M221](/packages/illuminate-broadcasting)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
