Dumpio

Dokumentace

This page covers the Laravel side of the dumpio/client package. Read the PHP library page first for the basics (dio(), Dumpio::dump(), typed helpers) — everything there works in Laravel too. Here we add what Laravel makes automatic.

The big win in Laravel: automatic capture. With one env line you can stream every SQL query, every reported exception, every outgoing email, queued job, Livewire interaction, and more into Dumpio — without touching your application code.

Setup

The service provider is auto-discovered — you don't register anything. Just require the package:

1composer require --dev dumpio/client

Then, in your .env, turn Dumpio on for local development:

1DUMPIO_ENABLED=true

That's the minimum. Now dio(), Dumpio::dump(), and all the helpers send to the viewer. Automatic capture is opt-in — see below.

Is it on? The two switches

Dumpio must be enabled before anything is sent, and each capture is a separate switch on top of that.

  • DUMPIO_ENABLED is the master switch. If you don't set it, it falls back to APP_DEBUG. So it's on in local dev and off in production automatically — a normal production deploy sends nothing and registers no listeners.
  • Everything under "automatic capture" (queries, exceptions, mail, …) is off by default even when Dumpio is enabled. You turn each one on individually.

If nothing appears, the first thing to check is DUMPIO_ENABLED=true (or APP_DEBUG=true) and that the Dumpio app is running.

The config file

You can drive everything from .env, but if you want to see or change defaults, publish the config:

1php artisan vendor:publish --tag=dumpio-config

config/dumpio.php:

1return [
2 'host' => env('DUMPIO_HOST', 'localhost'),
3 'port' => (int) env('DUMPIO_PORT', 21234),
4 'token' => env('DUMPIO_TOKEN', ''),
5 'enabled' => env('DUMPIO_ENABLED', env('APP_DEBUG', false)), // off in prod
6 
7 'listen_queries' => env('DUMPIO_LISTEN_QUERIES', false),
8 'listen_exceptions' => env('DUMPIO_LISTEN_EXCEPTIONS', false),
9 'intercept_dumps' => env('DUMPIO_INTERCEPT_DUMPS', false),
10 'listen_models' => env('DUMPIO_LISTEN_MODELS', false),
11 'listen_cache' => env('DUMPIO_LISTEN_CACHE', false),
12 'listen_jobs' => env('DUMPIO_LISTEN_JOBS', false),
13 'listen_events' => env('DUMPIO_LISTEN_EVENTS', false),
14 'listen_mail' => env('DUMPIO_LISTEN_MAIL', false),
15 'intercept_mail' => env('DUMPIO_INTERCEPT_MAIL', false),
16 'listen_http_client' => env('DUMPIO_LISTEN_HTTP_CLIENT', false),
17 'listen_livewire' => env('DUMPIO_LISTEN_LIVEWIRE', false),
18 'listen_livewire_verbose' => env('DUMPIO_LISTEN_LIVEWIRE_VERBOSE', false),
19 'register_macros' => env('DUMPIO_REGISTER_MACROS', true),
20 'stamp_requests' => env('DUMPIO_STAMP_REQUESTS', true),
21];

Automatic capture

Each switch below is off by default. Flip the env var (or the config key) to forward that signal — no code changes needed.

Env var What it forwards Dump type / channel
DUMPIO_LISTEN_QUERIES Every executed SQL query (via DB::listen), with bindings and time query
DUMPIO_LISTEN_EXCEPTIONS Reported exceptions, with request/user context exception
DUMPIO_INTERCEPT_DUMPS The framework's dump() / dd() routed into the viewer var (dump)
DUMPIO_LISTEN_MODELS Eloquent created / updated / deleted / restored model (models)
DUMPIO_LISTEN_CACHE Cache hit / missed / written / forgotten event (cache)
DUMPIO_LISTEN_JOBS Queue job processing / processed / failed event (jobs)
DUMPIO_LISTEN_EVENTS Your application (non-framework) events event (events)
DUMPIO_LISTEN_MAIL Every outgoing email, as a rendered preview mail (mail)
DUMPIO_INTERCEPT_MAIL With LISTEN_MAIL: also cancel the real send (dev only)
DUMPIO_LISTEN_HTTP_CLIENT Outgoing Http:: requests, with status and timing http
DUMPIO_LISTEN_LIVEWIRE Livewire component lifecycle (see below) event (livewire)
DUMPIO_LISTEN_LIVEWIRE_VERBOSE The high-volume Livewire points (boot/hydrate/dehydrate/destroy) — opt-in on top of LISTEN_LIVEWIRE event (livewire)

