Log stack traces and report exceptions as PSR-3 context (#41068, #41069) - #41070
Log stack traces and report exceptions as PSR-3 context (#41068, #41069)#41070lbajsarowicz wants to merge 2 commits into
Conversation
Logger\Handler\Base constructed LineFormatter without includeStacktraces, so a Throwable passed in the PSR-3 reserved context['exception'] key was rendered as its throw site alone. The correct PSR-3 call therefore produced less information than stringifying the exception into the message, which is why 268 call sites do the latter. Enable stack traces on the default formatter and accept an injected FormatterInterface, so JsonFormatter can be wired through di.xml instead of patching the framework. Convert the exception logging in Magento\Framework to a constant message template plus structured context, carrying the identifiers already in scope in the catch block. Stringified exceptions produce a message that is unique per occurrence and cannot be aggregated, and message-only calls drop the class, code, file, line and trace entirely, as well as the routing to exception.log that Handler\System performs on context['exception']. Webapi\ErrorProcessor no longer wraps the exception to build the log message, which made the wrapper the top frame of the logged trace; the original exception is logged instead and the report id moves to the context. DB\Adapter\Pdo\Mysql is left as is: its logger is Magento\Framework\DB\LoggerInterface, whose critical(\Exception $e) contract is not PSR-3. context['exception'] holds the Throwable rather than getTrace(), whose frames carry call arguments that may contain personal data or credentials. Fixes magento#41068 Fixes magento#41069
|
Hi @lbajsarowicz. Thank you for your contribution!
Allowed build names are:
You can find more information about the builds here For more details, review the Code Contributions documentation. |
|
Local verification, for transparency about what I ran and what I could not: Green
Pre-existing failures in my environment, unrelated to this diff — not fixed here
Happy to adjust the formatter default if maintainers would rather have |
|
@magento run all tests |
Static Tests counts PHPCS warnings as violations, so pre-existing warnings in
the files this branch touches now fail the build. Declare constant visibility,
replace {@inheritdoc} with @inheritdoc plus the parameter annotations the sniff
expects, drop property descriptions that only repeat the property name, and give
FeedFactory a class description that says what it does.
Layout::setBlock() casts the block name instead of coalescing it, which keeps
the null to empty string behaviour while satisfying the PHPStan rule that the
variable is never null at that point.
|
Pushed
Verified locally with the CI ruleset ( The docblock and constant cleanup is unrelated to the logging change — happy to split it into a separate PR if you'd rather keep this diff narrow. |
|
@magento run all tests |
Description
Two defects that together make Magento's own logs unusable for diagnosing failures. They have to be fixed in this order, which is why they are in one PR: converting call sites first would lose stack traces under the current formatter.
1. The log formatter discards stack traces (#41068)
Logger\Handler\Baseis the only place inapp/orlib/that constructs a Monolog formatter, and it leftincludeStacktracesat its default:Consequence: a
Throwablepassed in the PSR-3 reservedcontext['exception']key rendered as its throw site and nothing else —so the correct PSR-3 call produced less information than stringifying the exception into the message. Reported as #13128 back in 2018; merchants have been carrying
composer-patchesagainst this framework file ever since.This PR enables stack traces on the default formatter and makes the formatter injectable, so a merchant can swap in
JsonFormatter(one valid JSON document per record, structured trace, no frame arguments) throughdi.xmlinstead of patching core. Documented inlib/internal/Magento/Framework/Logger/README.md.2. Exception logging in
Magento\Frameworkis not aggregatable (#41069)26 call sites either stringified the
Throwableas the log message ($this->logger->critical($e)—Throwable extends Stringablesince PHP 8.0, so this silently casts) or logged$e->getMessage()with no context at all.Both are hostile to observability:
$e->getMessage()routinely embeds an entity id (No such entity with customerId = 4711), so 10,000 occurrences of one bug become 10,000 distinct groups in any log pipeline. Nothing crosses an alert threshold.messageas free text,context— already parsed, already indexed — is empty. Extracting the trace needs a per-version regex maintained by the merchant.getMessage()-only is silent data loss. No class, no code, no file, no line, no trace. And becauseLogger\Handler\Systemroutes oncontext['exception'], those records never reachexception.logeither.Every converted site now uses a constant message template plus structured context, preferring identifiers already in scope in the
catchblock:Notes on specific conversions:
Webapi\ErrorProcessor::_critical()wrapped the exception in a new\Exceptionpurely to build the log message, which made the wrapper's own location the top trace frame. The original exception is now logged directly, with the report ID in context — the report ID is still returned unchanged.MessageQueue\Consumergained a$topicName = nullinitialiser so the topic name is available to thecatchblock.MessageQueue/Amqp/Stomptopology installers no longer interpolategetTraceAsString()into the message.$e->getTrace()is deliberately not used as a context value anywhere: trace frames carry call arguments, which may contain personal data or credentials. Monolog's normalizer renderscontext['exception']as"file:line"frames with no arguments.DB\Adapter\Pdo\Mysqlwas left alone on purpose — its$loggerisMagento\Framework\DB\LoggerInterface, whosecritical(\Exception $e)contract is not PSR-3.Scope is limited to
lib/internal/Magento/Frameworkto keep the diff reviewable. The remaining ~330app/codesites are listed in #41069 and are proposed as follow-ups grouped by module.Related Pull Requests
None.
Fixed Issues
Manual testing scenarios
Stack traces are present (#41068)
chmod 000 var/view_preprocessedand open a storefront page, or trigger any handled failure from a converted call site.tail var/log/exception.log.{"exception":"[object] (FileSystemException(code: 0): ... at /app/lib/.../Write.php:154)"}.[stacktrace]block with the full call chain, and the message is a stable template with the identifiers incontext.Formatter is replaceable (#41068)
di.xml:bin/magento setup:di:compile && bin/magento cache:flush, trigger an exception.var/log/exception.logis one line of valid JSON;context.exception.traceis an array offile:lineframes. Confirm withtail -1 var/log/exception.log | php -r 'var_dump(json_decode(fgets(STDIN)) !== null);'.Aggregatable messages (#41069)
getViewFileUrl()for two different missing files.Unable to resolve the URL of the {fileId} view file, and differ only incontext.fileId— one group, two samples.Questions or comments
The default stays
LineFormatterso the human-readable log shape is preserved; enablingincludeStacktracesdoes make entries multi-line, which is the trade-off #13128 asked for. Merchants who parse logs should use theJsonFormatterwiring above — happy to promote that to a shipped virtual type or adeployment_configswitch if maintainers prefer that over documentation only.Contribution checklist (*)