Currently, any critical exceptions thrown from any DeferrableUpdate are silently ignored by DeferredUpdates::runUpdate.
Expected:
Exceptions thrown by a DeferrableUpdate cause a code 500 response (if still possible), and should go to the error log per default. They should also cause unit tests to fail.
Observed:
Critical exceptions (such as RuntimeExceptions or LogicExceptions) thrown by a DeferrableUpdate are silently ignored. They are only visible in the debug log if the "exception" channel is explcitly enabled.
Analysis:
- DeferredUpdates::runUpdate calls MWExceptionHandler::rollbackMasterChangesAndLog
- MWExceptionHandler::rollbackMasterChangesAndLog calls MWExceptionHandler::logException, which calls error() on a Logger returned by LoggerFactory::getInstance( 'exception' ).
- The logger used by MWExceptionHandler is per default a LegacyLogger. LegacyLogger::error forwards to LegacyLogger::log, which, per default, ends up calling MWDebug::debugMsg, but not LegacyLogger::emit(), because shouldEmit() returns false.
- shouldEmit() returns false unless $wgDebugLogGroups[] contains 'exception', which is this LegacyLogger's default channel as per the call to LoggerFactory::getInstance().
Proposal:
- LegacyLogger::log should default to the wfErrorLog channel if $level is >= LogLevel::ERROR. In addition, MWDebug::warning should be called instead of MWDebug::debugMsg if $level is >= LogLevel::WARNING.
- It's unclear if level LogLevel::WARNING should be considered a "developer warning" which just causes tests to fail, or a "production warning" that also goes into error logs. In any case, unit tests should fail when warning are triggered (unless the warnign was specifically suppressed).
- Alternatively, if the behavior of LegacyLogger should not be changed, DeferredUpdates::runUpdate should be changed to call wfLogWarning() for all exceptions that are not ErrorPageErrors (and maybe even for them).