1 <?php
  2 
  3 use Psr\Log\AbstractLogger;
  4 use Psr\Log\LogLevel;
  5 
  6 /**
  7  * Simple Logger class.
  8  *
  9  * @author staabm
 10  *
 11  * @package redaxo\core
 12  */
 13 class rex_logger extends AbstractLogger
 14 {
 15     use rex_factory_trait;
 16 
 17     /**
 18      * @var rex_log_file
 19      */
 20     private static $file;
 21 
 22     /**
 23      * Returns the path to the system.log file.
 24      *
 25      * @return string
 26      */
 27     public static function getPath()
 28     {
 29         return rex_path::coreData('system.log');
 30     }
 31 
 32     /**
 33      * Shorthand: Logs the given Exception.
 34      *
 35      * @param Throwable|Exception $exception The Exception to log
 36      */
 37     public static function logException($exception)
 38     {
 39         if ($exception instanceof ErrorException) {
 40             self::logError($exception->getSeverity(), $exception->getMessage(), $exception->getFile(), $exception->getLine());
 41         } else {
 42             $logger = self::factory();
 43             $logger->log(get_class($exception), $exception->getMessage(), [], $exception->getFile(), $exception->getLine());
 44         }
 45     }
 46 
 47     /**
 48      * Shorthand: Logs a error message.
 49      *
 50      * @param int    $errno   The error code to log, e.g. E_WARNING
 51      * @param string $errstr  The error message
 52      * @param string $errfile The file in which the error occured
 53      * @param int    $errline The line of the file in which the error occured
 54      *
 55      * @throws InvalidArgumentException
 56      */
 57     public static function logError($errno, $errstr, $errfile, $errline)
 58     {
 59         if (!is_int($errno)) {
 60             throw new InvalidArgumentException('Expecting $errno to be integer, but ' . gettype($errno) . ' given!');
 61         }
 62         if (!is_string($errstr)) {
 63             throw new InvalidArgumentException('Expecting $errstr to be string, but ' . gettype($errstr) . ' given!');
 64         }
 65         if (!is_string($errfile)) {
 66             throw new InvalidArgumentException('Expecting $errfile to be string, but ' . gettype($errfile) . ' given!');
 67         }
 68         if (!is_int($errline)) {
 69             throw new InvalidArgumentException('Expecting $errline to be integer, but ' . gettype($errline) . ' given!');
 70         }
 71 
 72         $logger = self::factory();
 73         $logger->log(rex_error_handler::getErrorType($errno), $errstr, [], $errfile, $errline);
 74     }
 75 
 76     /**
 77      * Logs with an arbitrary level.
 78      *
 79      * @param mixed  $level
 80      * @param string $message
 81      * @param array  $context
 82      * @param string $file
 83      * @param int    $line
 84      *
 85      * @throws InvalidArgumentException
 86      */
 87     public function log($level, $message, array $context = [], $file = null, $line = null)
 88     {
 89         if (static::hasFactoryClass()) {
 90             static::callFactoryClass(__FUNCTION__, func_get_args());
 91             return;
 92         }
 93 
 94         if (!is_string($message)) {
 95             throw new InvalidArgumentException('Expecting $message to be string, but ' . gettype($message) . ' given!');
 96         }
 97 
 98         self::open();
 99         // build a replacement array with braces around the context keys
100         $replace = [];
101         foreach ($context as $key => $val) {
102             $replace['{' . $key . '}'] = $val;
103         }
104 
105         // interpolate replacement values into the message and return
106         $message = strtr($message, $replace);
107 
108         $logData = [$level, $message];
109         if ($file && $line) {
110             $logData[] = rex_path::relative($file);
111             $logData[] = $line;
112         }
113         self::$file->add($logData);
114 
115         // forward the error into phps' error log
116         error_log($message, 0);
117     }
118 
119     /**
120      * Prepares the logifle for later use.
121      */
122     public static function open()
123     {
124         // check if already opened
125         if (!self::$file) {
126             self::$file = new rex_log_file(self::getPath(), 2000000);
127         }
128     }
129 
130     /**
131      * Closes the logfile. The logfile is not be able to log further message after beeing closed.
132      *
133      * You dont need to close the logfile manually when it was registered during the request.
134      */
135     public static function close()
136     {
137         self::$file = null;
138     }
139 
140     /**
141      * Map php error codes to PSR3 error levels.
142      *
143      * @param int $errno a php error code, e.g. E_ERROR
144      *
145      * @return string
146      */
147     public static function getLogLevel($errno)
148     {
149         switch ($errno) {
150             case E_STRICT:
151 
152             case E_USER_DEPRECATED:
153             case E_DEPRECATED:
154 
155             case E_USER_WARNING:
156             case E_WARNING:
157             case E_COMPILE_WARNING:
158                 return LogLevel::WARNING;
159 
160             case E_USER_NOTICE:
161             case E_NOTICE:
162                 return LogLevel::NOTICE;
163 
164             default:
165                 return LogLevel::ERROR;
166         }
167     }
168 
169     /**
170      * @return self
171      */
172     public static function factory()
173     {
174         $class = self::getFactoryClass();
175         return new $class();
176     }
177 }
178