AloFramework Logger
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Todo

Namespaces

  • AloFramework
    • Log
  • None
  • PHP
  • Psr
    • Log

Classes

  • Log

Exceptions

  • LogException
  1 <?php
  2 
  3     namespace AloFramework\Log;
  4 
  5     use Psr\Log\LoggerInterface;
  6     use Psr\Log\LogLevel;
  7     use Psr\Log\LoggerTrait;
  8     use Psr\Log\InvalidArgumentException;
  9 
 10     /**
 11      * AloFramework logger
 12      * @author Art <a.molcanovas@gmail.com>
 13      */
 14     class Log extends LogLevel implements LoggerInterface {
 15 
 16         use LoggerTrait;
 17 
 18         /**
 19          * Log identifier
 20          * @var string
 21          */
 22         protected $label;
 23 
 24         /**
 25          * Where the logs are stored
 26          * @var string
 27          */
 28         protected $savePath;
 29 
 30         /**
 31          * Log level set
 32          * @var string
 33          */
 34         protected $level;
 35 
 36         /**
 37          * Log element separator
 38          * @var string
 39          */
 40         private static $SEPARATOR = '|';
 41 
 42         /**
 43          * Log levels and their priorities
 44          * @var array
 45          */
 46         private static $priority = [self::DEBUG     => 1,
 47                                     self::INFO      => 2,
 48                                     self::NOTICE    => 3,
 49                                     self::WARNING   => 4,
 50                                     self::ERROR     => 5,
 51                                     self::CRITICAL  => 6,
 52                                     self::ALERT     => 7,
 53                                     self::EMERGENCY => 8];
 54 
 55         /**
 56          * Constructor
 57          * @author Art <a.molcanovas@gmail.com>
 58          *
 59          * @param string $logLevel Minimum log level to log. See class constants.
 60          * @param string $label    Log label, e.g. if you specify 'System' log entries will be prepended with SYSTEM
 61          * @param string $savePath The file to save the logs in. If omitted, the logs directory in this package will
 62          *                         be used with the filenames being today's date in YYYY-mm-dd format.
 63          *
 64          * @throws LogException             When a save path is specified, but the directory does not exist
 65          * @throws InvalidArgumentException When $log isn't scalar or $logLevel is invalid
 66          */
 67         function __construct($logLevel = self::DEBUG, $label = ALO_LOG_LABEL, $savePath = ALO_LOG_SAVE_PATH) {
 68             $this->logLabel($label)->level($logLevel)->savePath($savePath);
 69         }
 70 
 71         /**
 72          * Returns a string representation of the class
 73          * @author Art <a.molcanovas@gmail.com>
 74          * @return string
 75          */
 76         function __toString() {
 77             ob_start();
 78             d($this);
 79 
 80             return ob_get_clean();
 81         }
 82 
 83         /**
 84          * Gets or sets the log file path
 85          * @author Art <a.molcanovas@gmail.com>
 86          *
 87          * @throws LogException             When the directory doesn't exist
 88          * @throws InvalidArgumentException When the path isn't a valid string
 89          *
 90          * @param string|null $path Omit if using as a getter; The path to the log file otherwise
 91          *
 92          * @return Log|string
 93          */
 94         function savePath($path = null) {
 95             if ($path === null) {
 96                 return $this->savePath;
 97             } elseif (!is_string($path)) {
 98                 throw new InvalidArgumentException('The path must be a string');
 99             } else {
100                 $dir = dirname($path);
101 
102                 if (!file_exists($dir)) {
103                     throw new LogException('The directory does not exist: ' . $dir, LogException::E_INVALID_PATH);
104                 } else {
105                     $this->savePath = $path;
106                 }
107             }
108 
109             return $this;
110         }
111 
112         /**
113          * Gets or sets the log label
114          * @author Art <a.molcanovas@gmail.com>
115          *
116          * @param string|null $set Omit if using as a getter, the new log label if using as a setter.
117          *
118          * @throws InvalidArgumentException If attempting to set an invalid log level
119          *
120          * @return Log|string
121          */
122         function logLabel($set = null) {
123             if ($set === null) {
124                 return $this->label;
125             } elseif (!is_scalar($set)) {
126                 throw new InvalidArgumentException('The log label must be scalar. You tried to set (serialised): ' .
127                                                    serialize($set));
128             } else {
129                 $this->label = strtoupper($set);
130             }
131 
132             return $this;
133         }
134 
135         /**
136          * Gets or sets the log level
137          * @author Art <a.molcanovas@gmail.com>
138          *
139          * @param string|null $set Omit if using as a getter, the log level if using as a setter - see class constants
140          *                         or refer to the PSR-3 standards.
141          *
142          * @throws InvalidArgumentException If attempting to set an invalid log level
143          *
144          * @return Log|string
145          */
146         function level($set = null) {
147             if ($set === null) {
148                 return $this->level;
149             } elseif (!array_key_exists($set, self::$priority)) {
150                 throw new InvalidArgumentException('An invalid log level was passed: ' .
151                                                    (is_scalar($set) ? $set : serialize($set) . ' (serialised)'));
152             } else {
153                 $this->level = $set;
154             }
155 
156             return $this;
157         }
158 
159         /**
160          * Logs with an arbitrary level.
161          *
162          * @param string $level   The message's log level
163          * @param string $message The message
164          * @param array  $context The message context/placeholder asociative array
165          *
166          * @throws InvalidArgumentException When the log level is invalid
167          *
168          * @return bool Whether the message has been written
169          */
170         function log($level, $message, array $context = []) {
171             if (!array_key_exists($level, self::$priority)) {
172                 throw new InvalidArgumentException('Invalid log level supplied: ' .
173                                                    (is_scalar($level) ? $level : serialize($level) . ' (serialised)'));
174             } elseif (self::$priority[$level] < self::$priority[$this->level]) {
175                 return false;
176             } else {
177                 return $this->doWrite($level, $this->replaceContext($message, $context));
178             }
179         }
180 
181         /**
182          * Performs the actual log operation
183          * @author Art <a.molcanovas@gmail.com>
184          *
185          * @param string $level   Log level
186          * @param string $message Raw message
187          *
188          * @return bool
189          */
190         private function doWrite($level, $message) {
191             $fp = fopen($this->savePath, 'ab');
192 
193             if ($fp) {
194                 $trace = debug_backtrace();
195                 $trace = isset($trace[1]) ? $trace[1] : [];
196 
197                 $file    = isset($trace['file']) ? implode(DIRECTORY_SEPARATOR,
198                                                            array_slice(explode(DIRECTORY_SEPARATOR,
199                                                                                $trace['file']),
200                                                                        -2)) : '<<unknown file>>';
201                 $line    = isset($trace['line']) ? implode(DIRECTORY_SEPARATOR,
202                                                            array_slice(explode(DIRECTORY_SEPARATOR,
203                                                                                $trace['line']),
204                                                                        -2)) : '<<unknown line>>';
205                 $message =
206                     $level .
207                     ' ' .
208                     self::$SEPARATOR .
209                     ' ' .
210                     date('Y-m-d H:i:s') .
211                     ' ' .
212                     self::$SEPARATOR .
213                     ' ' .
214                     $this->label .
215                     ' ' .
216                     self::$SEPARATOR .
217                     ' ' .
218                     str_replace(self::$SEPARATOR, '\\' . self::$SEPARATOR, $message) .
219                     ' ' .
220                     self::$SEPARATOR .
221                     ' ' .
222                     $file .
223                     ' ' .
224                     self::$SEPARATOR .
225                     ' ' .
226                     $line .
227                     PHP_EOL;
228 
229                 $ok = [flock($fp, LOCK_EX),
230                        fwrite($fp, $message),
231                        flock($fp, LOCK_UN),
232                        fclose($fp)];
233 
234                 return !in_array(false, $ok, true);
235             } else {
236                 trigger_error('Failed to open log file ' . $this->savePath, E_USER_WARNING);
237             }
238 
239             return false;
240         }
241 
242         /**
243          * Replaces the placeholders in the message
244          *
245          * @param string $message The raw message
246          * @param array  $context The context/placeholder assoc array
247          *
248          * @return string
249          */
250         protected static function replaceContext($message, array $context = []) {
251             if (empty($context)) {
252                 return $message;
253             } else {
254                 $search = $replace = [];
255 
256                 foreach ($context as $k => $v) {
257                     $search[]  = '{' . $k . '}';
258                     $replace[] = $v;
259                 }
260 
261                 return str_ireplace($search, $replace, $message);
262             }
263         }
264     }
265 
AloFramework Logger API documentation generated byApiGen 2.8.0