* Install handlers with PHP.
*/
public static function installHandler() {
- set_exception_handler( 'MWExceptionHandler::handleException' );
+ set_exception_handler( 'MWExceptionHandler::handleUncaughtException' );
set_error_handler( 'MWExceptionHandler::handleError' );
// Reserve 16k of memory so we can report OOM fatals
self::logException( $e, self::CAUGHT_BY_HANDLER );
}
+ /**
+ * Callback to use with PHP's set_exception_handler.
+ *
+ * @since 1.31
+ * @param Exception|Throwable $e
+ */
+ public static function handleUncaughtException( $e ) {
+ self::handleException( $e );
+
+ // Make sure we don't claim success on exit for CLI scripts (T177414)
+ if ( PHP_SAPI === 'cli' ) {
+ register_shutdown_function(
+ function () {
+ exit( 255 );
+ }
+ );
+ }
+ }
+
/**
* Exception handler which simulates the appropriate catch() handling:
*
// HHVM: Class undefined: foo
// PHP5: Class 'foo' not found
if ( preg_match( "/Class (undefined: \w+|'\w+' not found)/", $msg ) ) {
- // @codingStandardsIgnoreStart Generic.Files.LineLength.TooLong
+ // phpcs:disable Generic.Files.LineLength
$msg = <<<TXT
{$msg}
Please see <a href="https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries">mediawiki.org</a> for help on installing the required components.
TXT;
- // @codingStandardsIgnoreEnd
+ // phpcs:enable
}
// We can't just create an exception and log it as it is likely that
return "[$id] $url $type from line $line of $file: $message";
}
+ /**
+ * Get a normalised message for formatting with PSR-3 log event context.
+ *
+ * Must be used together with `getLogContext()` to be useful.
+ *
+ * @since 1.30
+ * @param Exception|Throwable $e
+ * @return string
+ */
+ public static function getLogNormalMessage( $e ) {
+ $type = get_class( $e );
+ $file = $e->getFile();
+ $line = $e->getLine();
+ $message = $e->getMessage();
+
+ return "[{exception_id}] {exception_url} $type from line $line of $file: $message";
+ }
+
/**
* @param Exception|Throwable $e
* @return string
return [
'exception' => $e,
'exception_id' => WebRequest::getRequestId(),
+ 'exception_url' => self::getURL() ?: '[no req]',
'caught_by' => $catcher
];
}
if ( !( $e instanceof MWException ) || $e->isLoggable() ) {
$logger = LoggerFactory::getInstance( 'exception' );
$logger->error(
- self::getLogMessage( $e ),
+ self::getLogNormalMessage( $e ),
self::getLogContext( $e, $catcher )
);
* @param ErrorException $e
* @param string $channel
* @param string $level
- */
+ */
protected static function logError(
ErrorException $e, $channel, $level = LogLevel::ERROR
) {
$logger = LoggerFactory::getInstance( $channel );
$logger->log(
$level,
- self::getLogMessage( $e ),
+ self::getLogNormalMessage( $e ),
self::getLogContext( $e, $catcher )
);
}