Add a hook for reporting exceptions
[lhc/web/wiklou.git] / includes / exception / MWExceptionHandler.php
index 77ab6ad..a58705f 100644 (file)
@@ -150,22 +150,24 @@ class MWExceptionHandler {
         * @since 1.25
         * @param Exception $e
         */
-       public static function handleException( $e ) {
-               global $wgFullyInitialised;
+       public static function handleException( Exception $e ) {
+               try {
+                       // Rollback DBs to avoid transaction notices. This may fail
+                       // to rollback some DB due to connection issues or exceptions.
+                       // However, any sane DB driver will rollback implicitly anyway.
+                       self::rollbackMasterChangesAndLog( $e );
+               } catch ( DBError $e2 ) {
+                       // If the DB is unreacheable, rollback() will throw an error
+                       // and the error report() method might need messages from the DB,
+                       // which would result in an exception loop. PHP may escalate such
+                       // errors to "Exception thrown without a stack frame" fatals, but
+                       // it's better to be explicit here.
+                       self::logException( $e2 );
+               }
 
-               self::rollbackMasterChangesAndLog( $e );
                self::logException( $e );
                self::report( $e );
 
-               // Final cleanup
-               if ( $wgFullyInitialised ) {
-                       try {
-                               // uses $wgRequest, hence the $wgFullyInitialised condition
-                               wfLogProfilingData();
-                       } catch ( Exception $e ) {
-                       }
-               }
-
                // Exit value should be nonzero for the benefit of shell jobs
                exit( 1 );
        }
@@ -179,6 +181,7 @@ class MWExceptionHandler {
         */
        public static function handleError( $level, $message, $file = null, $line = null ) {
                // Map error constant to error name (reverse-engineer PHP error reporting)
+               $channel = 'error';
                switch ( $level ) {
                        case E_ERROR:
                        case E_CORE_ERROR:
@@ -187,6 +190,7 @@ class MWExceptionHandler {
                        case E_RECOVERABLE_ERROR:
                        case E_PARSE:
                                $levelName = 'Error';
+                               $channel = 'fatal';
                                break;
                        case E_WARNING:
                        case E_CORE_WARNING:
@@ -207,6 +211,7 @@ class MWExceptionHandler {
                                break;
                        case /* HHVM's FATAL_ERROR */ 16777217:
                                $levelName = 'Fatal';
+                               $channel = 'fatal';
                                break;
                        default:
                                $levelName = 'Unknown error';
@@ -214,7 +219,7 @@ class MWExceptionHandler {
                }
 
                $e = new ErrorException( "PHP $levelName: $message", 0, $level, $file, $line );
-               self::logError( $e );
+               self::logError( $e, $channel );
 
                // This handler is for logging only. Return false will instruct PHP
                // to continue regular handling.
@@ -246,6 +251,7 @@ class MWExceptionHandler {
                        if ( preg_match( "/Class (undefined: \w+|'\w+' not found)/",
                                $lastError['message']
                        ) ) {
+                               // @codingStandardsIgnoreStart Generic.Files.LineLength.TooLong
                                $msg = <<<TXT
 {$msg}
 
@@ -253,9 +259,10 @@ MediaWiki or an installed extension requires this class but it is not embedded d
 
 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
                        }
                        $e = new ErrorException( $msg, 0, $lastError['type'] );
-                       self::logError( $e );
+                       self::logError( $e, 'fatal' );
                }
        }
 
@@ -344,7 +351,7 @@ TXT;
         * returns the requested URL. Otherwise, returns false.
         *
         * @since 1.23
-        * @return string|bool
+        * @return string|false
         */
        public static function getURL() {
                global $wgRequest;
@@ -423,7 +430,7 @@ TXT;
         * @param Exception $e
         * @param bool $pretty Add non-significant whitespace to improve readability (default: false).
         * @param int $escaping Bitfield consisting of FormatJson::.*_OK class constants.
-        * @return string|bool JSON string if successful; false upon failure
+        * @return string|false JSON string if successful; false upon failure
         */
        public static function jsonSerializeException( Exception $e, $pretty = false, $escaping = 0 ) {
                global $wgLogExceptionBacktrace;
@@ -479,6 +486,8 @@ TXT;
                        if ( $json !== false ) {
                                wfDebugLog( 'exception-json', $json, 'private' );
                        }
+
+                       Hooks::run( 'LogException', array( $e, false ) );
                }
        }
 
@@ -487,25 +496,29 @@ TXT;
         *
         * @since 1.25
         * @param ErrorException $e
+        * @param string $channel
        */
-       protected static function logError( ErrorException $e ) {
+       protected static function logError( ErrorException $e, $channel ) {
                global $wgLogExceptionBacktrace;
 
                // The set_error_handler callback is independent from error_reporting.
                // Filter out unwanted errors manually (e.g. when wfSuppressWarnings is active).
-               if ( ( error_reporting() & $e->getSeverity() ) !== 0 ) {
+               $suppressed = ( error_reporting() & $e->getSeverity() ) === 0;
+               if ( !$suppressed ) {
                        $log = self::getLogMessage( $e );
                        if ( $wgLogExceptionBacktrace ) {
-                               wfDebugLog( 'error', $log . "\n" . $e->getTraceAsString() );
+                               wfDebugLog( $channel, $log . "\n" . $e->getTraceAsString() );
                        } else {
-                               wfDebugLog( 'error', $log );
+                               wfDebugLog( $channel, $log );
                        }
                }
 
                // Include all errors in the json log (surpressed errors will be flagged)
                $json = self::jsonSerializeException( $e, false, FormatJson::ALL_OK );
                if ( $json !== false ) {
-                       wfDebugLog( 'error-json', $json, 'private' );
+                       wfDebugLog( "$channel-json", $json, 'private' );
                }
+
+               Hooks::run( 'LogException', array( $e, $suppressed ) );
        }
 }