Repository navigation
Description
Summary
Since #539 the debug toolbar reads its tabs at render time from data the packages keep in memory: the debug-mode logger's messages, the mailer's transport log and the database's query log. Nothing bounds them. In a web request the process ends after the response, so they stay small, but a console command or worker in debug mode keeps every log line, every SMTP line and every query for its whole run, and no toolbar ever reads them. Its log lines are never written to a file either.
Bound the logger and the mailer buffers with a limit from each package's own config, keep the console's log lines in the log file, and keep Idiorm's query log off outside debug mode. For that the app context records whether the run is the web app or the console, read through is_web_app() and is_console_app(), and the error handler uses the same fact instead of checking PHP_SAPI.
Current state (checked on master)
| Place | What it does |
|---|---|
LoggerFactory::resolve() |
in debug mode always resolves the in-memory message adapter, for the web and the console alike, even when a caller asks for a file adapter (CronManager asks for single) |
MessageAdapter |
appends every reported message; nothing removes them |
MailerTrait |
appends to $transportLog the SMTP exchange (through the Debugoutput callback set by SmtpAdapter in debug mode) and the errors of failed API sends; nothing removes them, and the mailer instance lives for the whole process |
IdiormDbal::getBaseConfig() |
passes the raw config()->get('app.debug') to Idiorm's logging option; DEBUG=false in .env arrives as the text "false", which PHP reads as true, so every query is kept even with debug mode off |
AppContext |
holds the base directory and the container; nothing records whether the run is web or console |
ErrorHandler::handleException() |
picks the console or the web output with PHP_SAPI === 'cli', the only such check in src |
Proposed change
- The app context knows its type.
AppContext::__construct(string $baseDir, DiContainer $container, string $type)takesAppType::WEBorAppType::CONSOLE, andgetType()returns it.AppFactorypasses the type it creates;WebContextandConsoleContextcopy it. Two helpers insrc/App/Helpers/app.php,is_web_app()andis_console_app(), read it, so no package compares the type itself. - The error handler uses it.
ErrorHandler::handleException()chooses the console output whenis_console_app()and the web page otherwise. A request served byphp -S(php qt serve) is the web app, as before. - The console logs to files in debug mode.
LoggerFactoryforces themessageadapter only whenis_web_app(). A console command in debug mode logs to the configured adapter, still at every level. - The debug-mode logger keeps its last messages.
MessageAdapterkeeps the lastlogging.message.limitmessages (default1000) and drops the oldest;0means no limit. It trims after each message. - The mailer's transport log keeps its last lines. At the start of each
send(), the transport log is cut to its lastmailer.transport_log_limitlines (default1000,0means no limit). The start ofsend()is the one point every send reaches, also after a previous send threw, so the log holds at most the limit plus the lines of the send in progress. Both writers stay as they are. - The query log is off outside debug mode. Idiorm's
loggingoption readsis_debug_mode(), which converts the text"false"from.envtofalse. In debug mode the web app and the console keep their query log as before, soDatabase::lastQuery(),queryLog()and the toolbar's queries tab work in both.
Each package reads only its own config key, so the logger and the mailer stay independent of each other.
Out of scope
- Clearing the buffers per request or per job in long-running processes belongs to the deferred and long-running execution work (Possibility to run defer tasks after response #168).
- Capping Idiorm's query log. In debug mode it still keeps every query of a long-running command. Idiorm calls its own logging through
self::and keeps the log in a protected static property, so it cannot be capped from outside; that needs Quantum to record the queries itself, as a follow-up.
Behavior changes
| Case | Today | After |
|---|---|---|
logger() in a console command in debug mode |
in-memory message adapter, lines lost |
the configured adapter, lines written to the log file |
| debug-mode logger in a long web request | keeps every message | keeps the last logging.message.limit messages |
| mailer transport log | keeps every line for the whole process | cut to the last mailer.transport_log_limit lines before each send |
Idiorm query log with DEBUG=false in .env |
keeps every query | off |
| an uncaught exception | console or web output chosen by PHP_SAPI |
chosen by the app type; the same result for php qt, php -S and PHP-FPM |
The toolbar's tabs read the same sources as before; they change only once a limit is reached.
Breaking changes
AppContext::__construct()takes a third, required$typeargument. Code that goes throughAppFactoryis unaffected.
Tests
AppContext::getType()returns the given type;AppFactorycreates a web and a console context with the right type;WebContextandConsoleContextkeep it.is_web_app()andis_console_app()are each true for their own type and false for the other.ErrorHandler::handleException()writes the console output for a console context and renders the web page for a web context.LoggerFactoryin debug mode resolvesmessagefor the web app and the configured or requested adapter for the console.MessageAdapterkeeps the lastlimitmessages in order, keeps everything with0, and uses1000without the key.- The transport log is cut to its last
limitlines at the next send, keeps everything with0, and never cuts the lines of the send in progress. - Idiorm's
loggingoption is on in debug mode for the web app and the console, and off outside debug mode, also when debug mode is the text"false".
Acceptance criteria
- a console command in debug mode writes its log lines to the configured log file
- the debug-mode logger and the mailer's transport log are bounded by their own config keys
- Idiorm keeps no query log outside debug mode
- no
PHP_SAPIcheck is left insrc; the web-or-console decision goes throughis_web_app()oris_console_app() composer test,composer cs:check,composer phpstanandcomposer rector:checkpass
Notes
Relevant code:
src/App/AppContext.php,src/App/Factories/AppFactory.php,src/App/Adapters/Web/WebContext.php,src/App/Adapters/Console/ConsoleContext.php,src/App/Helpers/app.phpsrc/Tracer/ErrorHandler.phpsrc/Logger/Factories/LoggerFactory.php,src/Logger/Adapters/MessageAdapter.phpsrc/Mailer/Traits/MailerTrait.phpsrc/Database/Adapters/Idiorm/IdiormDbal.php
Follows #539.
Activity
[-]Bound the debug-mode buffers and log to files from the console in debug mode[/-][+]Bound the debug-mode buffers, log to files from the console in debug mode, and record the app type on the context[/+][-]Bound the debug-mode buffers, log to files from the console in debug mode, and record the app type on the context[/-][+]One bounded debug log: cap the debug-mode logger, route mailer diagnostics to it, log to files from the console, record the app type[/+][-]One bounded debug log: cap the debug-mode logger, route mailer diagnostics to it, log to files from the console, record the app type[/-][+]Bound the debug data kept in memory, log to files from the console, record the app type[/+]armanist commentedon Oct 23, 2026
Config values from
.envThe Idiorm fix above has the same cause as three other places: values read from
.envarrive as text, and these places use them without converting them. This ticket fixes them too, each at the point of use.MailerTrait::send()mailer.mail_trapas is, soMAIL_TRAP=falsefrom.envarrives as the text"false"and still traps every mailFILTER_VALIDATE_BOOLEAN, likeapp.debug,resource_cacheandauth.two_faSessionAuthAdapterremember_lifetimetoCookie::set(..., int $time)in astrict_typesfile, soREMEMBER_LIFETIMEset in.envthrows aTypeErroron a "remember me" login(int)RedisAdapter,MemcachedAdapter,RedisRateLimitAdapterportto the extension as is;Memcached::addServer()takes anint, andREDIS_PORTfrom.envis text(int)Values written as booleans and integers in the config files behave as before.
Tests
"false"formailer.mail_trapsends through the transport; the text"true"saves to the trap.remember_lifetimeas the text"2592000"sets the cookie.[quantum-php#593] Read config values from .env as the types their use…
[#593] Record the app type on the app context
[#593] Choose the console or web error output by the app type
[#593] Log to the configured adapter from the console in debug mode
[#593] Keep only the last messages in the debug-mode logger
[#593] Cut the mailer transport log to its last lines before each send
[#593] Add is_web_app() and is_console_app(), and keep no query log o…
[#593] Describe the debug buffer limits, console logging and app type…