debugd-laravel maintained by debugd
debugd for Laravel
Zero-instrumentation request tracing for Laravel — the passive collector for the debugd server.
It quietly ships each request's queries, logs, timings, exceptions, and your own dumps to a
local debugd server, which serves a live UI. When DEBUGD_HOST is unset, the package does
literally nothing — no listeners, zero overhead — so it's safe to leave installed.
your Laravel app ──POST /ingest──▶ debugd (one Go binary) ──live UI──▶ your browser
This is the read-only split of
ShahramMebashar/debugd(thedebugd-laravel/directory). Open issues and PRs there.
Requirements
- PHP
^8.2 - Laravel 11, 12, or 13 (
illuminate/support^11 || ^12 || ^13)
Install
composer require --dev debugd/debugd-laravel
echo "DEBUGD_HOST=http://localhost:9100" >> .env
Start the debugd server (./bin/debugd --open),
hit any page that does some work, and the trace shows up instantly. Remove DEBUGD_HOST and
the package goes inert again.
The service provider is auto-discovered — no manual registration needed.
The debugd() helper
Leave these in your code. They're a no-op in production (when DEBUGD_HOST is unset), and
bench() still returns its result either way.
debugd($user); // dump anything into the trace
debugd()->dump($payload, 'stripe response'); // ...with a label
$rows = debugd()->bench('report', fn () => Report::heavy()); // time it, get the result
debugd()->info('reached checkout', ['step' => 3]);
// run things in parallel (real parallelism under Octane, sequential otherwise),
// each shown as its own span:
[$user, $orders] = array_values(debugd()->concurrently([
'user' => fn () => User::find($id),
'orders' => fn () => Order::recent($id),
]));
Configuration
| Env var | Default | What |
|---|---|---|
DEBUGD_HOST |
(unset) | debugd server URL. Unset = package fully disabled. |
DEBUGD_CAPTURE_BINDINGS |
false |
Ship raw query bindings. Off by default — no values leave your app otherwise. |
How it works
The package hangs off DB::listen, a Monolog handler, and the exception reporter, buffers
everything per-request, and POSTs it from terminate() after the response is already sent —
with tight timeouts and every failure swallowed, so tracing can't slow down or break a
request. It's Octane- and FrankenPHP-safe (state is per-request, tested).
Not doing (on purpose)
No persistence, no production telemetry, no sampling, no auth. It's a local dev tool. The
frozen wire format lives in PROTOCOL.md.
License
MIT.