topthink/think-trace
topthink/think-trace adds a lightweight trace panel to ThinkPHP applications, displaying request parameters, SQL queries, errors, and execution timing during development. Use it to inspect request flow, diagnose issues, and spot performance bottlenecks without external debugging tools.
topthink/think-trace is the ThinkPHP 6+ port of the classic TP5 debug bar: a small collapsible panel appended to the bottom of HTML responses that shows base request/response info, loaded files, the execution timeline, errors/warnings, SQL statements and timing, and anything logged through the Log facade during the request.
Minimal setup:
composer require topthink/think-trace --dev
That is usually all you need. The package declares a service provider in its composer.json (extra.think.services), so ThinkPHP 6 auto-registers it — there is no app/provider.php entry to add and no middleware to wire up by hand.
The one requirement is debug mode. Ensure .env has:
APP_DEBUG = true
Then load any controller action that returns HTML and scroll to the bottom of the page. You should see the trace bar with tabs for base info, files, process/timeline, errors, SQL, and debug/log output.
Where to look first:
vendor/topthink/think-trace/src/ — the service class, the middleware/HttpEnd listener that injects the bar, and the HTML renderer/driver. That's the whole package; it is small enough to read in ten minutes.config/trace.php — if the package ships a publishable config, copy it into your app config directory to customize tabs. If it doesn't appear there, the defaults are baked into the package's src/config.php.think_page_trace to confirm injection is happening and see where the bar is being appended.First real use case: you have a slow page and no profiler. Load it with APP_DEBUG=true, open the SQL tab, and you immediately get every query for that request with bindings and per-query time, plus the timeline showing where the request spent its milliseconds. That is the 80% use case — it replaces ad-hoc dump()/Db::getQueryTimes() debugging.
Zero-config default. Rely on auto-injection. The service hooks the response lifecycle and appends the panel to HTML output at the end of the request. You do not call anything from your controllers.
Customizing tabs and panels. Publish or hand-copy the config to config/trace.php and shape it there. Typical edits:
// config/trace.php
return [
'tabs' => [
'base' => 'Base',
'file' => 'Files',
'info' => 'Timeline',
'error' => 'Errors',
'sql' => 'SQL',
'debug' => 'Debug',
],
// drop tabs you never read to keep the bar fast on query-heavy pages
];
Turning off the file tab alone is a meaningful speedup on pages that autoload hundreds of classes, because building that list is the most expensive part of rendering the bar.
Feeding your own data into the bar. The panel is fed by ThinkPHP's own collectors, so anything you route through the framework shows up automatically:
use think\facade\Log;
public function report()
{
Log::debug('report: rows=' . $count); // appears in the Debug tab
Log::warning('cache miss for key report'); // appears in the Error tab
// ...
}
SQL is collected by the Db service listener, so Db::name(...)->select() calls appear without any instrumentation on your side. If you use raw PDO connections outside the Db facade, those queries will not show up — a common source of "the bar is lying about my query count."
Using it as a console reporter instead of a bar. For XHR/JSON-heavy apps where the HTML bar never renders, switch the output type to console in the config so trace data is dumped to the browser console or to the log rather than appended to the body. This is the practical way to get SQL/timeline visibility on an API-only project.
Gating per environment. Keep the dependency in require-dev and let APP_DEBUG do the runtime gating. In CI/production deploys, composer install --no-dev removes the package entirely, so there is no code path that can leak the bar to end users. Add a smoke check to your deploy: request a production page and assert think_page_trace is absent from the HTML.
Team workflow. Treat the bar as a shared, zero-onboarding profiler: it gives every developer the same view of queries and timing without anyone installing Xdebug or a separate APM. It pairs naturally with topthink/think-tinker for quick experiments and with framework logging for anything that needs to survive the request.
APP_DEBUG=true is the master switch. If you have environments where debug is accidentally on (a staging box, a misconfigured .env), you will leak internals — file paths, SQL, session values — to whoever loads the page. Treat "debug off in prod" as a security control, not a convenience.Content-Length and compression. If a middleware or the web server has already set Content-Length, or output is gzipped, appending the bar after the fact can truncate the page or produce garbage. If pages render with a chopped-off footer, check middleware order and any output-compression layer before blaming the app.Response::create($content) and returned through the normal pipeline get the bar; early return response(...) calls from a middleware that short-circuits the pipeline may not. When the bar "randomly" disappears for one route, look for a middleware returning early.config/trace.php, clear the compiled config (php think clear or delete runtime/), otherwise your tab changes appear to do nothing.require-dev so a framework upgrade doesn't silently swap behavior under you.How can I help you explore Laravel packages today?