90 set_exception_handler(
'MWExceptionHandler::handleUncaughtException' );
97 set_error_handler(
'MWExceptionHandler::handleError' );
104 self::$reservedMemory = str_repeat(
' ', 16384 );
105 register_shutdown_function(
'MWExceptionHandler::handleFatalError' );
123 }
catch ( Exception $e2 ) {
140 $services = MediaWikiServices::getInstance();
141 if ( !$services->isServiceDisabled(
'DBLoadBalancerFactory' ) ) {
146 $services->getDBLoadBalancerFactory()->rollbackMasterChanges( __METHOD__ );
171 register_shutdown_function(
221 if ( in_array( $level, self::$fatalErrorTypes ) ) {
243 case E_COMPILE_WARNING:
244 $levelName =
'Warning';
245 $severity = LogLevel::ERROR;
248 $levelName =
'Notice';
249 $severity = LogLevel::ERROR;
253 $levelName =
'Notice';
254 $severity = LogLevel::WARNING;
258 $levelName =
'Warning';
259 $severity = LogLevel::WARNING;
262 $levelName =
'Strict Standards';
263 $severity = LogLevel::WARNING;
266 case E_USER_DEPRECATED:
267 $levelName =
'Deprecated';
268 $severity = LogLevel::WARNING;
271 $levelName =
'Unknown error';
272 $severity = LogLevel::ERROR;
276 $e =
new ErrorException(
"PHP $levelName: $message", 0, $level,
$file,
$line );
307 $level =
null, $message =
null,
$file =
null,
$line =
null,
312 self::$reservedMemory =
null;
314 if ( $level ===
null ) {
316 if ( static::$handledFatalCallback ) {
322 $lastError = error_get_last();
323 if ( $lastError !==
null ) {
324 $level = $lastError[
'type'];
325 $message = $lastError[
'message'];
326 $file = $lastError[
'file'];
327 $line = $lastError[
'line'];
334 if ( !in_array( $level, self::$fatalErrorTypes ) ) {
342 '[{exception_id}] {exception_url} PHP Fatal Error',
344 $line ?
" line $line" :
'',
346 $file ?
" $file" :
'',
349 $msg = implode(
'', $msgParts );
354 if ( preg_match(
"/Class (undefined: \w+|'\w+' not found)/", $message ) ) {
359 MediaWiki or an installed extension requires
this class but it is not embedded directly in
MediaWiki's git repository and must be installed separately by the end user.
361 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.
372 $trace = $trace ?: debug_backtrace();
373 $logger = LoggerFactory::getInstance(
'fatal' );
374 $logger->error( $msg, [
375 'fatal_exception' => [
376 'class' => ErrorException::class,
377 'message' =>
"PHP Fatal Error: {$message}",
381 'trace' => self::prettyPrintTrace( self::redactTrace( $trace ) ),
384 'exception_url' => $url,
385 'caught_by' => self::CAUGHT_BY_HANDLER
390 static::$handledFatalCallback =
true;
420 foreach ( $trace as $level => $frame ) {
421 if ( isset( $frame[
'file'] ) && isset( $frame[
'line'] ) ) {
422 $text .=
"{$pad}#{$level} {$frame['file']}({$frame['line']}): ";
428 $text .=
"{$pad}#{$level} [internal function]: ";
431 if ( isset( $frame[
'class'] ) && isset( $frame[
'type'] ) && isset( $frame[
'function'] ) ) {
432 $text .= $frame[
'class'] . $frame[
'type'] . $frame[
'function'];
433 } elseif ( isset( $frame[
'function'] ) ) {
434 $text .= $frame[
'function'];
436 $text .=
'NO_FUNCTION_GIVEN';
439 if ( isset( $frame[
'args'] ) ) {
440 $text .=
'(' . implode(
', ', $frame[
'args'] ) .
")\n";
447 $text .=
"{$pad}#{$level} {main}";
464 return static::redactTrace( $e->getTrace() );
478 return array_map(
function ( $frame ) {
479 if ( isset( $frame[
'args'] ) ) {
480 $frame[
'args'] = array_map(
function ( $arg ) {
481 return is_object( $arg ) ? get_class( $arg ) : gettype( $arg );
512 $type = get_class( $e );
513 $file = $e->getFile();
514 $line = $e->getLine();
515 $message = $e->getMessage();
518 return "[$id] $url $type from line $line of $file: $message";
531 $type = get_class( $e );
532 $file = $e->getFile();
533 $line = $e->getLine();
534 $message = $e->getMessage();
536 return "[{exception_id}] {exception_url} $type from line $line of $file: $message";
545 $type = get_class( $e );
546 return '[' . $reqId .
'] '
547 . gmdate(
'Y-m-d H:i:s' ) .
': '
548 .
'Fatal exception of type "' .
$type .
'"';
562 public static function getLogContext( $e, $catcher = self::CAUGHT_BY_OTHER ) {
567 'caught_by' => $catcher
588 'type' => get_class( $e ),
589 'file' => $e->getFile(),
590 'line' => $e->getLine(),
591 'message' => $e->getMessage(),
592 'code' => $e->getCode(),
594 'caught_by' => $catcher
597 if ( $e instanceof ErrorException &&
598 ( error_reporting() & $e->getSeverity() ) === 0
601 $data[
'suppressed'] =
true;
608 $previous = $e->getPrevious();
609 if ( $previous !==
null ) {
671 $e, $pretty =
false, $escaping = 0, $catcher = self::CAUGHT_BY_OTHER
674 self::getStructuredExceptionData( $e, $catcher ),
691 public static function logException( $e, $catcher = self::CAUGHT_BY_OTHER, $extraData = [] ) {
692 if ( !( $e instanceof
MWException ) || $e->isLoggable() ) {
693 $logger = LoggerFactory::getInstance(
'exception' );
699 self::getLogNormalMessage( $e ),
704 if ( $json !==
false ) {
705 $logger = LoggerFactory::getInstance(
'exception-json' );
706 $logger->error( $json, [
'private' =>
true ] );
722 ErrorException $e, $channel, $level = LogLevel::ERROR
728 $suppressed = ( error_reporting() & $e->getSeverity() ) === 0;
729 if ( !$suppressed ) {
730 $logger = LoggerFactory::getInstance( $channel );
733 self::getLogNormalMessage( $e ),
734 self::getLogContext( $e, $catcher )
740 if ( $json !==
false ) {
741 $logger = LoggerFactory::getInstance(
"{$channel}-json" );
742 $logger->log( $level, $json, [
'private' =>
true ] );
745 Hooks::run(
'LogException', [ $e, $suppressed ] );