0

Horizon, Telescope, Octane — what each tool does in one sentence

Beginner5 min read·eng-14-015

Concept

Horizon, Telescope, Octane — three official Laravel packages that extend the framework in specific ways. Quick one-sentence definitions:

  • Horizon: A dashboard and configuration system for Laravel's Redis queues. Supervises queue workers, provides real-time monitoring, failed job management, and throughput metrics.

  • Telescope: A debugging assistant (devtools) that captures and displays requests, queries, exceptions, logs, queue jobs, mail, notifications, cache operations, and more — all in a web UI.

  • Octane: A high-performance application server that keeps your Laravel application in memory between requests (using Swoole or RoadRunner), eliminating the bootstrap overhead of PHP-FPM.

Horizon in detail:

  • Configured in config/horizon.php — define workers, queues, connection, max processes.
  • Dashboard at /horizon shows: queue throughput, processed/failed/pending jobs, worker status.
  • Automatic balancing: Horizon scales workers up/down based on queue backlog.
  • Tags: Associate jobs with tags (e.g., user ID) to find all jobs for a specific user.
  • Uses Redis sorted sets for the monitoring data.

Telescope in detail:

  • Dev-only tool. Install with --dev. Not for production (it stores everything).
  • Captures: HTTP requests, SQL queries (with bindings), exceptions, mail, notifications, events, cache.
  • storage:prune to clean up old entries.
  • Authorization gate to restrict access.

Octane in detail:

  • Swoole: C extension, true async I/O, coroutines, HTTP server in PHP.
  • RoadRunner: Go-based worker, no C extension required.
  • App boots ONCE per worker, serves N requests in memory. 2-10x throughput improvement.
  • Gotcha: Stale state between requests — static properties, singletons hold state. Must be careful.

Code Example

php
<?php
// HORIZON CONFIGURATION (config/horizon.php)
return [
    'environments' => [
        'production' => [
            'supervisor-1' => [
                'connection'   => 'redis',
                'queue'        => ['critical', 'default', 'low'],
                'balance'      => 'auto',      // scale workers based on load
                'processes'    => 10,
                'tries'        => 3,
                'maxProcesses' => 20,
            ],
        ],
    ],
];
// php artisan horizon         — start Horizon (manages workers + monitoring)
// php artisan horizon:pause  — pause all workers
// php artisan horizon:status — check if running

// Job with Horizon tag (for filtering in dashboard)
class ProcessPayment implements \Illuminate\Contracts\Queue\ShouldQueue
{
    use \Illuminate\Foundation\Bus\Dispatchable, \Illuminate\Queue\InteractsWithQueue;

    public function __construct(private readonly int $userId, private readonly int $orderId) {}

    public function tags(): array
    {
        return ["user:{$this->userId}", "order:{$this->orderId}"]; // searchable in Horizon
    }

    public function handle(): void { /* ... */ }
}

// TELESCOPE — reads automatically (zero config in dev)
// Any request, query, exception is automatically captured
// Access: http://localhost/telescope
// Authorization:
\Laravel\Telescope\Telescope::auth(function ($request) {
    return in_array($request->user()?->email, ['admin@example.com']);
});

// OCTANE — config/octane.php
return ['server' => 'swoole', 'workers' => 4, 'task_workers' => 6];
// php artisan octane:start   — start the Swoole/RoadRunner server
// php artisan octane:reload  — hot-reload workers without dropping connections

// Octane safe singletons (scoped to request, not app lifetime)
class OrderController extends Controller
{
    public function __construct(
        // Inject in constructor — Octane resolves per-request for scoped bindings
        private readonly OrderService $orderService,
    ) {}
}

// Register scoped bindings that reset per request
// In AppServiceProvider:
$this->app->scoped(RequestContext::class, fn() => new RequestContext());
// 'scoped' = new instance per request, not per app boot