Custom Laravel Debug Watchers Without Telescope | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com) [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles  ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

  [ home ](https://www.msaied.com)    [ articles ](https://www.msaied.com/articles)    Laravel Telescope Alternatives: Building a Lightweight Debug Bar with Custom Watchers        On this page       1. [  Why Not Just Use Telescope? ](#why-not-just-use-telescope)
2. [  The Core Abstraction: A Watcher Contract ](#the-core-abstraction-a-watcher-contract)
3. [  Building a Query Watcher ](#building-a-query-watcher)
4. [  Wiring Watchers via the Service Container ](#wiring-watchers-via-the-service-container)
5. [  A Minimal Livewire Panel Component ](#a-minimal-livewire-panel-component)
6. [  Guarding Against Production Exposure ](#guarding-against-production-exposure)
7. [  Extending: An HTTP Client Watcher ](#extending-an-http-client-watcher)
8. [  Takeaways ](#takeaways)

  ![Laravel Telescope Alternatives: Building a Lightweight Debug Bar with Custom Watchers](https://cdn.msaied.com/397/074621fcb646d45c6d18806bc309ee24.png)

  #laravel   #debugging   #livewire   #observability  

 Laravel Telescope Alternatives: Building a Lightweight Debug Bar with Custom Watchers 
=======================================================================================

     9 Jul 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Why Not Just Use Telescope?  ](#why-not-just-use-telescope)
2. [  02   The Core Abstraction: A Watcher Contract  ](#the-core-abstraction-a-watcher-contract)
3. [  03   Building a Query Watcher  ](#building-a-query-watcher)
4. [  04   Wiring Watchers via the Service Container  ](#wiring-watchers-via-the-service-container)
5. [  05   A Minimal Livewire Panel Component  ](#a-minimal-livewire-panel-component)
6. [  06   Guarding Against Production Exposure  ](#guarding-against-production-exposure)
7. [  07   Extending: An HTTP Client Watcher  ](#extending-an-http-client-watcher)
8. [  08   Takeaways  ](#takeaways)

 Why Not Just Use Telescope?
---------------------------

Telescope is excellent for local development, but its storage writes on every request make it a liability in staging environments under load. More importantly, it captures *everything* — you often only care about a narrow slice: slow queries, specific job failures, or a single HTTP client call. Building a focused watcher gives you exactly what you need with negligible overhead.

This article walks through a production-safe, opt-in debug panel backed by Laravel's event system and a thin Livewire component.

---

The Core Abstraction: A Watcher Contract
----------------------------------------

Define a simple interface every watcher must implement:

```php
namespace App\Debug\Contracts;

interface Watcher
{
    public function register(): void;
    public function entries(): array;
    public function flush(): void;
}

```

Each watcher self-registers its listeners and stores entries in a bounded in-memory buffer — no database writes.

---

Building a Query Watcher
------------------------

```php
namespace App\Debug\Watchers;

use App\Debug\Contracts\Watcher;
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Facades\Event;

class QueryWatcher implements Watcher
{
    private array $entries = [];
    private int $limit;

    public function __construct(int $limit = 50)
    {
        $this->limit = $limit;
    }

    public function register(): void
    {
        Event::listen(QueryExecuted::class, function (QueryExecuted $event) {
            if (count($this->entries) >= $this->limit) {
                return;
            }

            $this->entries[] = [
                'sql'  => $event->sql,
                'time' => $event->time,
                'conn' => $event->connectionName,
                'at'   => now()->toTimeString(),
            ];
        });
    }

    public function entries(): array { return $this->entries; }
    public function flush(): void   { $this->entries = []; }
}

```

The `$limit` guard is critical — without it a long-running Octane worker accumulates unbounded memory.

---

Wiring Watchers via the Service Container
-----------------------------------------

Tag all watchers so the panel can resolve them without knowing each class:

```php
// AppServiceProvider::register()
$this->app->singleton(QueryWatcher::class);

$this->app->tag([QueryWatcher::class], 'debug.watchers');

```

In a `DebugServiceProvider` boot method, conditionally activate them:

```php
public function boot(): void
{
    if (! config('debug-panel.enabled')) {
        return;
    }

    foreach ($this->app->tagged('debug.watchers') as $watcher) {
        $watcher->register();
    }
}

```

Set `debug-panel.enabled` to `true` only via an environment variable — never hard-code it.

---

A Minimal Livewire Panel Component
----------------------------------

```php
namespace App\Livewire;

use Livewire\Component;
use Illuminate\Contracts\Container\Container;

class DebugPanel extends Component
{
    public array $queries = [];

    public function mount(Container $container): void
    {
        foreach ($container->tagged('debug.watchers') as $watcher) {
            if ($watcher instanceof \App\Debug\Watchers\QueryWatcher) {
                $this->queries = $watcher->entries();
            }
        }
    }

    public function render()
    {
        return view('livewire.debug-panel');
    }
}

```

The Blade view renders a fixed-position overlay only when the `X-Debug-Panel` request header is present — checked in a middleware that sets a view composer variable.

---

Guarding Against Production Exposure
------------------------------------

```php
// routes/web.php
Route::get('/_debug', DebugPanelController::class)
    ->middleware(['auth', 'can:view-debug-panel']);

```

Couple this with a `Gate::define('view-debug-panel', ...)` that checks an internal role. Never rely solely on `APP_DEBUG`.

---

Extending: An HTTP Client Watcher
---------------------------------

Laravel's `Http` facade fires `RequestSending` and `ResponseReceived` events since Laravel 10. A `HttpClientWatcher` listens to both and correlates them by request URL — giving you latency per external call without any instrumentation in your own code.

---

Takeaways
---------

- **Tag-based resolution** lets you add watchers without touching the panel code.
- **In-memory buffers with hard limits** keep overhead negligible even on Octane.
- **Event-driven registration** means watchers are zero-cost when disabled.
- **Gate-protected routes** prevent accidental exposure; never trust `APP_ENV` alone.
- The pattern composes cleanly with Filament — render the panel as a custom Filament widget on a restricted admin page.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Flaravel-telescope-alternatives-building-a-lightweight-debug-bar-with-custom-watchers&text=Laravel+Telescope+Alternatives%3A+Building+a+Lightweight+Debug+Bar+with+Custom+Watchers) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Flaravel-telescope-alternatives-building-a-lightweight-debug-bar-with-custom-watchers) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Is it safe to enable this in a staging environment with real traffic?        Yes, provided you set a sensible entry limit per watcher (50–100 entries) and gate the panel behind authentication. The in-memory buffer never writes to disk or a database, so the overhead is a few microseconds per event listener invocation. 

      Q02  How do I add a watcher for failed jobs without polling the database?        Listen to Illuminate\Queue\Events\JobFailed inside a JobWatcher::register() method. The event carries the job payload, exception, and connection name. Store a trimmed snapshot (message + trace top 5 frames) in the bounded buffer — no queue table queries needed. 

      Q03  Can this coexist with Telescope in local development?        Absolutely. Keep Telescope in your local dev dependencies and enable the custom panel only in staging via an environment variable. They listen to the same framework events independently and do not conflict. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://www.msaied.com/articles) 

 [ ![DDD Value Objects and DTOs in Laravel Without the Bloat](https://cdn.msaied.com/450/41688537085b86f102fd8c219a35319f.png) laravel ddd php 

### DDD Value Objects and DTOs in Laravel Without the Bloat

Learn how to implement domain-driven value objects and data transfer objects in Laravel using PHP 8.3 readonly...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 21 Jul 2026     1 min read  

  Read    

 ](https://www.msaied.com/articles/ddd-value-objects-and-dtos-in-laravel-without-the-bloat) [ ![Laravel + Python FastAPI: Image OCR Demo](https://cdn.msaied.com/447/5f3d87146fbf19957973cbc88c6c0155.png) Laravel Python FastAPI 

### Laravel + Python FastAPI: Image OCR Demo

Learn how to call a Python image OCR script from Laravel using FastAPI as the bridge. This premium tutorial wa...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 20 Jul 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-python-fastapi-image-ocr-demo) [ ![Scaffold Laravel Packages with the `laravel package` Command in Installer v5.31.0](https://cdn.msaied.com/449/522e744d3c7bae1f214c179a83ae5b88.png) Laravel Installer Package Development CLI 

### Scaffold Laravel Packages with the `laravel package` Command in Installer v5.31.0

Laravel Installer v5.31.0 introduces a `laravel package` command to scaffold packages from the CLI, automated...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 20 Jul 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/scaffold-laravel-packages-with-the-laravel-package-command-in-installer-v5310) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://www.msaied.com)
- [Projects](https://www.msaied.com/projects)
- [Articles](https://www.msaied.com/articles)
- [Certificates](https://www.msaied.com/certificates)
- [Contact](https://www.msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