A typical local setup:

1DUMPIO_ENABLED=true
2DUMPIO_LISTEN_QUERIES=true
3DUMPIO_LISTEN_EXCEPTIONS=true
4DUMPIO_INTERCEPT_DUMPS=true

Queries

DUMPIO_LISTEN_QUERIES=true forwards every executed query as a query dump — the SQL, the bound parameters, the connection, and how many milliseconds it took. This is the easiest way to spot N+1 problems: load a page and watch the query list fill up — and with request correlation on (the default), the viewer flags the request with an amber N+1 ×N badge when the same query repeats.

Exceptions

DUMPIO_LISTEN_EXCEPTIONS=true forwards reported exceptions as exception dumps with a parsed stack trace and the current request/user context, so you can inspect a failure without digging through laravel.log. (See the app's Exceptions page for how they render.)

dump() / dd() interception

DUMPIO_INTERCEPT_DUMPS=true reroutes Laravel's own dump() and dd() into Dumpio (via VarDumper::setHandler) instead of rendering them inline. Your existing dd() calls now land in the viewer — and dd() still stops the request as usual.

Models, cache, jobs, events

These forward framework lifecycle signals as model or event dumps, each on its own channel (models, cache, jobs, events) so you can filter them. DUMPIO_LISTEN_EVENTS deliberately filters out framework-internal events to avoid a firehose.

Mail capture and the Mailpit-style trap

DUMPIO_LISTEN_MAIL=true hooks Laravel's MessageSending event and sends every outgoing email to the viewer as a mail dump — a full preview with rendered HTML, the text part, and the subject/from/to/attachments envelope, shown in a sandboxed iframe.

Add DUMPIO_INTERCEPT_MAIL=true and the listener also cancels the real send — a dev-only trap (like Mailpit/Mailtrap) so your test emails land only in Dumpio and never reach a real inbox. It requires listen_mail.

1DUMPIO_LISTEN_MAIL=true
2DUMPIO_INTERCEPT_MAIL=true # don't actually send while developing

Outgoing HTTP-client calls

DUMPIO_LISTEN_HTTP_CLIENT=true forwards every outgoing request made with Laravel's Http:: client as an http dump with status and timing, via a global Guzzle middleware. Requires guzzlehttp/guzzle (which Laravel already uses). Great for seeing what a third-party API actually returned.

Livewire

DUMPIO_LISTEN_LIVEWIRE=true streams the lifecycle of your Livewire components to the viewer, so you can see a whole round-trip — what mounted, which action ran, which property changed, and what re-rendered — without echoing anything into the Blade markup. It requires livewire/livewire and works with Livewire 3 and 4 (a single hook covers both).

Each step becomes an event dump on the livewire channel (filter to #livewire to watch just these):

Event When it fires Livewire Payload
livewire.mount A component mounts v3 + v4 the mount parameter names
livewire.action An action method is called v3 + v4 the method name and its parameters
livewire.update A property is updated (data binding) v3 + v4 the property path and its new value
livewire.render The component re-renders v3 + v4 the view name
livewire.island An island (partial) re-renders v4 only the island name

Component exceptions are forwarded as full exception dumps on the same livewire channel (on both v3 and v4), so a failing update shows its stack trace instead of a silent 500.

Livewire 3 and 4. You don't configure anything version-specific. The integration registers a single Livewire componentHook(), whose API is identical across both majors, so one code path serves v3 and v4 alike. On both versions you get mount, action, update, render, and forwarded exceptions; livewire.island is emitted only on Livewire 4, where islands (partial re-renders) exist — on v3 that hook is simply never called, so nothing is emitted and nothing breaks. The hook is registered automatically whenever livewire/livewire is installed; if Livewire is absent, the feature is completely inert.

1DUMPIO_ENABLED=true
2DUMPIO_LISTEN_LIVEWIRE=true

Then interact with a Livewire component and watch the #livewire channel: you'll see the mount, each action, the property updates it triggered, and the render — in order.

Verbose lifecycle points (opt-in)

The default capture stops at the events above. If you also need the low-level lifecycle — for example to inspect the state serialized to and from the wire — enable the verbose tier. These points fire on nearly every request for every component, so they sit behind a separate switch to keep the default stream readable. All four exist on both Livewire 3 and 4:

Event When it fires Livewire Payload
livewire.boot The component boots (every request) v3 + v4 the component name
livewire.hydrate State is restored from the wire (every request) v3 + v4 the component name
livewire.dehydrate State is serialized back to the wire (every request) v3 + v4 the component name
livewire.destroy The component is destroyed v3 + v4 the component name

Turn it on alongside the standard capture:

1DUMPIO_LISTEN_LIVEWIRE=true
2DUMPIO_LISTEN_LIVEWIRE_VERBOSE=true

or at runtime with Dumpio::showLivewireVerbose() / Dumpio::stopShowingLivewireVerbose(). The four verbose events land on the same #livewire channel with a gray flag, so they filter and search alongside the rest.

Turning capture on and off at runtime

The env flags only seed the initial state. Any capture can be flipped at runtime — handy inside a test, a Tinker session, or around one suspect block:

1Dumpio::showQueries(); // start forwarding queries now
2$report->build(); // …the code you want to inspect…
3Dumpio::stopShowingQueries(); // and stop
4 
5Dumpio::showAll(); // everything on
6Dumpio::stopShowingAll(); // everything off

There's a show…() / stopShowing…() pair for Queries, Exceptions, Dumps, Models, Cache, Jobs, Events, Mail, HttpClient, Livewire, and LivewireVerbose. The generic form is Dumpio::enableFeature('queries') / disableFeature('queries').

The facade

The provider registers a Dumpio facade alias, so you can use it without importing the class:

1use Dumpio\Laravel\Facades\Dumpio;
2 
3Dumpio::query($sql, $bindings, $timeMs);
4Dumpio::dump($user, 'user');

This is equivalent to \Dumpio\Dumpio::… or the global helpers (dumpio(), dio(), dumpio_query(), …).

Chainable macros: ->dio() and ->ddio()

The provider registers ->dio() and ->ddio() macros on query builders and collections (on by default; disable with DUMPIO_REGISTER_MACROS=false). They let you drop a dump mid-chain without breaking it — the dump ships the current state and returns $this:

1User::query()
2 ->where('active', true)
3 ->dio() // → query dump (SQL + bindings so far)
4 ->whereDate('created_at', today())
5 ->dio() // → query dump (with the extra clause)
6 ->get();
7 
8collect($users)->dio('after filter'); // → var dump, returns the collection

->ddio() is the dump-and-die variant. The macros work on Eloquent\Builder, Query\Builder (DB::table(...)), and Support\Collection.

Send logs to the viewer (Monolog)

Point a log channel at Dumpio\Log\DumpioHandler to stream application logs into Dumpio as log dumps (the level picks the color):

1// config/logging.php
2'channels' => [
3 'dumpio' => [
4 'driver' => 'monolog',
5 'handler' => \Dumpio\Log\DumpioHandler::class,
6 ],
7],

Then log to it directly — Log::channel('dumpio')->warning('Auth failed', ['ip' => $ip]) — or add 'dumpio' to a stack channel's channels list to mirror your default log into the viewer. The handler never throws back into the logging pipeline.

Request correlation

Every dump emitted during one HTTP request automatically shares a requestId and a requestLabel ("METHOD /path"), so the viewer can group them under a collapsible request header — you see "what happened in this request" at a glance. This is on by default; a global middleware opens a scope on request start and closes it on terminate. Console commands and queued jobs have no request, so their dumps stay ungrouped.

Turn it off with stamp_requests (DUMPIO_STAMP_REQUESTS=false). You can also open a scope by hand — handy in a test or a long CLI job:

1Dumpio::beginRequest('import users'); // returns the id; label is optional
2// … dumps here share the correlation …
3Dumpio::endRequest();

Production safety

Leaving the package installed in production is safe: with enabled following APP_DEBUG, a production deploy configures the client as disabled and registers no listeners — a complete no-op. If you ever enable Dumpio on a shared or staging environment, gate individual dumps with ->when(...) so they only fire when you intend.