PHP Classes

File: src/Logger.php

Recommend this page to a friend!
  Classes of Vitaly   QuEasy PHP PSR Logger   src/Logger.php   Download  
File: src/Logger.php
Role: Class source
Content type: text/plain
Description: Class source
Class: QuEasy PHP PSR Logger
Log messages to containers compliant with PSR-3
Author: By
Last change:
Date: 3 years ago
Size: 14,634 bytes
 

Contents

Class file image Download
<?php /* * Queasy PHP Framework - Logger * * (c) Vitaly Demyanenko <vitaly_demyanenko@yahoo.com> * * For the full copyright and license information, please view the LICENSE file that was distributed with this source code. */ namespace queasy\log; use Exception; use ArrayAccess; use Psr\Log\AbstractLogger; use Psr\Log\LogLevel; /** * Logger aggregator class */ class Logger extends AbstractLogger { const DEFAULT_MIN_LEVEL = LogLevel::DEBUG; const DEFAULT_MAX_LEVEL = LogLevel::EMERGENCY; const DEFAULT_TIME_FORMAT = 'Y-m-d H:i:s.u T'; /** * @const string Message format that is acceptable by sprintf() function, passed parameters are (by order) * 1) time string, * 2) process name, * 3) session id, * 4) IP address, * 5) log level, * 6) message, * 7) context */ const DEFAULT_MESSAGE_FORMAT = '%1$s %2$s [%3$s] [%4$s] [%5$s] %6$s%7$s'; /** * Create logger instance. * * Logger class can be specified in config using 'class' option, by default Logger class will be used * * @param array|ArrayAccess $config Logger config * * @return int Integer log level value */ public static function create($config = array()) { $class = isset($config['class']) ? $config['class'] : 'queasy\log\Logger'; return new $class($config); } private static $logLevels = null; /** * Translate log level word into an integer value. * * @param string $level Log level string * * @return int Integer log level value */ public static function level2int($level) { if (null === self::$logLevels) { self::$logLevels = array( LogLevel::DEBUG => 0, LogLevel::INFO => 1, LogLevel::NOTICE => 2, LogLevel::WARNING => 3, LogLevel::ERROR => 4, LogLevel::CRITICAL => 5, LogLevel::ALERT => 6, LogLevel::EMERGENCY => 7 ); } return array_key_exists($level, self::$logLevels)? self::$logLevels[$level]: 0; } /** * @var callable Old error handler */ protected $oldErrorHandler; /** * @var callable Old exception handler */ protected $oldExceptionHandler; /** * @var array Subordinated loggers */ private $loggers; /** * The config instance. * * @var array|ArrayAccess */ private $config; /** * Constructor. * * @param array|ArrayAccess $config Logger configuration * * @throws InvalidArgumentException When a subordinated logger class doesn't exist or doesn't implement Psr\Log\LoggerInterface */ public function __construct($config = null, $setErrorHandlers = true) { $this->setConfig($config); if ($setErrorHandlers) { $this->oldErrorHandler = set_error_handler(array($this, 'handleError')); $this->oldExceptionHandler = set_exception_handler(array($this, 'handleException')); } } /** * Aggregator log method, will call all subordinated loggers depending on their min and max log levels. * * @param string $level Log level * @param string $message Log message * @param array|null $context Context */ public function log($level, $message, array $context = array()) { foreach ($this->loggers() as $logger) { if ((self::level2int($level) >= self::level2int($logger->minLevel())) && (self::level2int($level) <= self::level2int($logger->maxLevel()))) { $logger->log($level, $message, $context); } } return $this; } public function emergency($message, array $context = array()) { parent::emergency($message, $context); return $this; } public function alert($message, array $context = array()) { parent::alert($message, $context); return $this; } public function critical($message, array $context = array()) { parent::critical($message, $context); return $this; } public function error($message, array $context = array()) { parent::error($message, $context); return $this; } public function warning($message, array $context = array()) { parent::warning($message, $context); return $this; } public function notice($message, array $context = array()) { parent::notice($message, $context); return $this; } public function info($message, array $context = array()) { parent::info($message, $context); return $this; } public function debug($message, array $context = array()) { parent::debug($message, $context); return $this; } /** * Errors handler. * * @param int $errNo Error code * @param string $errStr Error message * @param string|null $errFile Error file * @param int|null $errLine Error line * * @return bool Indicates whether error was handled or not */ public function handleError($errNo, $errStr, $errFile = null, $errLine = null) { switch ($errNo) { case E_NOTICE: case E_USER_NOTICE: case E_DEPRECATED: case E_USER_DEPRECATED: case E_STRICT: $logLevel = LogLevel::NOTICE; break; case E_WARNING: case E_CORE_WARNING: case E_COMPILE_WARNING: case E_USER_WARNING: $logLevel = LogLevel::WARNING; break; default: $logLevel = LogLevel::ERROR; } $this->log($logLevel, $this->errorString($errNo, $errStr, $errFile, $errLine)); // TODO: Check if old handler is called automatically if ($this->oldErrorHandler) { return $this->oldErrorHandler($errNo, $errStr, $errFile, $errLine); } return false; } /** * Exceptions handler. * * @param Throwable|Exception $e Exception instance * * @return bool Indicates whether exception was handled or not */ public function handleException($exception) { $this->error('UNCAUGHT EXCEPTION.', array( 'exception' => $exception )); // TODO: Check if old handler is called automatically if ($this->oldExceptionHandler) { return $this->oldExceptionHandler($exception); } return false; } /** * Sets a config. * * @param array|ArrayAccess $config */ public function setConfig($config) { $this->config = $config; } public function __get($field) { if ('config' === $field) { return $this->config; } throw InvalidArgumentException::unknownField($field); } /** * Get subordinated loggers. * * @return array Subordinated loggers */ protected function loggers() { if (!$this->loggers) { $this->loggers = array(); foreach ($this->config() as $section) { if ((is_array($section) || is_object($section) && ($section instanceof ArrayAccess)) && isset($section['class'])) { $className = $section['class']; if (!class_exists($className)) { throw InvalidArgumentException::loggerNotExists($className); } $interfaceName = 'Psr\Log\LoggerInterface'; $interfaces = class_implements($className); if (!$interfaces || !isset($interfaces[$interfaceName])) { throw InvalidArgumentException::interfaceNotImplemented($className, $interfaceName); } $this->loggers[] = new $className($section, false); } } } return $this->loggers; } protected function config() { if (null === $this->config) { $this->setConfig(array()); } return $this->config; } /** * Get min log level. * * @return string Min log level */ protected function minLevel() { return isset($this->config['minLevel']) ? $this->config['minLevel'] : static::DEFAULT_MIN_LEVEL; } /** * Get max log level. * * @return string Max log level */ protected function maxLevel() { return isset($this->config['maxLevel']) ? $this->config['maxLevel'] : static::DEFAULT_MAX_LEVEL; } /** * Get process name. * * @return string Process name */ protected function processName() { return isset($this->config['processName']) ? $this->config['processName'] : null; } /** * Get time format. * * @return string Time format */ protected function timeFormat() { return isset($this->config['timeFormat']) ? $this->config['timeFormat'] : static::DEFAULT_TIME_FORMAT; } /** * Get message format. * * @return string Message format */ protected function messageFormat() { return isset($this->config['messageFormat']) ? $this->config['messageFormat'] : static::DEFAULT_MESSAGE_FORMAT; } /** * Build current time string. * * @return string Time string */ protected function logTimeString() { $uTimestamp = microtime(true); $timestamp = floor($uTimestamp); $milliseconds = '' . round(($uTimestamp - $timestamp) * 1000000); $milliseconds = str_pad($milliseconds, 6, '0'); return date(preg_replace('/(?<!\\\\)u/', $milliseconds, $this->timeFormat()), $timestamp); } /** * Get process name string. * * @return string Process name */ protected function processNameString() { return $this->processName(); } /** * Get session id string. * * @return string Session id */ protected function sessionIdString() { return session_id(); } /** * Get IP address string. * * @return string IP address */ protected function ipAddressString() { return filter_input(INPUT_SERVER, 'REMOTE_ADDR'); } /** * Get log level string. * * @param string $level Source log level * * @return string Log level */ protected function logLevelString($level) { return strtoupper($level); } /** * Build message and context string. * * @param string $message Source message * @param array $context Log message context * * @return string Message */ protected function messageString($message, array $context = null) { if (is_object($message) || is_array($message)) { $message = print_r($message, true); } elseif (null !== $context) { foreach ($context as $key => $value) { if (false !== strpos($message, '{' . $key . '}')) { $message = str_replace('{' . $key . '}', $value, $message); unset($context[$key]); } } unset($context['exception']); $message = $message . (count($context)? PHP_EOL . 'Context: ' . print_r($context, true): ''); } return $message; } /** * Build error string. * * @param int $errNo Error code * @param string $errStr Error message * @param string|null $errFile Error file * @param int|null $errLine Error line * * @return string Error string */ protected function errorString($errNo, $errStr, $errFile = null, $errLine = null) { return $errStr . ($errFile ? ' in ' . $errFile : '') . ($errLine ? ':' . $errLine : ''); } /** * Build exception string. * * @param array|null $context Log message context * * @return string Context string * * @throws \InvalidArgumentException When 'exception' key in $context doesn't contain a Throwable or Exception instance */ protected function exceptionString(array $context = null) { if (null === $context) { return ''; } $result = ''; $exception = null; if (isset($context['exception'])) { $exception = $context['exception']; if (interface_exists('\Throwable') && is_subclass_of($exception, '\Throwable') || ($exception instanceof Exception)) { $result .= sprintf('%s%s: %s in %s:%s%sStack trace:%s%s%s', PHP_EOL, get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine(), PHP_EOL, PHP_EOL, $exception->getTraceAsString(), PHP_EOL ); $previous = $exception->getPrevious(); if (is_object($previous)) { $result .= '---' . PHP_EOL; $result .= $this->exceptionString(array('exception' => $previous)); } } else { throw InvalidArgumentException::invalidContext(); } } return $result; } /** * Prepare final log message. * * @param string $level Log level * @param string $message Log message * @param array|null $context Context * * @return string Log message string */ protected function prepareMessage($level, $message, array $context = null) { return trim(sprintf( $this->messageFormat(), $this->logTimeString(), $this->processNameString(), $this->sessionIdString(), $this->ipAddressString(), $this->logLevelString($level), $this->messageString($message, $context), $this->exceptionString($context) )); } }