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

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

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

  [ home ](https://msaied.com)    [ articles ](https://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%2Fmsaied.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%2Fmsaied.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://msaied.com/articles) 

 [ ![Laravel New in 12: First-Class Typed Config, Slim Skeletons, and Upgrade Notes](https://cdn.msaied.com/401/bd0219f2cb584b037df76f985db348f8.png) laravel laravel-12 php 

### Laravel New in 12: First-Class Typed Config, Slim Skeletons, and Upgrade Notes

Laravel 12 ships with typed configuration objects, a leaner application skeleton, and several quality-of-life...

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

 9 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-new-in-12-first-class-typed-config-slim-skeletons-and-upgrade-notes) [ ![Laravel API Resources: Sparse Fieldsets, Conditional Relationships, and Versioning](https://cdn.msaied.com/400/67fe9d3c6c505a6f67092e2761690696.png) laravel api resources 

### Laravel API Resources: Sparse Fieldsets, Conditional Relationships, and Versioning

Go beyond basic transformers. Learn how to implement sparse fieldsets, conditionally load relationships, and v...

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

 9 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-api-resources-sparse-fieldsets-conditional-relationships-and-versioning-1) [ ![Laravel Eloquent Global Scopes: Bootable Traits, Scope Removal, and Testing Pitfalls](https://cdn.msaied.com/399/71a080bda5c9aa713a95cec2c8b42247.png) laravel eloquent testing 

### Laravel Eloquent Global Scopes: Bootable Traits, Scope Removal, and Testing Pitfalls

Global scopes silently shape every query on a model. Learn how to write bootable trait scopes, safely remove t...

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

 9 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-eloquent-global-scopes-bootable-traits-scope-removal-and-testing-pitfalls) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://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://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://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)
