cakephp/debug_kit
CakePHP DebugKit adds a debugging toolbar to your app, with panels for SQL queries, request data, cache, variables, and timing. It helps developers inspect and profile requests during development without digging through logs.
First, calibrate expectations: cakephp/debug_kit is the CakePHP debug toolbar, not a Laravel package. There is no service provider, no facade, and no config/debugbar.php. As a Laravel developer you will only touch this if you maintain a CakePHP app, are porting one, or are lifting UI/panel ideas for your own tooling. Your day-to-day Laravel equivalents are barryvdh/laravel-debugbar (toolbar, query/variable panels) and laravel/telescope (persistent request/job/mail/exception recorder) — DebugKit sits somewhere between the two, with a MailPreview panel that resembles Mailhog plus a lightweight Timer panel similar to Telescope's request timeline.
If you are in a CakePHP codebase:
composer require --dev cakephp/debug_kit — note the major-version pairing: DebugKit 4.x is for CakePHP 4.x, DebugKit 5.x for CakePHP 5.x. This release is on the 4.x line.src/Application.php → bootstrap(), gated on debug mode:
if (Configure::read('debug')) {
$this->addPlugin('DebugKit');
}
bin/cake plugin load DebugKit does the same thing.debug = true and a toolbar appears at the bottom.First use case worth wiring up on day one: the Timer panel via DebugTimer. As of 4.10.3 there is a documentation section on custom profiling — wrap any suspicious block and it shows up as a named span alongside CakePHP's own boot/render timings:
use DebugKit\DebugTimer;
DebugTimer::start('report:aggregate');
$this->fetchHeavyReport();
DebugTimer::stop('report:aggregate');
Gate the whole toolbar on debug, never on environment name. CakePHP's Configure::read('debug') is what the toolbar itself checks. Loading the plugin unconditionally in bootstrap() is the most common way people accidentally ship a toolbar-shaped information disclosure to production.
Panel configuration is the real integration surface. Trim noise per-app in config/app_local.php (or bootstrap.php):
Configure::write('DebugKit.panels', [
'DebugKit.SqlLog', 'DebugKit.Timer', 'DebugKit.Variables', 'DebugKit.MailPreview',
]);
Drop DebugKit.History and DebugKit.Routes on large apps — they are the slowest panels and rarely earn their keep outside routing work.
Custom profiling is the highest-leverage pattern. Rather than instrumenting ad hoc, define a small set of stable timer names (db:search, api:payments, pdf:render), start/stop them at service boundaries, and keep them in the codebase permanently behind a DebugTimer call that is a no-op in production. This gives you a repeatable flame-ish profile across every request, not a one-off debug() session.
MailPreview as a testing workflow. The MailPreview panel intercepts mail sent during a request and lets you inspect rendered HTML/text without a real SMTP round-trip. Use it as the fast inner loop for template work: trigger the mail, click through the panel, iterate on the template — reserve a real inbox (Mailhog/Mailpit) for header and deliverability checks. Note that this release further constrains the classes instantiated by the MailPreview controller, so if you have custom preview or mailer classes registered, verify they still resolve after upgrading.
Mirror panes you actually use. If your team is Laravel-first but maintaining a CakePHP surface, keep the mental mapping close: SqlLog ≈ Debugbar Queries, Variables ≈ Debugbar Messages/Dumps, MailPreview ≈ Mailhog inbox, Timer ≈ Telescope request watchers. Custom DebugTimer spans should get the same names as your Laravel Benchmark/Telescope::measure labels so traces are comparable across repos.
app.local, app.test, or a Docker hostname, the toolbar refuses to render until you whitelist it:
Configure::write('DebugKit.safeTld', ['local', 'test', 'docker']);
This is the single most common "it's installed but I see nothing" cause.DebugTimer::start('x') without a matching stop() leaves a dangling span; nested/duplicate starts overwrite rather than nest. Wrap in try/finally if the profiled block can throw./debug-kit/...; a toolbar that appears but shows empty panels usually means the panel endpoint is being blocked by auth middleware, a CSP header, or a routed catch-all. Confirm the request in the network tab before assuming the plugin is broken.DebugKit.forceEnable = false or a non-safe TLD hides the UI, not the collection. Only debug = false (or not loading the plugin) actually stops it.DebugKit\Panel\Panel and are registered in DebugKit.panels — useful if you have a domain-specific invariant you want surfaced on every request (queue depth, feature-flag state, tenant id), but check the CakePHP major version before porting a panel between 4.x and 5.x.dont-discover. Anything you script around DebugKit is CakePHP-shaped and will not transfer; budget for a rewrite if the app ever moves to Laravel, and prefer laravel-debugbar + telescope for the target state so the instrumentation concepts map cleanly.How can I help you explore Laravel packages today?