24 use Wikimedia\NormalizedException\INormalizedException;
34 public const CAUGHT_BY_HANDLER =
'mwe_handler';
36 public const CAUGHT_BY_ENTRYPOINT =
'entrypoint';
38 public const CAUGHT_BY_OTHER =
'other';
105 set_exception_handler(
'MWExceptionHandler::handleUncaughtException' );
109 set_error_handler(
'MWExceptionHandler::handleError' );
114 self::$reservedMemory = str_repeat(
' ', 16384 );
115 register_shutdown_function(
'MWExceptionHandler::handleFatalError' );
122 protected static function report( Throwable $e ) {
133 }
catch ( Throwable $e2 ) {
152 $catcher = self::CAUGHT_BY_OTHER
154 $services = MediaWikiServices::getInstance();
155 if ( !$services->isServiceDisabled(
'DBLoadBalancerFactory' ) ) {
160 $lbFactory = $services->getDBLoadBalancerFactory();
161 $lbFactory->rollbackPrimaryChanges( __METHOD__ );
162 $lbFactory->flushPrimarySessions( __METHOD__ );
183 $catcher = self::CAUGHT_BY_OTHER
200 register_shutdown_function(
226 public static function handleException( Throwable $e, $catcher = self::CAUGHT_BY_OTHER ) {
269 case E_COMPILE_WARNING:
270 $prefix =
'PHP Warning: ';
271 $severity = LogLevel::ERROR;
274 $prefix =
'PHP Notice: ';
275 $severity = LogLevel::ERROR;
279 $prefix =
'PHP Notice: ';
280 $severity = LogLevel::WARNING;
284 $prefix =
'PHP Warning: ';
285 $severity = LogLevel::WARNING;
288 $prefix =
'PHP Strict Standards: ';
289 $severity = LogLevel::WARNING;
292 $prefix =
'PHP Deprecated: ';
293 $severity = LogLevel::WARNING;
295 case E_USER_DEPRECATED:
296 $prefix =
'PHP Deprecated: ';
297 $severity = LogLevel::WARNING;
303 $file = $real[
'file'];
304 $line = $real[
'line'];
305 $message = $real[
'message'];
309 $prefix =
'PHP Unknown error: ';
310 $severity = LogLevel::ERROR;
315 $e =
new ErrorException( $prefix . $message, 0, $level,
$file,
$line );
316 self::logError( $e,
'error', $severity, self::CAUGHT_BY_HANDLER );
321 return !( self::$propagateErrors || ini_get(
'track_errors' ) );
341 self::$reservedMemory =
null;
343 $lastError = error_get_last();
344 if ( $lastError ===
null ) {
348 $level = $lastError[
'type'];
349 $message = $lastError[
'message'];
350 $file = $lastError[
'file'];
351 $line = $lastError[
'line'];
353 if ( !in_array( $level, self::$fatalErrorTypes ) ) {
360 '[{reqId}] {exception_url} PHP Fatal Error',
362 $line ?
" line $line" :
'',
364 $file ?
" $file" :
'',
367 $msg = implode(
'', $msgParts );
370 if ( preg_match(
"/Class '\w+' not found/", $message ) ) {
375 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.
377 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.
382 $e =
new ErrorException(
"PHP Fatal Error: {$message}", 0, $level,
$file,
$line );
383 $logger = LoggerFactory::getInstance(
'exception' );
384 $logger->error( $msg, self::getLogContext( $e, self::CAUGHT_BY_HANDLER ) );
400 $from =
'from ' . $e->getFile() .
'(' . $e->getLine() .
')' .
"\n";
416 foreach ( $trace as $level => $frame ) {
417 if ( isset( $frame[
'file'] ) && isset( $frame[
'line'] ) ) {
418 $text .=
"{$pad}#{$level} {$frame['file']}({$frame['line']}): ";
424 $text .=
"{$pad}#{$level} [internal function]: ";
427 if ( isset( $frame[
'class'] ) && isset( $frame[
'type'] ) && isset( $frame[
'function'] ) ) {
428 $text .= $frame[
'class'] . $frame[
'type'] . $frame[
'function'];
429 } elseif ( isset( $frame[
'function'] ) ) {
430 $text .= $frame[
'function'];
432 $text .=
'NO_FUNCTION_GIVEN';
435 if ( isset( $frame[
'args'] ) ) {
436 $text .=
'(' . implode(
', ', $frame[
'args'] ) .
")\n";
443 $text .=
"{$pad}#{$level} {main}";
460 return static::redactTrace( $e->getTrace() );
474 return array_map(
static function ( $frame ) {
475 if ( isset( $frame[
'args'] ) ) {
476 $frame[
'args'] = array_map(
static function ( $arg ) {
477 return is_object( $arg ) ? get_class( $arg ) : gettype( $arg );
512 $type = get_class( $e );
513 $message = $e->getMessage();
517 $message =
"A database query error has occurred. Did you forget to run"
518 .
" your application's database schema updater after upgrading"
519 .
" or after adding a new extension?\n\nPlease see"
520 .
" https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Upgrading and"
521 .
" https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:How_to_debug"
522 .
" for more information.\n\n"
526 return "[$id] $url $type: $message";
539 if ( $e instanceof INormalizedException ) {
540 $message = $e->getNormalizedMessage();
542 $message = $e->getMessage();
544 if ( !$e instanceof ErrorException ) {
549 $message = get_class( $e ) .
": $message";
552 return "[{reqId}] {exception_url} $message";
561 $type = get_class( $e );
562 return '[' . $reqId .
'] '
563 . gmdate(
'Y-m-d H:i:s' ) .
': '
564 .
'Fatal exception of type "' .
$type .
'"';
579 public static function getLogContext( Throwable $e, $catcher = self::CAUGHT_BY_OTHER ) {
590 'caught_by' => $catcher
592 if ( $e instanceof INormalizedException ) {
593 $context += $e->getMessageContext();
612 $catcher = self::CAUGHT_BY_OTHER
616 'type' => get_class( $e ),
617 'file' => $e->getFile(),
618 'line' => $e->getLine(),
619 'message' => $e->getMessage(),
620 'code' => $e->getCode(),
622 'caught_by' => $catcher
625 if ( $e instanceof ErrorException &&
626 ( error_reporting() & $e->getSeverity() ) === 0
629 $data[
'suppressed'] =
true;
632 if ( self::$logExceptionBacktrace ) {
636 $previous = $e->getPrevious();
637 if ( $previous !==
null ) {
702 $catcher = self::CAUGHT_BY_OTHER
705 self::getStructuredExceptionData( $e, $catcher ),
724 $catcher = self::CAUGHT_BY_OTHER,
727 if ( !( $e instanceof
MWException ) || $e->isLoggable() ) {
728 $logger = LoggerFactory::getInstance(
'exception' );
731 $context[
'extraData'] = $extraData;
734 self::getLogNormalMessage( $e ),
739 if ( $json !==
false ) {
740 $logger = LoggerFactory::getInstance(
'exception-json' );
741 $logger->error( $json, [
'private' =>
true ] );
765 $suppressed = ( error_reporting() & $e->getSeverity() ) === 0;
766 if ( !$suppressed ) {
767 $logger = LoggerFactory::getInstance( $channel );
770 self::getLogNormalMessage( $e ),
771 self::getLogContext( $e, $catcher )
777 if ( $json !==
false ) {
778 $logger = LoggerFactory::getInstance(
"{$channel}-json" );
785 $unfilteredLevel = $suppressed ? LogLevel::DEBUG : $level;
786 $logger->log( $unfilteredLevel, $json, [
'private' =>
true ] );
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
wfIsCLI()
Check if we are running from the commandline.
WebRequest clone which takes values from a provided array.
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
static parseCallerDescription( $msg)
Append a caller description to an error message.
Handler class for MWExceptions.
static getLogContext(Throwable $e, $catcher=self::CAUGHT_BY_OTHER)
Get a PSR-3 log event context from a Throwable.
static handleError( $level, $message, $file=null, $line=null)
Handler for set_error_handler() callback notifications.
static rollbackPrimaryChangesAndLog(Throwable $e, $catcher=self::CAUGHT_BY_OTHER)
Roll back any open database transactions and log the stack trace of the throwable.
static bool $logExceptionBacktrace
Whether exception data should include a backtrace.
static installHandler(bool $logExceptionBacktrace=true, bool $propagateErrors=true)
Install handlers with PHP.
static getStructuredExceptionData(Throwable $e, $catcher=self::CAUGHT_BY_OTHER)
Get a structured representation of a Throwable.
static rollbackMasterChangesAndLog(Throwable $e, $catcher=self::CAUGHT_BY_OTHER)
static bool $propagateErrors
Whether to propagate errors to PHP's built-in handler.
static getRedactedTraceAsString(Throwable $e)
Generate a string representation of a throwable's stack trace.
static report(Throwable $e)
Report a throwable to the user.
static logException(Throwable $e, $catcher=self::CAUGHT_BY_OTHER, $extraData=[])
Log a throwable to the exception log (if enabled).
static array $fatalErrorTypes
Error types that, if unhandled, are fatal to the request.
static getPublicLogMessage(Throwable $e)
static getRedactedTrace(Throwable $e)
Return a copy of a throwable's backtrace as an array.
static handleUncaughtException(Throwable $e)
Callback to use with PHP's set_exception_handler.
static prettyPrintTrace(array $trace, $pad='')
Generate a string representation of a stacktrace.
static string null $reservedMemory
static jsonSerializeException(Throwable $e, $pretty=false, $escaping=0, $catcher=self::CAUGHT_BY_OTHER)
Serialize a Throwable object to JSON.
static logError(ErrorException $e, $channel, $level, $catcher)
Log an exception that wasn't thrown but made to wrap an error.
static getLogMessage(Throwable $e)
Get a message formatting the throwable message and its origin.
static redactTrace(array $trace)
Redact a stacktrace generated by Throwable::getTrace(), debug_backtrace() or similar means.
static handleFatalError()
Callback used as a registered shutdown function.
static getLogNormalMessage(Throwable $e)
Get a normalised message for formatting with PSR-3 log event context.
static getURL()
If the exception occurred in the course of responding to a request, returns the requested URL.
static handleException(Throwable $e, $catcher=self::CAUGHT_BY_OTHER)
Exception handler which simulates the appropriate catch() handling:
static output(Throwable $e, $mode, Throwable $eNew=null)
static getRequestId()
Get the current request ID.
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.