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__ );
153 self::logException( $e2, self::CAUGHT_BY_HANDLER );
157 self::logException( $e, self::CAUGHT_BY_HANDLER );
167 self::handleException( $e );
171 register_shutdown_function(
194 self::rollbackMasterChangesAndLog( $e );
221 if ( in_array( $level, self::$fatalErrorTypes ) ) {
222 return self::handleFatalError( ...func_get_args() );
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 );
277 self::logError( $e,
'error', $severity );
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 ) ) {
340 $url = WebRequest::getGlobalRequestURL();
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 ) ) {
359MediaWiki 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.
361Please 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 ) ),
383 'exception_id' => WebRequest::getRequestId(),
384 'exception_url' => $url,
385 'caught_by' => self::CAUGHT_BY_HANDLER
390 static::$handledFatalCallback =
true;
405 return self::prettyPrintTrace( self::getRedactedTrace( $e ) );
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 );
511 $id = WebRequest::getRequestId();
512 $type = get_class( $e );
513 $file = $e->getFile();
514 $line = $e->getLine();
515 $message = $e->getMessage();
516 $url = self::getURL() ?:
'[no req]';
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";
544 $reqId = WebRequest::getRequestId();
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 ) {
565 'exception_id' => WebRequest::getRequestId(),
566 'exception_url' => self::getURL() ?:
'[no req]',
567 'caught_by' => $catcher
587 'id' => WebRequest::getRequestId(),
588 'type' => get_class( $e ),
589 'file' => $e->getFile(),
590 'line' => $e->getLine(),
591 'message' => $e->getMessage(),
592 'code' => $e->getCode(),
593 'url' => self::getURL() ?:
null,
594 'caught_by' => $catcher
597 if ( $e instanceof ErrorException &&
598 ( error_reporting() & $e->getSeverity() ) === 0
601 $data[
'suppressed'] =
true;
605 $data[
'backtrace'] = self::getRedactedTrace( $e );
608 $previous = $e->getPrevious();
609 if ( $previous !==
null ) {
610 $data[
'previous'] = self::getStructuredExceptionData( $previous, $catcher );
671 $e, $pretty =
false, $escaping = 0, $catcher = self::CAUGHT_BY_OTHER
673 return FormatJson::encode(
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' );
694 $context = self::getLogContext( $e, $catcher );
699 self::getLogNormalMessage( $e ),
703 $json = self::jsonSerializeException( $e,
false, FormatJson::ALL_OK, $catcher );
704 if ( $json !==
false ) {
705 $logger = LoggerFactory::getInstance(
'exception-json' );
706 $logger->error( $json, [
'private' =>
true ] );
709 Hooks::run(
'LogException', [ $e,
false ] );
722 ErrorException $e, $channel, $level = LogLevel::ERROR
724 $catcher = self::CAUGHT_BY_HANDLER;
728 $suppressed = ( error_reporting() & $e->getSeverity() ) === 0;
729 if ( !$suppressed ) {
730 $logger = LoggerFactory::getInstance( $channel );
733 self::getLogNormalMessage( $e ),
734 self::getLogContext( $e, $catcher )
739 $json = self::jsonSerializeException( $e,
false, FormatJson::ALL_OK, $catcher );
740 if ( $json !==
false ) {
741 $logger = LoggerFactory::getInstance(
"{$channel}-json" );
742 $logger->log( $level, $json, [
'private' =>
true ] );
745 Hooks::run(
'LogException', [ $e, $suppressed ] );
$wgLogExceptionBacktrace
If true, send the exception backtrace to the error log.
$wgPropagateErrors
If true, the MediaWiki error handler passes errors/warnings to the default error handler after loggin...
wfIsCLI()
Check if we are running from the commandline.
if(! $wgDBerrorLogTZ) $wgRequest
WebRequest clone which takes values from a provided array.
Handler class for MWExceptions.
static handleError( $level, $message, $file=null, $line=null)
Handler for set_error_handler() callback notifications.
static getRedactedTraceAsString( $e)
Generate a string representation of an exception's stack trace.
static handleFatalError( $level=null, $message=null, $file=null, $line=null, $context=null, $trace=null)
Dual purpose callback used as both a set_error_handler() callback and a registered shutdown function.
static logException( $e, $catcher=self::CAUGHT_BY_OTHER, $extraData=[])
Log an exception to the exception log (if enabled).
static jsonSerializeException( $e, $pretty=false, $escaping=0, $catcher=self::CAUGHT_BY_OTHER)
Serialize an Exception object to JSON.
static getLogMessage( $e)
Get a message formatting the exception message and its origin.
static getPublicLogMessage( $e)
static $fatalErrorTypes
Error types that, if unhandled, are fatal to the request.
static prettyPrintTrace(array $trace, $pad='')
Generate a string representation of a stacktrace.
static getLogNormalMessage( $e)
Get a normalised message for formatting with PSR-3 log event context.
static logError(ErrorException $e, $channel, $level=LogLevel::ERROR)
Log an exception that wasn't thrown but made to wrap an error.
static rollbackMasterChangesAndLog( $e)
Roll back any open database transactions and log the stack trace of the exception.
static handleUncaughtException( $e)
Callback to use with PHP's set_exception_handler.
static report( $e)
Report an exception to the user.
static getStructuredExceptionData( $e, $catcher=self::CAUGHT_BY_OTHER)
Get a structured representation of an Exception.
static redactTrace(array $trace)
Redact a stacktrace generated by Exception::getTrace(), debug_backtrace() or similar means.
static installHandler()
Install handlers with PHP.
static $handledFatalCallback
static getRedactedTrace( $e)
Return a copy of an exception's backtrace as an array.
static getURL()
If the exception occurred in the course of responding to a request, returns the requested URL.
static getLogContext( $e, $catcher=self::CAUGHT_BY_OTHER)
Get a PSR-3 log event context from an Exception.
static handleException( $e)
Exception handler which simulates the appropriate catch() handling:
static output( $e, $mode, $eNew=null)
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.