3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
15 use Zend\Stdlib\ArrayUtils
;
16 use Zend\Stdlib\SplPriorityQueue
;
19 * Logging messages with a stack of backends
21 class Logger
implements LoggerInterface
24 * @const int defined from the BSD Syslog message severities
25 * @link http://tools.ietf.org/html/rfc3164
37 * Map native PHP errors to priority
41 public static $errorPriorityMap = array(
42 E_NOTICE
=> self
::NOTICE
,
43 E_USER_NOTICE
=> self
::NOTICE
,
44 E_WARNING
=> self
::WARN
,
45 E_CORE_WARNING
=> self
::WARN
,
46 E_USER_WARNING
=> self
::WARN
,
48 E_USER_ERROR
=> self
::ERR
,
49 E_CORE_ERROR
=> self
::ERR
,
50 E_RECOVERABLE_ERROR
=> self
::ERR
,
51 E_STRICT
=> self
::DEBUG
,
52 E_DEPRECATED
=> self
::DEBUG
,
53 E_USER_DEPRECATED
=> self
::DEBUG
,
57 * Registered error handler
61 protected static $registeredErrorHandler = false;
64 * Registered exception handler
68 protected static $registeredExceptionHandler = false;
71 * List of priority code => priority (short) name
75 protected $priorities = array(
76 self
::EMERG
=> 'EMERG',
77 self
::ALERT
=> 'ALERT',
81 self
::NOTICE
=> 'NOTICE',
83 self
::DEBUG
=> 'DEBUG',
89 * @var SplPriorityQueue
96 * @var SplPriorityQueue
98 protected $processors;
103 * @var WriterPluginManager
105 protected $writerPlugins;
110 * @var ProcessorPluginManager
112 protected $processorPlugins;
117 * Set options for an logger. Accepted options are:
118 * - writers: array of writers to add to this logger
119 * - exceptionhandler: if true register this logger as exceptionhandler
120 * - errorhandler: if true register this logger as errorhandler
122 * @param array|Traversable $options
124 * @throws Exception\InvalidArgumentException
126 public function __construct($options = null)
128 $this->writers
= new SplPriorityQueue();
130 if ($options instanceof Traversable
) {
131 $options = ArrayUtils
::iteratorToArray($options);
134 if (is_array($options)) {
135 if (isset($options['writers']) && is_array($options['writers'])) {
136 foreach ($options['writers'] as $writer) {
138 if (!isset($writer['name'])) {
139 throw new Exception\
InvalidArgumentException('Options must contain a name for the writer');
142 $priority = (isset($writer['priority'])) ?
$writer['priority'] : null;
143 $writerOptions = (isset($writer['options'])) ?
$writer['options'] : null;
145 $this->addWriter($writer['name'], $priority, $writerOptions);
149 if (isset($options['exceptionhandler']) && $options['exceptionhandler'] === true) {
150 static::registerExceptionHandler($this);
153 if (isset($options['errorhandler']) && $options['errorhandler'] === true) {
154 static::registerErrorHandler($this);
157 } elseif ($options) {
158 throw new Exception\
InvalidArgumentException('Options must be an array or an object implementing \Traversable ');
161 $this->processors
= new SplPriorityQueue();
165 * Shutdown all writers
169 public function __destruct()
171 foreach ($this->writers
as $writer) {
174 } catch (\Exception
$e) {}
179 * Get writer plugin manager
181 * @return WriterPluginManager
183 public function getWriterPluginManager()
185 if (null === $this->writerPlugins
) {
186 $this->setWriterPluginManager(new WriterPluginManager());
188 return $this->writerPlugins
;
192 * Set writer plugin manager
194 * @param string|WriterPluginManager $plugins
196 * @throws Exception\InvalidArgumentException
198 public function setWriterPluginManager($plugins)
200 if (is_string($plugins)) {
201 $plugins = new $plugins;
203 if (!$plugins instanceof WriterPluginManager
) {
204 throw new Exception\
InvalidArgumentException(sprintf(
205 'Writer plugin manager must extend %s\WriterPluginManager; received %s',
207 is_object($plugins) ?
get_class($plugins) : gettype($plugins)
211 $this->writerPlugins
= $plugins;
216 * Get writer instance
218 * @param string $name
219 * @param array|null $options
220 * @return Writer\WriterInterface
222 public function writerPlugin($name, array $options = null)
224 return $this->getWriterPluginManager()->get($name, $options);
228 * Add a writer to a logger
230 * @param string|Writer\WriterInterface $writer
231 * @param int $priority
232 * @param array|null $options
234 * @throws Exception\InvalidArgumentException
236 public function addWriter($writer, $priority = 1, array $options = null)
238 if (is_string($writer)) {
239 $writer = $this->writerPlugin($writer, $options);
240 } elseif (!$writer instanceof Writer\WriterInterface
) {
241 throw new Exception\
InvalidArgumentException(sprintf(
242 'Writer must implement %s\Writer\WriterInterface; received "%s"',
244 is_object($writer) ?
get_class($writer) : gettype($writer)
247 $this->writers
->insert($writer, $priority);
255 * @return SplPriorityQueue
257 public function getWriters()
259 return $this->writers
;
265 * @param SplPriorityQueue $writers
267 * @throws Exception\InvalidArgumentException
269 public function setWriters(SplPriorityQueue
$writers)
271 foreach ($writers->toArray() as $writer) {
272 if (!$writer instanceof Writer\WriterInterface
) {
273 throw new Exception\
InvalidArgumentException('Writers must be a SplPriorityQueue of Zend\Log\Writer');
276 $this->writers
= $writers;
281 * Get processor plugin manager
283 * @return ProcessorPluginManager
285 public function getProcessorPluginManager()
287 if (null === $this->processorPlugins
) {
288 $this->setProcessorPluginManager(new ProcessorPluginManager());
290 return $this->processorPlugins
;
294 * Set processor plugin manager
296 * @param string|ProcessorPluginManager $plugins
298 * @throws Exception\InvalidArgumentException
300 public function setProcessorPluginManager($plugins)
302 if (is_string($plugins)) {
303 $plugins = new $plugins;
305 if (!$plugins instanceof ProcessorPluginManager
) {
306 throw new Exception\
InvalidArgumentException(sprintf(
307 'processor plugin manager must extend %s\ProcessorPluginManager; received %s',
309 is_object($plugins) ?
get_class($plugins) : gettype($plugins)
313 $this->processorPlugins
= $plugins;
318 * Get processor instance
320 * @param string $name
321 * @param array|null $options
322 * @return Processor\ProcessorInterface
324 public function processorPlugin($name, array $options = null)
326 return $this->getProcessorPluginManager()->get($name, $options);
330 * Add a processor to a logger
332 * @param string|Processor\ProcessorInterface $processor
333 * @param int $priority
334 * @param array|null $options
336 * @throws Exception\InvalidArgumentException
338 public function addProcessor($processor, $priority = 1, array $options = null)
340 if (is_string($processor)) {
341 $processor = $this->processorPlugin($processor, $options);
342 } elseif (!$processor instanceof Processor\ProcessorInterface
) {
343 throw new Exception\
InvalidArgumentException(sprintf(
344 'Processor must implement Zend\Log\ProcessorInterface; received "%s"',
345 is_object($processor) ?
get_class($processor) : gettype($processor)
348 $this->processors
->insert($processor, $priority);
356 * @return SplPriorityQueue
358 public function getProcessors()
360 return $this->processors
;
364 * Add a message as a log entry
366 * @param int $priority
367 * @param mixed $message
368 * @param array|Traversable $extra
370 * @throws Exception\InvalidArgumentException if message can't be cast to string
371 * @throws Exception\InvalidArgumentException if extra can't be iterated over
372 * @throws Exception\RuntimeException if no log writer specified
374 public function log($priority, $message, $extra = array())
376 if (!is_int($priority) ||
($priority<0) ||
($priority>=count($this->priorities
))) {
377 throw new Exception\
InvalidArgumentException(sprintf(
378 '$priority must be an integer > 0 and < %d; received %s',
379 count($this->priorities
),
380 var_export($priority, 1)
383 if (is_object($message) && !method_exists($message, '__toString')) {
384 throw new Exception\
InvalidArgumentException(
385 '$message must implement magic __toString() method'
389 if (!is_array($extra) && !$extra instanceof Traversable
) {
390 throw new Exception\
InvalidArgumentException(
391 '$extra must be an array or implement Traversable'
393 } elseif ($extra instanceof Traversable
) {
394 $extra = ArrayUtils
::iteratorToArray($extra);
397 if ($this->writers
->count() === 0) {
398 throw new Exception\
RuntimeException('No log writer specified');
401 $timestamp = new DateTime();
403 if (is_array($message)) {
404 $message = var_export($message, true);
408 'timestamp' => $timestamp,
409 'priority' => (int) $priority,
410 'priorityName' => $this->priorities
[$priority],
411 'message' => (string) $message,
415 foreach ($this->processors
->toArray() as $processor) {
416 $event = $processor->process($event);
419 foreach ($this->writers
->toArray() as $writer) {
420 $writer->write($event);
427 * @param string $message
428 * @param array|Traversable $extra
431 public function emerg($message, $extra = array())
433 return $this->log(self
::EMERG
, $message, $extra);
437 * @param string $message
438 * @param array|Traversable $extra
441 public function alert($message, $extra = array())
443 return $this->log(self
::ALERT
, $message, $extra);
447 * @param string $message
448 * @param array|Traversable $extra
451 public function crit($message, $extra = array())
453 return $this->log(self
::CRIT
, $message, $extra);
457 * @param string $message
458 * @param array|Traversable $extra
461 public function err($message, $extra = array())
463 return $this->log(self
::ERR
, $message, $extra);
467 * @param string $message
468 * @param array|Traversable $extra
471 public function warn($message, $extra = array())
473 return $this->log(self
::WARN
, $message, $extra);
477 * @param string $message
478 * @param array|Traversable $extra
481 public function notice($message, $extra = array())
483 return $this->log(self
::NOTICE
, $message, $extra);
487 * @param string $message
488 * @param array|Traversable $extra
491 public function info($message, $extra = array())
493 return $this->log(self
::INFO
, $message, $extra);
497 * @param string $message
498 * @param array|Traversable $extra
501 public function debug($message, $extra = array())
503 return $this->log(self
::DEBUG
, $message, $extra);
507 * Register logging system as an error handler to log PHP errors
509 * @link http://www.php.net/manual/function.set-error-handler.php
510 * @param Logger $logger
511 * @param bool $continueNativeHandler
512 * @return mixed Returns result of set_error_handler
513 * @throws Exception\InvalidArgumentException if logger is null
515 public static function registerErrorHandler(Logger
$logger, $continueNativeHandler = false)
517 // Only register once per instance
518 if (static::$registeredErrorHandler) {
522 $errorPriorityMap = static::$errorPriorityMap;
524 $previous = set_error_handler(function ($level, $message, $file, $line) use ($logger, $errorPriorityMap, $continueNativeHandler) {
525 $iniLevel = error_reporting();
527 if ($iniLevel & $level) {
528 if (isset($errorPriorityMap[$level])) {
529 $priority = $errorPriorityMap[$level];
531 $priority = Logger
::INFO
;
533 $logger->log($priority, $message, array(
540 return !$continueNativeHandler;
543 static::$registeredErrorHandler = true;
548 * Unregister error handler
551 public static function unregisterErrorHandler()
553 restore_error_handler();
554 static::$registeredErrorHandler = false;
558 * Register logging system as an exception handler to log PHP exceptions
560 * @link http://www.php.net/manual/en/function.set-exception-handler.php
561 * @param Logger $logger
563 * @throws Exception\InvalidArgumentException if logger is null
565 public static function registerExceptionHandler(Logger
$logger)
567 // Only register once per instance
568 if (static::$registeredExceptionHandler) {
572 if ($logger === null) {
573 throw new Exception\
InvalidArgumentException('Invalid Logger specified');
576 $errorPriorityMap = static::$errorPriorityMap;
578 set_exception_handler(function ($exception) use ($logger, $errorPriorityMap) {
579 $logMessages = array();
582 $priority = Logger
::ERR
;
583 if ($exception instanceof ErrorException
&& isset($errorPriorityMap[$exception->getSeverity()])) {
584 $priority = $errorPriorityMap[$exception->getSeverity()];
588 'file' => $exception->getFile(),
589 'line' => $exception->getLine(),
590 'trace' => $exception->getTrace(),
592 if (isset($exception->xdebug_message
)) {
593 $extra['xdebug'] = $exception->xdebug_message
;
596 $logMessages[] = array(
597 'priority' => $priority,
598 'message' => $exception->getMessage(),
601 $exception = $exception->getPrevious();
602 } while ($exception);
604 foreach (array_reverse($logMessages) as $logMessage) {
605 $logger->log($logMessage['priority'], $logMessage['message'], $logMessage['extra']);
609 static::$registeredExceptionHandler = true;
614 * Unregister exception handler
616 public static function unregisterExceptionHandler()
618 restore_exception_handler();
619 static::$registeredExceptionHandler = false;