[!!!][FEATURE] Introduce PSR-3 Logging 56/61156/16
authorMarkus Klein <markus.klein@typo3.org>
Wed, 5 Jun 2019 23:21:19 +0000 (01:21 +0200)
committerFrank Naegler <frank.naegler@typo3.org>
Fri, 19 Jul 2019 14:46:54 +0000 (16:46 +0200)
TYPO3's Logging Framework (especially LogLevel and LogManager)
are not PSR-3 compatible, as it uses integers as constants
and not strings, as defined in PSR-3's interfaces.

This makes TYPO3 incompatible to use custom loggers,
or to interop with other logging systems.

The patch changes the logging API to depend on PSR-3
interfaces but strives for maximum compatibility.

Resolves: #88799
Releases: master
Change-Id: Ib41840cc87035c2323087877bac07e62c439482e
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/61156
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Frank Naegler <frank.naegler@typo3.org>
Tested-by: TYPO3com <noreply@typo3.com>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Frank Naegler <frank.naegler@typo3.org>
17 files changed:
typo3/sysext/adminpanel/Classes/Modules/Debug/Log.php
typo3/sysext/adminpanel/Classes/Modules/DebugModule.php
typo3/sysext/core/Classes/Error/ErrorHandler.php
typo3/sysext/core/Classes/Log/LogLevel.php
typo3/sysext/core/Classes/Log/LogManager.php
typo3/sysext/core/Classes/Log/LogRecord.php
typo3/sysext/core/Classes/Log/Logger.php
typo3/sysext/core/Classes/Log/Writer/DatabaseWriter.php
typo3/sysext/core/Classes/Log/Writer/FileWriter.php
typo3/sysext/core/Classes/Log/Writer/PhpErrorLogWriter.php
typo3/sysext/core/Classes/Log/Writer/SyslogWriter.php
typo3/sysext/core/Documentation/Changelog/master/Breaking-88799-IntroducedPSR-3CompatibleLoggingAPI.rst [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Feature-88799-IntroducedPSR-3CompatibleLoggingAPI.rst [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/Log/Writer/DatabaseWriterTest.php
typo3/sysext/core/Tests/Unit/Log/LogLevelTest.php
typo3/sysext/core/Tests/Unit/Log/LogRecordTest.php
typo3/sysext/core/Tests/Unit/Log/Writer/DatabaseWriterTest.php

index 99acb23..b60a922 100644 (file)
@@ -36,7 +36,10 @@ use TYPO3\CMS\Fluid\View\StandaloneView;
  */
 class Log extends AbstractSubModule implements DataProviderInterface, ContentProviderInterface, ModuleSettingsProviderInterface, InitializableInterface
 {
-    protected $logLevel = LogLevel::INFO;
+    /**
+     * @var int
+     */
+    protected $logLevel;
 
     /**
      * @var ConfigurationService
@@ -45,6 +48,7 @@ class Log extends AbstractSubModule implements DataProviderInterface, ContentPro
 
     public function __construct()
     {
+        $this->logLevel = LogLevel::normalizeLevel(LogLevel::INFO);
         $this->configurationService = GeneralUtility::makeInstance(ConfigurationService::class);
     }
 
@@ -73,8 +77,9 @@ class Log extends AbstractSubModule implements DataProviderInterface, ContentPro
      */
     public function getDataToStore(ServerRequestInterface $request): ModuleData
     {
+        $maxLevel = LogLevel::normalizeLevel(LogLevel::DEBUG);
         $levels = [];
-        for ($i = 1; $i <= LogLevel::DEBUG; $i++) {
+        for ($i = 1; $i <= $maxLevel; $i++) {
             $levels[] = [
                 'level' => $i,
                 'levelName' => LogLevel::getName($i),
@@ -110,8 +115,9 @@ class Log extends AbstractSubModule implements DataProviderInterface, ContentPro
         $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
         $view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
 
+        $maxLevel = LogLevel::normalizeLevel(LogLevel::DEBUG);
         $levels = [];
-        for ($i = 1; $i <= LogLevel::DEBUG; $i++) {
+        for ($i = 1; $i <= $maxLevel; $i++) {
             $levels[] = [
                 'level' => $i,
                 'levelName' => LogLevel::getName($i),
@@ -187,10 +193,11 @@ class Log extends AbstractSubModule implements DataProviderInterface, ContentPro
 
     protected function setLoggingConfigRecursive(array $logConfig): array
     {
+        $maxLevel = LogLevel::normalizeLevel(LogLevel::DEBUG);
         foreach ($logConfig as $key => $value) {
             if ($key === 'writerConfiguration') {
                 $logConfig[$key] = $value;
-                $logConfig[$key][LogLevel::DEBUG][InMemoryLogWriter::class] = [];
+                $logConfig[$key][$maxLevel][InMemoryLogWriter::class] = [];
             } elseif (is_array($value)) {
                 $logConfig[$key] = $this->setLoggingConfigRecursive($value);
             }
index 35b3f9e..3bb1713 100644 (file)
@@ -19,6 +19,7 @@ namespace TYPO3\CMS\Adminpanel\Modules;
 use TYPO3\CMS\Adminpanel\Log\InMemoryLogWriter;
 use TYPO3\CMS\Adminpanel\ModuleApi\AbstractModule;
 use TYPO3\CMS\Adminpanel\ModuleApi\ShortInfoProviderInterface;
+use TYPO3\CMS\Core\Log\LogLevel;
 use TYPO3\CMS\Core\Log\LogRecord;
 
 /**
@@ -56,7 +57,7 @@ class DebugModule extends AbstractModule implements ShortInfoProviderInterface
     public function getShortInfo(): string
     {
         $errorsAndWarnings = array_filter(InMemoryLogWriter::$log, function (LogRecord $entry) {
-            return $entry->getLevel() <= 4;
+            return LogLevel::normalizeLevel($entry->getLevel()) <= 4;
         });
         return sprintf($this->getLanguageService()->sL(
             'LLL:EXT:adminpanel/Resources/Private/Language/locallang_debug.xlf:module.shortinfo'
index f8de654..e82acf5 100644 (file)
@@ -147,7 +147,7 @@ class ErrorHandler implements ErrorHandlerInterface, LoggerAwareInterface
             return true;
         }
         if ($this->logger) {
-            $this->logger->log(LogLevel::NOTICE - $severity, $message);
+            $this->logger->log(LogLevel::normalizeLevel(LogLevel::NOTICE) - $severity, $message);
         }
 
         // Write error message to TSlog (admin panel)
index 8f8a8ee..2c43240 100644 (file)
@@ -14,111 +14,50 @@ namespace TYPO3\CMS\Core\Log;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Log\InvalidArgumentException;
+
 /**
  * Log levels according to RFC 3164
  */
-class LogLevel
+class LogLevel extends \Psr\Log\LogLevel
 {
     /**
-     * Emergency: system is unusable
-     *
-     * You'd likely not be able to reach the system. You better have an SLA in
-     * place when this happens.
-     *
-     * @var int
-     */
-    const EMERGENCY = 0;
-
-    /**
-     * Alert: action must be taken immediately
-     *
-     * Example: Entire website down, database unavailable, etc.
-     *
-     * @var int
-     */
-    const ALERT = 1;
-
-    /**
-     * Critical: critical conditions
-     *
-     * Example: unexpected exception.
-     *
-     * @var int
-     */
-    const CRITICAL = 2;
-
-    /**
-     * Error: error conditions
-     *
-     * Example: Runtime error.
-     *
-     * @var int
-     */
-    const ERROR = 3;
-
-    /**
-     * Warning: warning conditions
-     *
-     * Examples: Use of deprecated APIs, undesirable things that are not
-     * necessarily wrong.
-     *
-     * @var int
-     */
-    const WARNING = 4;
-
-    /**
-     * Notice: normal but significant condition
-     *
-     * Example: things you should have a look at, nothing to worry about though.
-     *
-     * @var int
-     */
-    const NOTICE = 5;
-
-    /**
-     * Informational: informational messages
-     *
-     * Examples: User logs in, SQL logs.
-     *
-     * @var int
-     */
-    const INFO = 6;
-
-    /**
-     * Debug: debug-level messages
-     *
-     * Example: Detailed status information.
-     *
-     * @var int
-     */
-    const DEBUG = 7;
-
-    /**
      * Reverse look up of log level to level name.
      *
      * @var array
      */
     protected static $levels = [
-        self::EMERGENCY => 'EMERGENCY',
-        self::ALERT => 'ALERT',
-        self::CRITICAL => 'CRITICAL',
-        self::ERROR => 'ERROR',
-        self::WARNING => 'WARNING',
-        self::NOTICE => 'NOTICE',
-        self::INFO => 'INFO',
-        self::DEBUG => 'DEBUG'
+        self::EMERGENCY,
+        self::ALERT,
+        self::CRITICAL,
+        self::ERROR,
+        self::WARNING,
+        self::NOTICE,
+        self::INFO,
+        self::DEBUG,
     ];
 
     /**
-     * Resolves the name of a log level.
+     * Resolves the name of a log level and returns it in upper case letters
      *
      * @param int $level Log level.
      * @return string Log level name.
      */
-    public static function getName($level)
+    public static function getName(int $level): string
+    {
+        return strtoupper(static::getInternalName($level));
+    }
+
+    /**
+     * Resolves the name of the log level and returns its internal string representation
+     *
+     * @param int $level
+     * @return string
+     */
+    public static function getInternalName(int $level): string
     {
         self::validateLevel($level);
-        return self::$levels[$level];
+        return static::$levels[$level];
     }
 
     /**
@@ -128,36 +67,39 @@ class LogLevel
      * @param int $level log level to validate
      * @return bool TRUE if the given log level is valid, FALSE otherwise
      */
-    public static function isValidLevel($level)
+    public static function isValidLevel(int $level): bool
     {
-        return \TYPO3\CMS\Core\Utility\MathUtility::isIntegerInRange($level, self::EMERGENCY, self::DEBUG);
+        return isset(static::$levels[$level]);
     }
 
     /**
      * Validates a log level.
      *
      * @param int $level log level to validate
-     * @throws \Psr\Log\InvalidArgumentException if the given log level is invalid
+     * @throws InvalidArgumentException if the given log level is invalid
      */
-    public static function validateLevel($level)
+    public static function validateLevel(int $level): void
     {
         if (!self::isValidLevel($level)) {
-            throw new \Psr\Log\InvalidArgumentException('Invalid Log Level.', 1321637121);
+            throw new InvalidArgumentException('Invalid Log Level ' . $level, 1321637121);
         }
     }
 
     /**
      * Normalizes level by converting it from string to integer
      *
-     * @param string $level
-     * @return int|string
+     * @param string|int $level
+     * @return int
      */
-    public static function normalizeLevel($level)
+    public static function normalizeLevel($level): int
     {
-        if (is_string($level) && defined(__CLASS__ . '::' . strtoupper($level))) {
-            $level = constant(__CLASS__ . '::' . strtoupper($level));
+        if (is_string($level)) {
+            if (!defined(__CLASS__ . '::' . strtoupper($level))) {
+                throw new InvalidArgumentException('Invalid Log Level ' . $level, 1550247164);
+            }
+            return array_search(strtolower($level), self::$levels, true);
         }
 
-        return $level;
+        return (int)$level;
     }
 }
index 0a79b3d..48f6711 100644 (file)
@@ -204,7 +204,7 @@ class LogManager implements SingletonInterface, LogManagerInterface
         // Validate the config
         foreach ($result as $level => $unused) {
             try {
-                LogLevel::validateLevel($level);
+                LogLevel::validateLevel(LogLevel::normalizeLevel($level));
             } catch (\Psr\Log\InvalidArgumentException $e) {
                 throw new \Psr\Log\InvalidArgumentException('The given severity level "' . htmlspecialchars($level) . '" for ' . $configurationKey . ' of logger "' . $loggerName . '" is not valid.', 1326406447);
             }
index a4fc912..688507e 100644 (file)
@@ -43,7 +43,7 @@ class LogRecord implements \ArrayAccess
     /**
      * Severity level
      *
-     * @var int
+     * @var string
      */
     protected $level = LogLevel::INFO;
 
@@ -90,12 +90,12 @@ class LogRecord implements \ArrayAccess
      * Constructor.
      *
      * @param string $component Affected component
-     * @param int $level Severity level (see \TYPO3\CMS\Core\Log\Level)
+     * @param string $level Severity level (see \TYPO3\CMS\Core\Log\Level)
      * @param string $message Log message
      * @param array $data Additional data
      * @param string $requestId Unique ID of the request
      */
-    public function __construct($component = '', $level, $message, array $data = [], string $requestId = '')
+    public function __construct(string $component, string $level, string $message, array $data = [], string $requestId = '')
     {
         $this->setRequestId($requestId)
             ->setCreated(microtime(true))
@@ -109,7 +109,7 @@ class LogRecord implements \ArrayAccess
      * Sets the affected component
      *
      * @param string $component Component key
-     * @return \TYPO3\CMS\Core\Log\LogRecord
+     * @return LogRecord
      */
     public function setComponent($component)
     {
@@ -131,7 +131,7 @@ class LogRecord implements \ArrayAccess
      * Sets the the creation time
      *
      * @param float $created Creation time as float
-     * @return \TYPO3\CMS\Core\Log\LogRecord
+     * @return LogRecord
      */
     public function setCreated($created)
     {
@@ -152,13 +152,13 @@ class LogRecord implements \ArrayAccess
     /**
      * Sets the severity level
      *
-     * @param int $level Severity level
-     * @return \TYPO3\CMS\Core\Log\LogRecord
+     * @param string $level Severity level
+     * @return LogRecord
      * @see \TYPO3\CMS\Core\Log\Level
      */
-    public function setLevel($level)
+    public function setLevel(string $level)
     {
-        LogLevel::validateLevel($level);
+        LogLevel::validateLevel(LogLevel::normalizeLevel($level));
         $this->level = $level;
         return $this;
     }
@@ -167,9 +167,9 @@ class LogRecord implements \ArrayAccess
      * Returns the severity level
      *
      * @see \TYPO3\CMS\Core\Log\Level
-     * @return int Severity level
+     * @return string Severity level
      */
-    public function getLevel()
+    public function getLevel(): string
     {
         return $this->level;
     }
@@ -178,7 +178,7 @@ class LogRecord implements \ArrayAccess
      * Sets log data array
      *
      * @param array $data
-     * @return \TYPO3\CMS\Core\Log\LogRecord
+     * @return LogRecord
      */
     public function setData($data)
     {
@@ -201,7 +201,7 @@ class LogRecord implements \ArrayAccess
      * and overwrites previously data using the same array keys.
      *
      * @param array $data
-     * @return \TYPO3\CMS\Core\Log\LogRecord
+     * @return LogRecord
      */
     public function addData(array $data)
     {
@@ -213,7 +213,7 @@ class LogRecord implements \ArrayAccess
      * Sets the log message
      *
      * @param string|object $message Log message. Usually a string, or an object that can be casted to string (implements __toString())
-     * @return \TYPO3\CMS\Core\Log\LogRecord
+     * @return LogRecord
      */
     public function setMessage($message)
     {
@@ -235,7 +235,7 @@ class LogRecord implements \ArrayAccess
      * Sets the request ID
      *
      * @param string $requestId
-     * @return \TYPO3\CMS\Core\Log\LogRecord
+     * @return LogRecord
      */
     public function setRequestId($requestId)
     {
@@ -262,7 +262,7 @@ class LogRecord implements \ArrayAccess
     public function __toString()
     {
         $timestamp = date('r', (int)$this->created);
-        $levelName = LogLevel::getName($this->level);
+        $levelName = strtoupper($this->level);
         $data = '';
         if (!empty($this->data)) {
             // According to PSR3 the exception-key may hold an \Exception
index 03e85c8..09a19c3 100644 (file)
@@ -14,12 +14,15 @@ namespace TYPO3\CMS\Core\Log;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Log\AbstractLogger;
+use TYPO3\CMS\Core\Log\Processor\ProcessorInterface;
+use TYPO3\CMS\Core\Log\Writer\WriterInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * Logger to log events and data for different components.
  */
-class Logger implements \Psr\Log\LoggerInterface
+class Logger extends AbstractLogger
 {
     /**
      * Logger name or component for which this logger is meant to be used for.
@@ -43,7 +46,7 @@ class Logger implements \Psr\Log\LoggerInterface
      *
      * @var int
      */
-    protected $minimumLogLevel = LogLevel::EMERGENCY;
+    protected $minimumLogLevel;
 
     /**
      * Writers used by this logger
@@ -69,6 +72,7 @@ class Logger implements \Psr\Log\LoggerInterface
     {
         $this->name = $name;
         $this->requestId = $requestId;
+        $this->minimumLogLevel = LogLevel::normalizeLevel(LogLevel::EMERGENCY);
     }
 
     /**
@@ -129,23 +133,25 @@ class Logger implements \Psr\Log\LoggerInterface
     /**
      * Adds a writer to this logger
      *
-     * @param int $minimumLevel
+     * @param string $minimumLevel
      * @param \TYPO3\CMS\Core\Log\Writer\WriterInterface $writer Writer object
      * @return \TYPO3\CMS\Core\Log\Logger $this
      */
-    public function addWriter($minimumLevel, Writer\WriterInterface $writer)
+    public function addWriter(string $minimumLevel, Writer\WriterInterface $writer)
     {
-        LogLevel::validateLevel($minimumLevel);
+        $minLevelAsNumber = LogLevel::normalizeLevel($minimumLevel);
+        LogLevel::validateLevel($minLevelAsNumber);
         // Cycle through all the log levels which are as severe as or higher
         // than $minimumLevel and add $writer to each severity level
-        for ($logLevelWhichTriggersWriter = LogLevel::EMERGENCY; $logLevelWhichTriggersWriter <= $minimumLevel; $logLevelWhichTriggersWriter++) {
-            if (!isset($this->writers[$logLevelWhichTriggersWriter])) {
-                $this->writers[$logLevelWhichTriggersWriter] = [];
+        for ($logLevelWhichTriggersWriter = LogLevel::normalizeLevel(LogLevel::EMERGENCY); $logLevelWhichTriggersWriter <= $minLevelAsNumber; $logLevelWhichTriggersWriter++) {
+            $logLevelName = LogLevel::getInternalName($logLevelWhichTriggersWriter);
+            if (!isset($this->writers[$logLevelName])) {
+                $this->writers[$logLevelName] = [];
             }
-            $this->writers[$logLevelWhichTriggersWriter][] = $writer;
+            $this->writers[$logLevelName][] = $writer;
         }
-        if ($minimumLevel > $this->getMinimumLogLevel()) {
-            $this->setMinimumLogLevel($minimumLevel);
+        if ($minLevelAsNumber > $this->getMinimumLogLevel()) {
+            $this->setMinimumLogLevel($minLevelAsNumber);
         }
         return $this;
     }
@@ -163,22 +169,24 @@ class Logger implements \Psr\Log\LoggerInterface
     /**
      * Adds a processor to the logger.
      *
-     * @param int $minimumLevel
+     * @param string $minimumLevel
      * @param \TYPO3\CMS\Core\Log\Processor\ProcessorInterface $processor The processor to add.
      */
-    public function addProcessor($minimumLevel, Processor\ProcessorInterface $processor)
+    public function addProcessor(string $minimumLevel, Processor\ProcessorInterface $processor)
     {
-        LogLevel::validateLevel($minimumLevel);
+        $minLevelAsNumber = LogLevel::normalizeLevel($minimumLevel);
+        LogLevel::validateLevel($minLevelAsNumber);
         // Cycle through all the log levels which are as severe as or higher
         // than $minimumLevel and add $processor to each severity level
-        for ($logLevelWhichTriggersProcessor = LogLevel::EMERGENCY; $logLevelWhichTriggersProcessor <= $minimumLevel; $logLevelWhichTriggersProcessor++) {
-            if (!isset($this->processors[$logLevelWhichTriggersProcessor])) {
-                $this->processors[$logLevelWhichTriggersProcessor] = [];
+        for ($logLevelWhichTriggersProcessor = LogLevel::normalizeLevel(LogLevel::EMERGENCY); $logLevelWhichTriggersProcessor <= $minLevelAsNumber; $logLevelWhichTriggersProcessor++) {
+            $logLevelName = LogLevel::getInternalName($logLevelWhichTriggersProcessor);
+            if (!isset($this->processors[$logLevelName])) {
+                $this->processors[$logLevelName] = [];
             }
-            $this->processors[$logLevelWhichTriggersProcessor][] = $processor;
+            $this->processors[$logLevelName][] = $processor;
         }
-        if ($minimumLevel > $this->getMinimumLogLevel()) {
-            $this->setMinimumLogLevel($minimumLevel);
+        if ($minLevelAsNumber > $this->getMinimumLogLevel()) {
+            $this->setMinimumLogLevel($minLevelAsNumber);
         }
     }
 
@@ -208,7 +216,7 @@ class Logger implements \Psr\Log\LoggerInterface
             return $this;
         }
         /** @var \TYPO3\CMS\Core\Log\LogRecord $record */
-        $record = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(LogRecord::class, $this->name, $level, $message, $data, $this->requestId);
+        $record = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(LogRecord::class, $this->name, LogLevel::getInternalName($level), $message, $data, $this->requestId);
         $record = $this->callProcessors($record);
         $this->writeLog($record);
         return $this;
@@ -224,6 +232,7 @@ class Logger implements \Psr\Log\LoggerInterface
     protected function callProcessors(LogRecord $record)
     {
         if (!empty($this->processors[$record->getLevel()])) {
+            /** @var ProcessorInterface $processor */
             foreach ($this->processors[$record->getLevel()] as $processor) {
                 $processedRecord = $processor->processLogRecord($record);
                 if (!$processedRecord instanceof LogRecord) {
@@ -243,105 +252,10 @@ class Logger implements \Psr\Log\LoggerInterface
     protected function writeLog(LogRecord $record)
     {
         if (!empty($this->writers[$record->getLevel()])) {
+            /** @var WriterInterface $writer */
             foreach ($this->writers[$record->getLevel()] as $writer) {
                 $writer->writeLog($record);
             }
         }
     }
-
-    /**
-     * Shortcut to log an EMERGENCY record.
-     *
-     * @param string $message Log message.
-     * @param array $data Additional data to log
-     * @return \TYPO3\CMS\Core\Log\Logger $this
-     */
-    public function emergency($message, array $data = [])
-    {
-        return $this->log(LogLevel::EMERGENCY, $message, $data);
-    }
-
-    /**
-     * Shortcut to log an ALERT record.
-     *
-     * @param string $message Log message.
-     * @param array $data Additional data to log
-     * @return \TYPO3\CMS\Core\Log\Logger $this
-     */
-    public function alert($message, array $data = [])
-    {
-        return $this->log(LogLevel::ALERT, $message, $data);
-    }
-
-    /**
-     * Shortcut to log a CRITICAL record.
-     *
-     * @param string $message Log message.
-     * @param array $data Additional data to log
-     * @return \TYPO3\CMS\Core\Log\Logger $this
-     */
-    public function critical($message, array $data = [])
-    {
-        return $this->log(LogLevel::CRITICAL, $message, $data);
-    }
-
-    /**
-     * Shortcut to log an ERROR record.
-     *
-     * @param string $message Log message.
-     * @param array $data Additional data to log
-     * @return \TYPO3\CMS\Core\Log\Logger $this
-     */
-    public function error($message, array $data = [])
-    {
-        return $this->log(LogLevel::ERROR, $message, $data);
-    }
-
-    /**
-     * Shortcut to log a WARNING record.
-     *
-     * @param string $message Log message.
-     * @param array $data Additional data to log
-     * @return \TYPO3\CMS\Core\Log\Logger $this
-     */
-    public function warning($message, array $data = [])
-    {
-        return $this->log(LogLevel::WARNING, $message, $data);
-    }
-
-    /**
-     * Shortcut to log a NOTICE record.
-     *
-     * @param string $message Log message.
-     * @param array $data Additional data to log
-     * @return \TYPO3\CMS\Core\Log\Logger $this
-     */
-    public function notice($message, array $data = [])
-    {
-        return $this->log(LogLevel::NOTICE, $message, $data);
-    }
-
-    /**
-     * Shortcut to log an INFORMATION record.
-     *
-     * @param string $message Log message.
-     * @param array $data Additional data to log
-     * @return \TYPO3\CMS\Core\Log\Logger $this
-     */
-    public function info($message, array $data = [])
-    {
-        return $this->log(LogLevel::INFO, $message, $data);
-    }
-
-    /**
-     * Shortcut to log a DEBUG record.
-     *
-     * @param string $message Log message.
-     * @param array $data Additional data to log
-     * @return \TYPO3\CMS\Core\Log\Logger $this
-     */
-    public function debug($message, array $data = [])
-    {
-        return $this->log(LogLevel::DEBUG, $message, $data);
-    }
 }
index afcd9c7..d3d4520 100644 (file)
@@ -14,6 +14,7 @@ namespace TYPO3\CMS\Core\Log\Writer;
  * The TYPO3 project - inspiring people to share!
  */
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Log\LogLevel;
 use TYPO3\CMS\Core\Log\LogRecord;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
@@ -74,7 +75,7 @@ class DatabaseWriter extends AbstractWriter
             'request_id' => $record->getRequestId(),
             'time_micro' => $record->getCreated(),
             'component' => $record->getComponent(),
-            'level' => $record->getLevel(),
+            'level' => LogLevel::normalizeLevel($record->getLevel()),
             'message' => $record->getMessage(),
             'data' => $data
         ];
index 7790fa6..d15946a 100644 (file)
@@ -17,7 +17,6 @@ namespace TYPO3\CMS\Core\Log\Writer;
 
 use TYPO3\CMS\Core\Core\Environment;
 use TYPO3\CMS\Core\Log\Exception\InvalidLogWriterConfigurationException;
-use TYPO3\CMS\Core\Log\LogLevel;
 use TYPO3\CMS\Core\Log\LogRecord;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\PathUtility;
@@ -145,7 +144,7 @@ class FileWriter extends AbstractWriter
     public function writeLog(LogRecord $record)
     {
         $timestamp = date('r', (int)$record->getCreated());
-        $levelName = LogLevel::getName($record->getLevel());
+        $levelName = strtoupper($record->getLevel());
         $data = '';
         $recordData = $record->getData();
         if (!empty($recordData)) {
index 3e5759a..239583e 100644 (file)
@@ -13,7 +13,6 @@ namespace TYPO3\CMS\Core\Log\Writer;
  *
  * The TYPO3 project - inspiring people to share!
  */
-use TYPO3\CMS\Core\Log\LogLevel;
 use TYPO3\CMS\Core\Log\LogRecord;
 
 /**
@@ -30,7 +29,7 @@ class PhpErrorLogWriter extends AbstractWriter
      */
     public function writeLog(LogRecord $record)
     {
-        $levelName = LogLevel::getName($record->getLevel());
+        $levelName = strtoupper($record->getLevel());
         $data = '';
         $recordData = $record->getData();
         if (!empty($recordData)) {
index de18946..30ab726 100644 (file)
@@ -13,6 +13,8 @@ namespace TYPO3\CMS\Core\Log\Writer;
  *
  * The TYPO3 project - inspiring people to share!
  */
+
+use TYPO3\CMS\Core\Log\LogLevel;
 use TYPO3\CMS\Core\Log\LogRecord;
 
 /**
@@ -131,7 +133,7 @@ class SyslogWriter extends AbstractWriter
      */
     public function writeLog(LogRecord $record)
     {
-        if (false === syslog($record->getLevel(), $this->getMessageForSyslog($record))) {
+        if (false === syslog(LogLevel::normalizeLevel($record->getLevel()), $this->getMessageForSyslog($record))) {
             throw new \RuntimeException('Could not write log record to syslog', 1345036337);
         }
         return $this;
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-88799-IntroducedPSR-3CompatibleLoggingAPI.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-88799-IntroducedPSR-3CompatibleLoggingAPI.rst
new file mode 100644 (file)
index 0000000..55f0e96
--- /dev/null
@@ -0,0 +1,71 @@
+.. include:: ../../Includes.txt
+
+==========================================================
+Breaking: #88799 - Introduced PSR-3 compatible Logging API
+==========================================================
+
+See :issue:`88799`
+
+Description
+===========
+
+With the adaption of the PSR-3 standard some PHP code had to be changed in order to reach compliance.
+The key difference is that log levels are now represented by strings rather than numbers. Note that the order
+of log levels is not affected and stays the same.
+
+The breaking changes mostly affect internal functionality and usually does not affect third-party extensions.
+
+
+Impact
+======
+
+The class :php:`\TYPO3\CMS\Core\Log\LogLevel` now extends from the PSR-3 base class and therefore inherits the new definition of the log levels (EMERGENCY to DEBUG) based on string values.
+
+The signatures of these to methods has been adjusted to accept the new :php:`LogLevel::*` constants:
+- :php:`\TYPO3\CMS\Core\Log\Logger::addWriter`
+- :php:`\TYPO3\CMS\Core\Log\Logger::addProcessor`
+
+The internal storage of the log level inside :php:`\TYPO3\CMS\Core\Log\LogRecord` has been adjusted, consequently the methods :php:`setLevel()` and :php:`getLevel()` accept :php:`string` values now.
+
+Also in case you have configured own logger or log targets, you have to adjust the integer level and use strings.
+
+Example:
+
+.. code-block:: php
+
+   # old configuration
+   $GLOBALS['TYPO3_CONF_VARS']['LOG']['TYPO3']['CMS']['Core']['writerConfiguration'] = [
+      7 => [
+         \TYPO3\CMS\Core\Log\Writer\FileWriter::class => [
+            'logFile' => 'typo3temp/var/log/core.log'
+         ]
+      ],
+   ];
+
+   # new configuration
+   $GLOBALS['TYPO3_CONF_VARS']['LOG']['TYPO3']['CMS']['Core']['writerConfiguration'] = [
+      'debug' => [
+         \TYPO3\CMS\Core\Log\Writer\FileWriter::class => [
+            'logFile' => 'typo3temp/var/log/core.log'
+         ]
+      ],
+   ];
+
+In case you have used the constants like :php:`LogLevel::DEBUG` you are fine and your config will work like before.
+
+
+Affected Installations
+======================
+
+Any installation using third-party extensions interacting with the internals of the Logging API.
+
+
+Migration
+=========
+
+There are two easy ways to convert the number to the string representation and vice versa:
+
+- Convert from number to string: :php:`$logLevel = LogLevel::getInternalName($logLevelAsNumber)`
+- Convert from string to number: :php:`$logLevelAsNumber = LogLevel::normalizeLevel($logLevel)`
+
+.. index:: PHP-API, NotScanned, ext:core
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-88799-IntroducedPSR-3CompatibleLoggingAPI.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-88799-IntroducedPSR-3CompatibleLoggingAPI.rst
new file mode 100644 (file)
index 0000000..10909cf
--- /dev/null
@@ -0,0 +1,26 @@
+.. include:: ../../Includes.txt
+
+=========================================================
+Feature: #88799 - Introduced PSR-3 compatible Logging API
+=========================================================
+
+See :issue:`88799`
+
+Description
+===========
+
+The existing Logging API evolved pretty similar to the later established PSR-3 logging standard.
+There was one key difference between the standard and the TYPO3 implementation though:
+The log levels were represented by numbers in TYPO3 whereas PSR-3 requires the string representation.
+
+TYPO3 therefore finally adapted to the standard and opens up for better integration with other libraries
+also following the standard.
+
+
+Impact
+======
+
+The adaption was not possible with a few breaking changes to the code base.
+Luckily no configuration changes are necessary, hence integrators are not affected by those.
+
+.. index:: PHP-API, ext:core
index da2cd5b..1d4fc18 100644 (file)
@@ -33,13 +33,13 @@ class DatabaseWriterTest extends \TYPO3\TestingFramework\Core\Functional\Functio
             'request_id' => '5862c0e7838ac',
             'time_micro' => 1469740000.0,
             'component' => 'aComponent',
-            'level' => LogLevel::DEBUG,
+            'level' => LogLevel::normalizeLevel(LogLevel::DEBUG),
             'message' => 'aMessage',
             'data' => ''
         ];
         $logRecord = new LogRecord(
             $logRecordData['component'],
-            $logRecordData['level'],
+            LogLevel::DEBUG,
             $logRecordData['message'],
             [],
             $logRecordData['request_id']
index 3bf0a47..9997a31 100644 (file)
@@ -37,7 +37,7 @@ class LogLevelTest extends UnitTestCase
      */
     public function isValidLevelDoesNotValidateInvalidLevels()
     {
-        $invalidLevels = [-1, 8, 1.5, 'string', [], new \stdClass(), false, null];
+        $invalidLevels = [-1, 8];
         foreach ($invalidLevels as $invalidLevel) {
             $this->assertFalse(\TYPO3\CMS\Core\Log\LogLevel::isValidLevel($invalidLevel));
         }
@@ -51,12 +51,6 @@ class LogLevelTest extends UnitTestCase
         return [
             'negative integer' => [-1],
             'higher level than expected' => [8],
-            'float' => [1.5],
-            'string' => ['string'],
-            'array' => [[]],
-            'object' => [new \stdClass()],
-            'boolean FALSE' => [false],
-            'NULL' => [null]
         ];
     }
 
@@ -79,13 +73,4 @@ class LogLevelTest extends UnitTestCase
     {
         $this->assertEquals(7, \TYPO3\CMS\Core\Log\LogLevel::normalizeLevel('debug'));
     }
-
-    /**
-     * @test
-     */
-    public function normalizeLevelDoesNotConvertInvalidLevel()
-    {
-        $levelString = 'invalid';
-        $this->assertEquals($levelString, \TYPO3\CMS\Core\Log\LogLevel::normalizeLevel($levelString));
-    }
 }
index a738e55..042c4a3 100644 (file)
@@ -109,10 +109,10 @@ class LogRecordTest extends UnitTestCase
     public function setLevelValidatesLevel()
     {
         $this->expectException(\Psr\Log\InvalidArgumentException::class);
-        $this->expectExceptionCode(1321637121);
+        $this->expectExceptionCode(1550247164);
 
         $record = $this->getRecord();
-        $record->setLevel(100);
+        $record->setLevel('foo');
     }
 
     /**
index d38dbf7..b58b40d 100644 (file)
@@ -14,10 +14,8 @@ namespace TYPO3\CMS\Core\Tests\Unit\Log\Writer;
  * The TYPO3 project - inspiring people to share!
  */
 
-use Prophecy\Argument;
-use TYPO3\CMS\Core\Database\Connection;
-use TYPO3\CMS\Core\Database\ConnectionPool;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
+use PHPUnit\Framework\MockObject\MockObject;
+use TYPO3\CMS\Core\Log\Writer\DatabaseWriter;
 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
 
 /**
@@ -31,37 +29,12 @@ class DatabaseWriterTest extends UnitTestCase
     public function getTableReturnsPreviouslySetTable()
     {
         $logTable = $this->getUniqueId('logtable_');
-        /** @var \TYPO3\CMS\Core\Log\Writer\DatabaseWriter|\PHPUnit_Framework_MockObject_MockObject $subject */
-        $subject = $this->getMockBuilder(\TYPO3\CMS\Core\Log\Writer\DatabaseWriter::class)
+        /** @var DatabaseWriter|MockObject $subject */
+        $subject = $this->getMockBuilder(DatabaseWriter::class)
             ->setMethods(['dummy'])
             ->disableOriginalConstructor()
             ->getMock();
         $subject->setLogTable($logTable);
         $this->assertSame($logTable, $subject->getLogTable());
     }
-
-    /**
-     * @test
-     */
-    public function writeLogInsertsToSpecifiedTable()
-    {
-        $logTable = $this->getUniqueId('logtable_');
-
-        $connectionProphecy = $this->prophesize(Connection::class);
-        $connectionPoolProphecy = $this->prophesize(ConnectionPool::class);
-        $connectionPoolProphecy->getConnectionForTable(Argument::cetera())->willReturn($connectionProphecy->reveal());
-
-        GeneralUtility::addInstance(ConnectionPool::class, $connectionPoolProphecy->reveal());
-        $logRecordMock = $this->createMock(\TYPO3\CMS\Core\Log\LogRecord::class);
-        $subject = $this->getMockBuilder(\TYPO3\CMS\Core\Log\Writer\DatabaseWriter::class)
-            ->setMethods(['dummy'])
-            ->disableOriginalConstructor()
-            ->getMock();
-        $subject->setLogTable($logTable);
-
-        // $logTable should end up as first insert argument
-        $connectionProphecy->insert($logTable, Argument::cetera())->willReturn(1);
-
-        $subject->writeLog($logRecordMock);
-    }
 }