[!!!][FEATURE] Introduce PSR-3 Logging
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Error / ErrorHandler.php
index 7b70bc8..e82acf5 100644 (file)
@@ -14,7 +14,12 @@ namespace TYPO3\CMS\Core\Error;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerAwareTrait;
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Log\LogLevel;
+use TYPO3\CMS\Core\Log\LogManager;
+use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
@@ -23,14 +28,23 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
  *
  * This file is a backport from TYPO3 Flow
  */
-class ErrorHandler implements ErrorHandlerInterface
+class ErrorHandler implements ErrorHandlerInterface, LoggerAwareInterface
 {
+    use LoggerAwareTrait;
+
     /**
      * Error levels which should result in an exception thrown.
      *
-     * @var array
+     * @var int
+     */
+    protected $exceptionalErrors = 0;
+
+    /**
+     * Error levels which should be handled.
+     *
+     * @var int
      */
-    protected $exceptionalErrors = [];
+    protected $errorHandlerErrors = 0;
 
     /**
      * Whether to write a flash message in case of an error
@@ -48,19 +62,21 @@ class ErrorHandler implements ErrorHandlerInterface
     {
         $excludedErrors = E_COMPILE_WARNING | E_COMPILE_ERROR | E_CORE_WARNING | E_CORE_ERROR | E_PARSE | E_ERROR;
         // reduces error types to those a custom error handler can process
-        $errorHandlerErrors = $errorHandlerErrors & ~$excludedErrors;
-        set_error_handler([$this, 'handleError'], $errorHandlerErrors);
+        $this->errorHandlerErrors = $errorHandlerErrors & ~$excludedErrors;
+        set_error_handler([$this, 'handleError'], $this->errorHandlerErrors);
     }
 
     /**
      * Defines which error levels should result in an exception thrown.
      *
      * @param int $exceptionalErrors The integer representing the E_* error level to handle as exceptions
-     * @return void
      */
     public function setExceptionalErrors($exceptionalErrors)
     {
-        $this->exceptionalErrors = (int)$exceptionalErrors;
+        $exceptionalErrors = (int)$exceptionalErrors;
+        // We always disallow E_USER_DEPRECATED to generate exceptions as this may cause
+        // bad user experience specifically during upgrades.
+        $this->exceptionalErrors = $exceptionalErrors & ~E_USER_DEPRECATED;
     }
 
     /**
@@ -87,91 +103,86 @@ class ErrorHandler implements ErrorHandlerInterface
      */
     public function handleError($errorLevel, $errorMessage, $errorFile, $errorLine)
     {
-        // Don't do anything if error_reporting is disabled by an @ sign
-        if (error_reporting() === 0) {
+        // Don't do anything if error_reporting is disabled by an @ sign or $errorLevel is something we won't handle
+        $shouldHandleErrorLevel = (bool)($this->errorHandlerErrors & $errorLevel);
+        if (error_reporting() === 0 || !$shouldHandleErrorLevel) {
             return true;
         }
         $errorLevels = [
-            E_WARNING => 'Warning',
-            E_NOTICE => 'Notice',
-            E_USER_ERROR => 'User Error',
-            E_USER_WARNING => 'User Warning',
-            E_USER_NOTICE => 'User Notice',
-            E_STRICT => 'Runtime Notice',
-            E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
-            E_DEPRECATED => 'Runtime Deprecation Notice'
+            E_WARNING => 'PHP Warning',
+            E_NOTICE => 'PHP Notice',
+            E_USER_ERROR => 'PHP User Error',
+            E_USER_WARNING => 'PHP User Warning',
+            E_USER_NOTICE => 'PHP User Notice',
+            E_STRICT => 'PHP Runtime Notice',
+            E_RECOVERABLE_ERROR => 'PHP Catchable Fatal Error',
+            E_USER_DEPRECATED => 'TYPO3 Deprecation Notice',
+            E_DEPRECATED => 'PHP Runtime Deprecation Notice'
         ];
-        $message = 'PHP ' . $errorLevels[$errorLevel] . ': ' . $errorMessage . ' in ' . $errorFile . ' line ' . $errorLine;
+        $message = $errorLevels[$errorLevel] . ': ' . $errorMessage . ' in ' . $errorFile . ' line ' . $errorLine;
         if ($errorLevel & $this->exceptionalErrors) {
-            // handle error raised at early parse time
-            // autoloader not available & built-in classes not resolvable
-            if (!class_exists('stdClass', false)) {
-                $message = 'PHP ' . $errorLevels[$errorLevel] . ': ' . $errorMessage . ' in ' . basename($errorFile) .
-                    'line ' . $errorLine;
-                die($message);
-            }
             throw new Exception($message, 1476107295);
-        } else {
-            switch ($errorLevel) {
-                case E_USER_ERROR:
-                case E_RECOVERABLE_ERROR:
-                    $severity = 2;
-                    break;
-                case E_USER_WARNING:
-                case E_WARNING:
-                    $severity = 1;
-                    break;
-                default:
-                    $severity = 0;
-            }
-            $logTitle = 'Core: Error handler (' . TYPO3_MODE . ')';
-            $message = $logTitle . ': ' . $message;
-            // Write error message to the configured syslogs,
-            // see: $TYPO3_CONF_VARS['SYS']['systemLog']
-            if ($errorLevel & $GLOBALS['TYPO3_CONF_VARS']['SYS']['syslogErrorReporting']) {
-                GeneralUtility::sysLog($message, 'core', $severity + 1);
-            }
-            // Write error message to devlog extension(s),
-            // see: $TYPO3_CONF_VARS['SYS']['enable_errorDLOG']
-            if (TYPO3_ERROR_DLOG) {
-                GeneralUtility::devLog($message, 'core', $severity + 1);
-            }
-            // Write error message to TSlog (admin panel)
-            $timeTracker = $this->getTimeTracker();
-            if (is_object($timeTracker)) {
-                $timeTracker->setTSlogMessage($message, $severity + 1);
-            }
-            // Write error message to sys_log table (ext: belog, Tools->Log)
-            if ($errorLevel & $GLOBALS['TYPO3_CONF_VARS']['SYS']['belogErrorReporting']) {
-                // Silently catch in case an error occurs before a database connection exists,
-                // but DatabaseConnection fails to connect.
-                try {
-                    $this->writeLog($message, $severity);
-                } catch (\Exception $e) {
-                }
+        }
+        switch ($errorLevel) {
+            case E_USER_ERROR:
+            case E_RECOVERABLE_ERROR:
+                // no $flashMessageSeverity, as there will be no flash message for errors
+                $severity = 2;
+                break;
+            case E_USER_WARNING:
+            case E_WARNING:
+                $flashMessageSeverity = FlashMessage::WARNING;
+                $severity = 1;
+                break;
+            default:
+                $flashMessageSeverity = FlashMessage::NOTICE;
+                $severity = 0;
+        }
+        $logTitle = 'Core: Error handler (' . TYPO3_MODE . ')';
+        $message = $logTitle . ': ' . $message;
+
+        if ($errorLevel === E_USER_DEPRECATED) {
+            $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger('TYPO3.CMS.deprecations');
+            $logger->notice($message);
+            return true;
+        }
+        if ($this->logger) {
+            $this->logger->log(LogLevel::normalizeLevel(LogLevel::NOTICE) - $severity, $message);
+        }
+
+        // Write error message to TSlog (admin panel)
+        $timeTracker = $this->getTimeTracker();
+        if (is_object($timeTracker)) {
+            $timeTracker->setTSlogMessage($message, $severity + 1);
+        }
+        // Write error message to sys_log table (ext: belog, Tools->Log)
+        if ($errorLevel & $GLOBALS['TYPO3_CONF_VARS']['SYS']['belogErrorReporting']) {
+            // Silently catch in case an error occurs before a database connection exists.
+            try {
+                $this->writeLog($message, $severity);
+            } catch (\Exception $e) {
             }
-            if ($severity === 2) {
-                // Let the internal handler continue. This will stop the script
-                return false;
-            } else {
-                if ($this->debugMode) {
-                    /** @var $flashMessage \TYPO3\CMS\Core\Messaging\FlashMessage */
-                    $flashMessage = GeneralUtility::makeInstance(
-                        \TYPO3\CMS\Core\Messaging\FlashMessage::class,
-                        $message,
-                        'PHP ' . $errorLevels[$errorLevel],
-                        $severity
+        }
+        if ($severity === 2) {
+            // Let the internal handler continue. This will stop the script
+            return false;
+        }
+        if ($this->debugMode) {
+            /** @var \TYPO3\CMS\Core\Messaging\FlashMessage $flashMessage */
+            $flashMessage = GeneralUtility::makeInstance(
+                \TYPO3\CMS\Core\Messaging\FlashMessage::class,
+                $message,
+                $errorLevels[$errorLevel],
+                $flashMessageSeverity
                     );
-                    /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
-                    $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
-                    /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
-                    $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
-                    $defaultFlashMessageQueue->enqueue($flashMessage);
-                }
-                // Don't execute PHP internal error handler
-                return true;
-            }
+            /** @var \TYPO3\CMS\Core\Messaging\FlashMessageService $flashMessageService */
+            $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
+            /** @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue $defaultFlashMessageQueue */
+            $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
+            $defaultFlashMessageQueue->enqueue($flashMessage);
         }
+        // Don't execute PHP internal error handler
+        return true;
     }
 
     /**
@@ -179,7 +190,6 @@ class ErrorHandler implements ErrorHandlerInterface
      *
      * @param string $logMessage Default text that follows the message (in english!).
      * @param int $severity The error level of the message (0 = OK, 1 = warning, 2 = error)
-     * @return void
      */
     protected function writeLog($logMessage, $severity)
     {
@@ -211,7 +221,7 @@ class ErrorHandler implements ErrorHandlerInterface
                     'error' => $severity,
                     'details_nr' => 0,
                     'details' => str_replace('%', '%%', $logMessage),
-                    'log_data' => (empty($data) ? '' : serialize($data)),
+                    'log_data' => empty($data) ? '' : serialize($data),
                     'IP' => (string)GeneralUtility::getIndpEnv('REMOTE_ADDR'),
                     'tstamp' => $GLOBALS['EXEC_TIME'],
                     'workspace' => $workspace