33 public const CAUGHT_BY_HANDLER =
'mwe_handler';
35 public const CAUGHT_BY_ENTRYPOINT =
'entrypoint';
37 public const CAUGHT_BY_OTHER =
'other';
80 set_exception_handler(
'MWExceptionHandler::handleUncaughtException' );
84 set_error_handler(
'MWExceptionHandler::handleError' );
89 self::$reservedMemory = str_repeat(
' ', 16384 );
90 register_shutdown_function(
'MWExceptionHandler::handleFatalError' );
97 protected static function report( Throwable $e ) {
108 }
catch ( Throwable $e2 ) {
127 $catcher = self::CAUGHT_BY_OTHER
129 $services = MediaWikiServices::getInstance();
130 if ( !$services->isServiceDisabled(
'DBLoadBalancerFactory' ) ) {
135 $services->getDBLoadBalancerFactory()->rollbackMasterChanges( __METHOD__ );
142 self::logException( $e2, $catcher );
146 self::logException( $e, $catcher );
156 self::handleException( $e, self::CAUGHT_BY_HANDLER );
160 register_shutdown_function(
183 public static function handleException( Throwable $e, $catcher = self::CAUGHT_BY_OTHER ) {
184 self::rollbackMasterChangesAndLog( $e, $catcher );
228 case E_COMPILE_WARNING:
229 $prefix =
'PHP Warning: ';
230 $severity = LogLevel::ERROR;
233 $prefix =
'PHP Notice: ';
234 $severity = LogLevel::ERROR;
238 $prefix =
'PHP Notice: ';
239 $severity = LogLevel::WARNING;
243 $prefix =
'PHP Warning: ';
244 $severity = LogLevel::WARNING;
247 $prefix =
'PHP Strict Standards: ';
248 $severity = LogLevel::WARNING;
251 case E_USER_DEPRECATED:
252 $prefix =
'PHP Deprecated: ';
253 $severity = LogLevel::WARNING;
256 $prefix =
'PHP Unknown error: ';
257 $severity = LogLevel::ERROR;
261 $e =
new ErrorException( $prefix . $message, 0, $level,
$file,
$line );
262 self::logError( $e,
'error', $severity, self::CAUGHT_BY_HANDLER );
287 self::$reservedMemory =
null;
289 $lastError = error_get_last();
290 if ( $lastError !==
null ) {
291 $level = $lastError[
'type'];
292 $message = $lastError[
'message'];
293 $file = $lastError[
'file'];
294 $line = $lastError[
'line'];
300 if ( !in_array( $level, self::$fatalErrorTypes ) ) {
306 $url = WebRequest::getGlobalRequestURL();
308 '[{exception_id}] {exception_url} PHP Fatal Error',
310 $line ?
" line $line" :
'',
312 $file ?
" $file" :
'',
315 $msg = implode(
'', $msgParts );
318 if ( preg_match(
"/Class '\w+' not found/", $message ) ) {
323MediaWiki 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.
325Please see <a href=
"https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries">mediawiki.org</a>
for help on installing the required components.
330 $e =
new ErrorException(
"PHP Fatal Error: {$message}", 0, $level,
$file,
$line );
331 $logger = LoggerFactory::getInstance(
'exception' );
332 $logger->error( $msg, [
334 'exception_id' => WebRequest::getRequestId(),
335 'exception_url' => $url,
336 'caught_by' => self::CAUGHT_BY_HANDLER
353 return self::prettyPrintTrace( self::getRedactedTrace( $e ) );
368 foreach ( $trace as $level => $frame ) {
369 if ( isset( $frame[
'file'] ) && isset( $frame[
'line'] ) ) {
370 $text .=
"{$pad}#{$level} {$frame['file']}({$frame['line']}): ";
376 $text .=
"{$pad}#{$level} [internal function]: ";
379 if ( isset( $frame[
'class'] ) && isset( $frame[
'type'] ) && isset( $frame[
'function'] ) ) {
380 $text .= $frame[
'class'] . $frame[
'type'] . $frame[
'function'];
381 } elseif ( isset( $frame[
'function'] ) ) {
382 $text .= $frame[
'function'];
384 $text .=
'NO_FUNCTION_GIVEN';
387 if ( isset( $frame[
'args'] ) ) {
388 $text .=
'(' . implode(
', ', $frame[
'args'] ) .
")\n";
395 $text .=
"{$pad}#{$level} {main}";
412 return static::redactTrace( $e->getTrace() );
426 return array_map(
function ( $frame ) {
427 if ( isset( $frame[
'args'] ) ) {
428 $frame[
'args'] = array_map(
function ( $arg ) {
429 return is_object( $arg ) ? get_class( $arg ) : gettype( $arg );
463 $id = WebRequest::getRequestId();
464 $type = get_class( $e );
465 $file = $e->getFile();
466 $line = $e->getLine();
467 $message = $e->getMessage();
468 $url = self::getURL() ?:
'[no req]';
471 $message =
"A database query error has occurred. Did you forget to run"
472 .
" your application's database schema updater after upgrading?\n\n"
476 return "[$id] $url $type from line $line of $file: $message";
489 $type = get_class( $e );
490 $file = $e->getFile();
491 $line = $e->getLine();
492 $message = $e->getMessage();
494 return "[{exception_id}] {exception_url} $type from line $line of $file: $message";
502 $reqId = WebRequest::getRequestId();
503 $type = get_class( $e );
504 return '[' . $reqId .
'] '
505 . gmdate(
'Y-m-d H:i:s' ) .
': '
506 .
'Fatal exception of type "' .
$type .
'"';
521 public static function getLogContext( Throwable $e, $catcher = self::CAUGHT_BY_OTHER ) {
524 'exception_id' => WebRequest::getRequestId(),
525 'exception_url' => self::getURL() ?:
'[no req]',
526 'caught_by' => $catcher
544 $catcher = self::CAUGHT_BY_OTHER
549 'id' => WebRequest::getRequestId(),
550 'type' => get_class( $e ),
551 'file' => $e->getFile(),
552 'line' => $e->getLine(),
553 'message' => $e->getMessage(),
554 'code' => $e->getCode(),
555 'url' => self::getURL() ?:
null,
556 'caught_by' => $catcher
559 if ( $e instanceof ErrorException &&
560 ( error_reporting() & $e->getSeverity() ) === 0
563 $data[
'suppressed'] =
true;
567 $data[
'backtrace'] = self::getRedactedTrace( $e );
570 $previous = $e->getPrevious();
571 if ( $previous !==
null ) {
572 $data[
'previous'] = self::getStructuredExceptionData( $previous, $catcher );
636 $catcher = self::CAUGHT_BY_OTHER
638 return FormatJson::encode(
639 self::getStructuredExceptionData( $e, $catcher ),
658 $catcher = self::CAUGHT_BY_OTHER,
661 if ( !( $e instanceof
MWException ) || $e->isLoggable() ) {
662 $logger = LoggerFactory::getInstance(
'exception' );
663 $context = self::getLogContext( $e, $catcher );
665 $context[
'extraData'] = $extraData;
668 self::getLogNormalMessage( $e ),
672 $json = self::jsonSerializeException( $e,
false, FormatJson::ALL_OK, $catcher );
673 if ( $json !==
false ) {
674 $logger = LoggerFactory::getInstance(
'exception-json' );
675 $logger->error( $json, [
'private' =>
true ] );
678 Hooks::runner()->onLogException( $e,
false );
699 $suppressed = ( error_reporting() & $e->getSeverity() ) === 0;
700 if ( !$suppressed ) {
701 $logger = LoggerFactory::getInstance( $channel );
704 self::getLogNormalMessage( $e ),
705 self::getLogContext( $e, $catcher )
710 $json = self::jsonSerializeException( $e,
false, FormatJson::ALL_OK, $catcher );
711 if ( $json !==
false ) {
712 $logger = LoggerFactory::getInstance(
"{$channel}-json" );
719 $unfilteredLevel = $suppressed ? LogLevel::DEBUG : $level;
720 $logger->log( $unfilteredLevel, $json, [
'private' =>
true ] );
723 Hooks::runner()->onLogException( $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 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 getStructuredExceptionData(Throwable $e, $catcher=self::CAUGHT_BY_OTHER)
Get a structured representation of a Throwable.
static rollbackMasterChangesAndLog(Throwable $e, $catcher=self::CAUGHT_BY_OTHER)
Roll back any open database transactions and log the stack trace of the throwable.
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 $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 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 installHandler()
Install handlers with PHP.
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)
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.