jkocik/laravel-profiler
Laravel Profiler is a powerful debugging tool for Laravel apps. It collects request data, database queries, events, logs, memory usage, and performance metrics, then presents them in a clean timeline UI so you can quickly find bottlenecks and optimize your application.
Install it as a dev-only dependency so it never ships to production:
composer require --dev jkocik/laravel-profiler
Laravel's package discovery registers the service provider automatically. Publish the config (and assets if the toolbar renders but looks unstyled):
php artisan vendor:publish --provider="JKocik\LaravelProfiler\LaravelProfilerServiceProvider"
# or, if the package exposes tags:
php artisan vendor:publish --tag=laravel-profiler-config
php artisan vendor:publish --tag=laravel-profiler-assets
Assets typically land in public/vendor/laravel-profiler; re-publish after upgrading.
Where to look first
config/laravel-profiler.php — the enabled, only_in_local / environment guards, toolbar toggle, and the list of collectors are all here. Read this before touching anything else.First use case
Open your slowest page in APP_ENV=local with APP_DEBUG=true. Click the timeline segment in the toolbar, then the database panel. You will usually find either an N+1 query pattern or one long-running block in the timeline. That is the 30-second win this package is built for — a Symfony-style profiler view of a single request without wiring up Xdebug or Telescope.
Keep it strictly local. The common pattern is to leave enabled driven by an env flag and gate it on environment:
// config/laravel-profiler.php
'enabled' => env('LARAVEL_PROFILER_ENABLED', false),
'only_in_local' => true,
Then set LARAVEL_PROFILER_ENABLED=true only in .env.local / .env.testing as needed. Never rely on the default in a shared staging environment.
Profile one request at a time. Rather than leaving profiling on for every page (which inflates overhead and fills storage), use the package's "watch"/force option or toggle enabled in .env, hit the single route you care about, then turn it back off. This is the day-to-day workflow: reproduce → capture one profile → read the panels → disable.
Use it as a before/after measurement tool. Capture a profile of the endpoint, note the query count, total timeline duration, and number of events. Make the refactor (eager loading, caching, moving work to a queue). Capture again and compare the same three numbers. The profiler's value is comparative, not absolute — the wall-clock overlap view shows you which part of the request got cheaper.
Read the timeline as a call stack, not a list. The timeline panel nests events, so a slow controller shows as a controller block containing a query block containing a hydration block. Scan top-down for the widest child rather than the widest parent.
Annotate your own code. If the package exposes a facade/helper (or via Log and dispatched events, which its collectors pick up), drop markers around suspicious sections:
profiler()->start('pricing-calculation');
// ...
profiler()->stop('pricing-calculation');
Even without a dedicated API, Log::debug() and custom events show up in the logs/events panels and give you named segments for free.
Add custom collectors for domain-specific data (e.g. feature flags resolved, external API calls). Implement the package's collector contract and register it in the collectors array in config. This is the intended extension point and keeps your custom metrics in the same UI as the built-ins.
Do not stack debug tools. Running this alongside barryvdh/laravel-debugbar and Telescope triples the request listener overhead and produces confusing, duplicated measurements. Pick one profiler per environment.
public/vendor/laravel-profiler exists and is web-accessible. If you serve from a subdirectory, check APP_URL and ASSET_URL; the toolbar's CSS/JS paths are derived from them.php artisan config:clear (and view:clear if you publish/override views). A cached config is the most common reason a toggle "does nothing."config/laravel-profiler.php. On a query-heavy page the profiler can double the request time — never trust its absolute timings as production numbers.enabled => true. Enforce the env guard in CI (APP_ENV=production plus LARAVEL_PROFILER_ENABLED unset) and add the package to require-dev only. Remember composer install --no-dev in deploy pipelines.enabled true, (3) environment matches only_in_local, (4) response Content-Type is text/html, (5) assets published, (6) no other middleware replaced the response. Work down that list in order — it resolves most reports.composer.json, expect occasional Laravel-version lag, and keep a fallback (Debugbar/Telescope) in mind if an upgrade breaks it. Because it is dev-only, a broken upgrade should never block a production deploy — that is the main reason to keep it out of require.How can I help you explore Laravel packages today?