From 05fc4bd0a9afc10b17fb411046258a764028905a Mon Sep 17 00:00:00 2001 From: Yorick Terweijden Date: Wed, 15 Nov 2017 18:02:37 +0100 Subject: [PATCH] Initial work on moving the Processor for messages to a separate class --- lib/Handler/AbstractHandler.php | 98 +++++-------------- lib/Handler/ArrayHandler.php | 8 -- lib/Handler/FileHandler.php | 16 +-- lib/Handler/GelfHandler.php | 12 ++- lib/Handler/HandlerInterface.php | 15 +++ lib/Handler/LogfileHandler.php | 24 +++++ lib/Handler/RedisHandler.php | 8 +- lib/Handler/ShellHandler.php | 8 +- lib/Handler/SymfonyConsoleHandler.php | 8 +- lib/Processor/AbstractProcessor.php | 57 +++++++++++ lib/Processor/DefaultProcessor.php | 79 +++++++++++++++ lib/Processor/LogfileProcessor.php | 33 +++++++ lib/Processor/ProcessorInterface.php | 29 ++++++ tests/Productsup/Flexilog/LoggerFileTest.php | 2 +- .../Productsup/Flexilog/LoggerLogfileTest.php | 38 +++++++ 15 files changed, 339 insertions(+), 96 deletions(-) create mode 100644 lib/Handler/LogfileHandler.php create mode 100644 lib/Processor/AbstractProcessor.php create mode 100644 lib/Processor/DefaultProcessor.php create mode 100644 lib/Processor/LogfileProcessor.php create mode 100644 lib/Processor/ProcessorInterface.php create mode 100644 tests/Productsup/Flexilog/LoggerLogfileTest.php diff --git a/lib/Handler/AbstractHandler.php b/lib/Handler/AbstractHandler.php index d4b932e..50f61f7 100644 --- a/lib/Handler/AbstractHandler.php +++ b/lib/Handler/AbstractHandler.php @@ -2,12 +2,15 @@ namespace Productsup\Flexilog\Handler; +use Productsup\Flexilog\Processor\ProcessorInterface; + /** * Abstract Handler to simplify the implementation of a Handler Interface */ abstract class AbstractHandler implements HandlerInterface { protected $logger = null; + protected $processor = null; public $verbose = 0; public $minLevel = 7; const LOG_LEVELS = array( @@ -30,9 +33,20 @@ abstract class AbstractHandler implements HandlerInterface * * @param string $minimalLevel the minimal severity of the LogLevel to start logging with * @param integer $verbose the Verbosity of the Log + * @param array $additionalParameters optional additional parameters required for the Handler + * @param \Productsup\Flexilog\Processor $processor the Processor to be used for the Handler, if none supplied + * the DefaultProcessor one will be used. */ - public function __construct($minimalLevel = 'debug', $verbose = 0) + public function __construct($minimalLevel = 'debug', + $verbose = 0, + array $additionalParameters = array(), + ProcessorInterface $processor = null) { + if (is_null($processor)) { + $this->processor = new \Productsup\Flexilog\Processor\DefaultProcessor(); + } else { + $this->processor = $processor; + } $this->verbose = $verbose; if (array_key_exists($minimalLevel, self::LOG_LEVELS)) { $this->minLevel = self::LOG_LEVELS[$minimalLevel]; @@ -46,88 +60,30 @@ public function init() { } - /** - * Set the Logger for the Handler - * - * @param \Productsup\Flexilog\Logger $logger - * - * @return HandlerInterface $this - */ - public function setLogger(\Productsup\Flexilog\Logger $logger) + public function setProcessor(\Productsup\Flexilog\Processor $processor) { - $this->logger = $logger; + $this->processor = $processor; return $this; } - /** - * Interpolates context values into the message placeholders. - * - * @param string $message Message to Log with Placeholders, defined by {curly}-braces. - * @param array $context Key/Value array with properties for the Placeholders. - * - * @return string $message Message with Placeholders replaced by the context. - */ - public function interpolate($message, array $context = array()) + public function getProcessor() { - // build a replacement array with braces around the context keys - $replace = array(); - foreach ($context as $key => $val) { - if (is_numeric($val)) { - $val = (string) $val; - } - if (is_string($val)) { - $replace['{'.$key.'}'] = $val; - } - } - - // interpolate replacement values into the message and return - return strtr($message, $replace); + return $this->processor; } /** - * Prepare the Context before interpolation - * Turns Objects into String representations. + * Set the Logger for the Handler * - * @param array $context Key/Value array with properties for the Placeholders. + * @param \Productsup\Flexilog\Logger $logger * - * @return array $conext Cleaned context + * @return HandlerInterface $this */ - public function prepareContext(array $context) + public function setLogger(\Productsup\Flexilog\Logger $logger) { - // cleanup any thrown exceptions - foreach ($context as $contextKey => $contextObject) { - // some reserved keywords - $reserved = array('date'); - if (in_array($contextKey, $reserved)) { - // prepend with an underscore - $newkey = '_'.$contextKey; - $context[$newkey] = $context[$contextKey]; - unset($context[$contextKey]); - $contextKey = $newkey; - } - - if ($contextObject instanceof \Exception) { - $contextObject = $contextObject->__toString(); - } elseif (is_array($contextObject)) { - $contextObject = json_encode($contextObject, true); - } elseif ($contextObject instanceof \DateTime) { - $contextObject = $contextObject->format(\DateTime::RFC3339); - } elseif (is_object($contextObject)) { - $contextObject = $contextObject->__toString(); - } elseif (is_resource($contextObject)) { - $contextObject = get_resource_type($contextObject); - } - - $context[$contextKey] = $contextObject; - - // clean empty values - if (empty($contextObject) && (string) $contextObject !== '0') { - unset($context[$contextKey]); - } - } + $this->logger = $logger; - return $context; + return $this; } /** @@ -148,8 +104,8 @@ public function prepare($level, $message, array $context = array()) $logInfo->validate(); $context = array_merge($context, $logInfo->getData()); $context['loglevel'] = $level; - $context = $this->prepareContext($context); - $message = $this->interpolate($message, $context); + $context = $this->processor->prepareContext($context); + $message = $this->processor->interpolate($message, $context); return array($message, $context); } diff --git a/lib/Handler/ArrayHandler.php b/lib/Handler/ArrayHandler.php index 440b6aa..b77424f 100644 --- a/lib/Handler/ArrayHandler.php +++ b/lib/Handler/ArrayHandler.php @@ -9,14 +9,6 @@ class ArrayHandler extends AbstractHandler { public $array = array(); - /** - * {@inheritDoc} - */ - public function __construct($minimalLevel = 'debug', $verbose = 0) - { - parent::__construct($minimalLevel, $verbose); - } - /** * {@inheritDoc} */ diff --git a/lib/Handler/FileHandler.php b/lib/Handler/FileHandler.php index bbc263b..3d9a87b 100644 --- a/lib/Handler/FileHandler.php +++ b/lib/Handler/FileHandler.php @@ -2,6 +2,8 @@ namespace Productsup\Flexilog\Handler; +use Productsup\Flexilog\Processor\ProcessorInterface; + /** * Write to a specified File */ @@ -14,13 +16,16 @@ class FileHandler extends AbstractHandler * * @param array $additionalParameters Pass an array with the `filename` as a key/value to be used. */ - public function __construct($minimalLevel, $verbose, $additionalParameters = array()) + public function __construct($minimalLevel = 'debug', + $verbose = 0, + array $additionalParameters = array(), + ProcessorInterface $processor = null) { if (!isset($additionalParameters['filename'])) { throw new \Exception('Filename parameter must be set'); } $filename = $additionalParameters['filename']; - parent::__construct($minimalLevel, $verbose); + parent::__construct($minimalLevel, $verbose, $additionalParameters, $processor); if ((!file_exists($filename) && file_put_contents($filename, '') === false) ||!is_writable($filename)) { throw new \Exception('No write permission on file:'.$filename); } @@ -35,13 +40,12 @@ public function __construct($minimalLevel, $verbose, $additionalParameters = arr */ public function write($level, $message, array $context = array()) { - $line = sprintf('%s %s: %s%s', date('H:i:s'), strtoupper($level), $message, PHP_EOL); - $this->writeToFile($line); + $this->writeToFile($this->processor->decorateMessage($level, $message, $context)); if ($this->verbose >= 1) { - $this->writeToFile("Extra Variables:".PHP_EOL); + $this->writeToFile($this->processor->contextSeparator()); foreach ($context as $contextKey => $contextObject) { - $this->writeToFile(sprintf("\t%s: %s%s", $contextKey, $contextObject, PHP_EOL)); + $this->writeToFile($this->processor->decorateContext($level, $contextKey, $contextObject)); } } } diff --git a/lib/Handler/GelfHandler.php b/lib/Handler/GelfHandler.php index fb3a8f4..8883e83 100644 --- a/lib/Handler/GelfHandler.php +++ b/lib/Handler/GelfHandler.php @@ -2,6 +2,7 @@ namespace Productsup\Flexilog\Handler; +use Productsup\Flexilog\Processor\ProcessorInterface; use Productsup\Flexilog\Exception\HandlerException; use Productsup\Flexilog\Exception\HandlerConnectionException; use Gelf; @@ -19,13 +20,16 @@ class GelfHandler extends AbstractHandler * * @param $additionalParameters array Pass the `server` and `port` as a key/value array */ - public function __construct($minimalLevel, $verbose, $additionalParameters = array()) + public function __construct($minimalLevel = 'debug', + $verbose = 0, + array $additionalParameters = array(), + ProcessorInterface $processor = null) { if (!isset($additionalParameters['server'])) { throw new HandlerException('Server parameter must be set inside the $additionalParameters'); } $port = isset($additionalParameters['port']) ? $additionalParameters['port'] : Gelf\Transport\UdpTransport::DEFAULT_PORT; - parent::__construct($minimalLevel, $verbose); + parent::__construct($minimalLevel, $verbose, $additionalParameters, $processor); $this->transport = new Gelf\Transport\UdpTransport( $additionalParameters['server'], $port, @@ -79,8 +83,8 @@ public function write($level, $message, array $context = array()) if (isset($context['fullMessage'])) { $fullMessage = $context['fullMessage']; unset($context['fullMessage']); - $fullMessage = $this->interpolate($fullMessage, $this->logger->getLogInfo()->getData()); - $fullMessage = $this->interpolate($fullMessage, $context); + $fullMessage = $this->processor->interpolate($fullMessage, $this->logger->getLogInfo()->getData()); + $fullMessage = $this->processor->interpolate($fullMessage, $context); $splitFullMessage = $this->splitMessage($fullMessage); } diff --git a/lib/Handler/HandlerInterface.php b/lib/Handler/HandlerInterface.php index e5ec0c5..66c4764 100644 --- a/lib/Handler/HandlerInterface.php +++ b/lib/Handler/HandlerInterface.php @@ -2,11 +2,26 @@ namespace Productsup\Flexilog\Handler; +use \Productsup\Flexilog\Processor\ProcessorInterface; + /** * Handler Interface for the Flexilog endpoint handlers */ interface HandlerInterface { + /** + * Construct the Handler, optionally with a minimal logging level + * + * @param string $minimalLevel the minimal severity of the LogLevel to start logging with + * @param integer $verbose the Verbosity of the Log + * @param array $additionalParameters optional additional parameters required for the Handler + * @param \Productsup\Flexilog\Processor $processor the Processor to be used for the Handler, if none supplied + * the DefaultProcessor one will be used. + */ + public function __construct($minimalLevel = 'debug', + $verbose = 0, + array $additionalParameters = array(), + ProcessorInterface $processor = null); /** * Write received Log information through the Handlers mechanism diff --git a/lib/Handler/LogfileHandler.php b/lib/Handler/LogfileHandler.php new file mode 100644 index 0000000..625c6dc --- /dev/null +++ b/lib/Handler/LogfileHandler.php @@ -0,0 +1,24 @@ +redis = new Redis(); $this->redisConfig = $redisConfig; - parent::__construct($minimalLevel, $verbose); + parent::__construct($minimalLevel, $verbose, $additionalParameters, $processor); } /** diff --git a/lib/Handler/ShellHandler.php b/lib/Handler/ShellHandler.php index d10955d..8c8f1a9 100644 --- a/lib/Handler/ShellHandler.php +++ b/lib/Handler/ShellHandler.php @@ -2,6 +2,7 @@ namespace Productsup\Flexilog\Handler; +use Productsup\Flexilog\Processor\ProcessorInterface; use League; /** @@ -14,9 +15,12 @@ class ShellHandler extends AbstractHandler /** * {@inheritDoc} */ - public function __construct($minimalLevel = 'debug', $verbose = 0) + public function __construct($minimalLevel = 'debug', + $verbose = 0, + array $additionalParameters = array(), + ProcessorInterface $processor = null) { - parent::__construct($minimalLevel, $verbose); + parent::__construct($minimalLevel, $verbose, $additionalParameters, $processor); $this->CLImate = new League\CLImate\CLImate(); $this->CLImate->output->defaultTo('error'); } diff --git a/lib/Handler/SymfonyConsoleHandler.php b/lib/Handler/SymfonyConsoleHandler.php index c715055..9c9501b 100644 --- a/lib/Handler/SymfonyConsoleHandler.php +++ b/lib/Handler/SymfonyConsoleHandler.php @@ -2,6 +2,7 @@ namespace Productsup\Flexilog\Handler; +use Productsup\Flexilog\Processor\ProcessorInterface; use Productsup\Flexilog\Log\LogLevel; use Productsup\Flexilog\Exception\HandlerException; use Symfony\Component\Console\Output\OutputInterface; @@ -43,12 +44,15 @@ class SymfonyConsoleHandler extends AbstractHandler /** * {@inheritDoc} */ - public function __construct($minimalLevel = 'debug', $verbose = 0, $additionalParameters = array()) + public function __construct($minimalLevel = 'debug', + $verbose = 0, + array $additionalParameters = array(), + ProcessorInterface $processor = null) { if (!isset($additionalParameters['outputInterface'])) { throw new HandlerException('OutputInterface parameter must be set'); } - parent::__construct($minimalLevel, $verbose); + parent::__construct($minimalLevel, $verbose, $additionalParameters, $processor); $this->outputInterface = $additionalParameters['outputInterface']; } diff --git a/lib/Processor/AbstractProcessor.php b/lib/Processor/AbstractProcessor.php new file mode 100644 index 0000000..b91a227 --- /dev/null +++ b/lib/Processor/AbstractProcessor.php @@ -0,0 +1,57 @@ +handler = $handler; + + return $this; + } + + /** + * Decorate a message before writing + * + * @param string $level + * @param string $message + * @param array $context + */ + public function decorateMessage($level, $message, array $context = array()) + { + return $message.PHP_EOL; + } + + /** + * Decorate the output of the context for the verbose levels + * + * @param string $level + * @param string $contextKey + * @param string $contextObject + */ + public function decorateContext($level, $contextKey, $contextObject) + { + return $contextObject.PHP_EOL; + } + + /** + * Return a separator between the Message and the Context if applicable. + */ + public function contextSeparator() + { + return PHP_EOL; + } +} diff --git a/lib/Processor/DefaultProcessor.php b/lib/Processor/DefaultProcessor.php new file mode 100644 index 0000000..12fc030 --- /dev/null +++ b/lib/Processor/DefaultProcessor.php @@ -0,0 +1,79 @@ + $val) { + if (is_numeric($val)) { + $val = (string) $val; + } + if (is_string($val)) { + $replace['{'.$key.'}'] = $val; + } + } + + // interpolate replacement values into the message and return + return strtr($message, $replace); + } + + /** + * Prepare the Context before interpolation + * Turns Objects into String representations. + * + * @param array $context Key/Value array with properties for the Placeholders. + * + * @return array $conext Cleaned context + */ + public function prepareContext(array $context) + { + // cleanup any thrown exceptions + foreach ($context as $contextKey => $contextObject) { + // some reserved keywords + $reserved = array('date'); + if (in_array($contextKey, $reserved)) { + // prepend with an underscore + $newkey = '_'.$contextKey; + $context[$newkey] = $context[$contextKey]; + unset($context[$contextKey]); + $contextKey = $newkey; + } + + if ($contextObject instanceof \Exception) { + $contextObject = $contextObject->__toString(); + } elseif (is_array($contextObject)) { + $contextObject = json_encode($contextObject, true); + } elseif ($contextObject instanceof \DateTime) { + $contextObject = $contextObject->format(\DateTime::RFC3339); + } elseif (is_object($contextObject)) { + $contextObject = $contextObject->__toString(); + } elseif (is_resource($contextObject)) { + $contextObject = get_resource_type($contextObject); + } + + $context[$contextKey] = $contextObject; + + // clean empty values + if (empty($contextObject) && (string) $contextObject !== '0') { + unset($context[$contextKey]); + } + } + + return $context; + } +} diff --git a/lib/Processor/LogfileProcessor.php b/lib/Processor/LogfileProcessor.php new file mode 100644 index 0000000..24fbfc3 --- /dev/null +++ b/lib/Processor/LogfileProcessor.php @@ -0,0 +1,33 @@ + - $handler = new Handler\FileHandler('trace', 0, ['filename' => '/Users/twisted/productsup/logger/test.log']), + $handler = new Handler\FileHandler('trace', 2, ['filename' => 'FileHandler_test.log']), )); $this->handler = $handler; return $logger; diff --git a/tests/Productsup/Flexilog/LoggerLogfileTest.php b/tests/Productsup/Flexilog/LoggerLogfileTest.php new file mode 100644 index 0000000..a0eaa57 --- /dev/null +++ b/tests/Productsup/Flexilog/LoggerLogfileTest.php @@ -0,0 +1,38 @@ + + $handler = new Handler\LogfileHandler('trace', 2, ['filename' => 'LogfileHandler_test.log']), + )); + $this->handler = $handler; + return $logger; + } + + function getLogs() + { + return $this->handler->logs; + } + + function testFullMessage() + { + $logger = $this->getLogger(); + $context = array( + 'fullMessage' => 'Blablablabla bla blaaaa blaaaa {foo} blaa', + 'foo' => 'bar', + 'exception' => new \Exception('wut', 0, new \Exception('Previous')), + 'someArray' => array('yo, sup', 'nm nm', 'a' => array('foo', 'bar' => 'baz')), + 'date' => new \DateTime() + ); + $logger->message('default message', $context); + $logger->message('critical message', $context, 'critical'); + } +}