From 31dc01f931cc35d0a2aa7fa23a9a978aaf94949c Mon Sep 17 00:00:00 2001 From: Olivier Paroz Date: Fri, 9 Jan 2015 19:39:13 +0100 Subject: Improved normalizer * Its own class * Only JSON encodes the end result * Max depth for object recursion is set to 2 * Max number of array elements to log is set to 20 --- utility/normalizer.php | 167 ++++++++++++++++++++++++++++++++++++++++++++++ utility/smarterlogger.php | 154 ++++++++---------------------------------- 2 files changed, 193 insertions(+), 128 deletions(-) create mode 100644 utility/normalizer.php (limited to 'utility') diff --git a/utility/normalizer.php b/utility/normalizer.php new file mode 100644 index 00000000..287345c6 --- /dev/null +++ b/utility/normalizer.php @@ -0,0 +1,167 @@ + + * @author Jordi Boggiano + * + * @copyright Olivier Paroz 2015 + * @copyright Jordi Boggiano 2014-2015 + */ + +namespace OCA\GalleryPlus\Utility; + +/** + * Lets us call the main logger without having to add the context at every + * request + * + * @package OCA\GalleryPlus\Utility + */ +class Normalizer { + + /** + * Converts Objects, Arrays and Exceptions to String + * + * @param $data + * @param int $depth + * + * @return string|array + */ + public function normalize($data, $depth = 0) { + if (null === $data || is_scalar($data)) { + /*// utf8_encode only works for Latin1, we may have ANSI strings + if (is_string($data)) { + $data = mb_convert_encoding($data, "UTF-8"); + }*/ + + return $data; + } + + $traversable = $this->normalizeTraversable($data, $depth); + if ($traversable) { + return $traversable; + } + + $object = $this->normalizeObject($data, $depth); + if ($object) { + return $object; + } + + $resource = $this->normalizeResource($data); + if ($resource) { + return $resource; + } + + return '[unknown(' . gettype($data) . ')]'; + } + + /** + * Converts each element of a traversable variable to String + * + * @param $data + * @param int $depth + * + * @return array|null + */ + private function normalizeTraversable($data, $depth = 0) { + if (is_array($data) || $data instanceof \Traversable) { + $maxArrayRecursion = 20; // + $normalized = array(); + $count = 1; + + foreach ($data as $key => $value) { + if ($count++ >= $maxArrayRecursion) { + $normalized['...'] = + 'Over ' + . $maxArrayRecursion + . ' items, aborting normalization'; + break; + } + $normalized[$key] = $this->normalize($value, $depth); + } + + return $normalized; + } + + return null; + } + + /** + * Converts an Object to String + * + * @param mixed $data + * @param int $depth + * + * @return array|null + */ + private function normalizeObject($data, $depth) { + if (is_object($data)) { + if ($data instanceof \Exception) { + return $this->normalizeException($data); + } + + // We don't need to go too deep in the recursion + $maxObjectRecursion = 2; + $response = $data; + $arrayObject = new \ArrayObject($data); + $serializedObject = $arrayObject->getArrayCopy(); + + if ($depth < $maxObjectRecursion) { + $depth++; + $response = $this->normalize($serializedObject, $depth); + } + + // Don't convert to json here as we would double encode + return array( + sprintf("[object] (%s)", get_class($data)), + $response + ); + } + + return null; + } + + /** + * Converts an Exception to String + * + * @param \Exception $exception + * + * @return string + */ + private function normalizeException(\Exception $exception) { + $data = array( + 'class' => get_class($exception), + 'message' => $exception->getMessage(), + 'code' => $exception->getCode(), + 'file' => $exception->getFile() . ':' . $exception->getLine(), + ); + $trace = $exception->getTraceAsString(); + $data['trace'][] = $trace; + + $previous = $exception->getPrevious(); + if ($previous) { + $data['previous'] = $this->normalizeException($previous); + } + + return $data; + } + + /** + * Converts a resource to a String + * + * @param $data + * + * @return string|null + */ + private function normalizeResource($data) { + if (is_resource($data)) { + return "[resource] " . substr((string)$data, 0, 40); + } + + return null; + } + +} \ No newline at end of file diff --git a/utility/smarterlogger.php b/utility/smarterlogger.php index 4cc2e11c..4899b18e 100644 --- a/utility/smarterlogger.php +++ b/utility/smarterlogger.php @@ -6,12 +6,8 @@ * later. See the COPYING file. * * @author Olivier Paroz - * @author Bart Visscher - * @author Jordi Boggiano * * @copyright Olivier Paroz 2015 - * @copyright Bart Visscher 2013-2015 - * @copyright Jordi Boggiano 2014-2015 */ namespace OCA\GalleryPlus\Utility; @@ -22,7 +18,7 @@ use OCP\ILogger; * Lets us call the main logger without having to add the context at every * request * - * @package OCA\GalleryPlus\Middleware + * @package OCA\GalleryPlus\Utility */ class SmarterLogger implements ILogger { @@ -30,19 +26,26 @@ class SmarterLogger implements ILogger { * @type ILogger */ private $logger; + /** + * @type Normalizer + */ + private $normalizer; /*** * Constructor * * @param string $appName * @param ILogger $logger + * @param Normalizer $normalizer */ public function __construct( $appName, - ILogger $logger + ILogger $logger, + Normalizer $normalizer ) { $this->appName = $appName; $this->logger = $logger; + $this->normalizer = $normalizer; } /** @@ -143,7 +146,8 @@ class SmarterLogger implements ILogger { } /** - * Normalises a message and logs it with an arbitrary level. + * Converts the received log message to string before sending it to the + * ownCloud logger * * @param mixed $level * @param string $message @@ -152,140 +156,34 @@ class SmarterLogger implements ILogger { * @return mixed */ public function log($level, $message, array $context = array()) { - // interpolate $message as defined in PSR-3 - $replace = array(); - foreach ($context as $key => $val) { - // Allows us to dump arrays, objects and exceptions to the log - $val = $this->normalize($val); - $replace['{' . $key . '}'] = $val; - } - - // interpolate replacement values into the message and return - $message = strtr($message, $replace); - - $this->logger->log( - $level, $message, - array( - 'app' => $this->appName - ) - ); - - } + array_walk($context, [$this, 'contextNormalizer']); - /** - * Converts Objects, Arrays and Exceptions to String - * - * @param $data - * - * @return string - */ - private function normalize($data) { - if (null === $data || is_scalar($data)) { - return $data; + if (!isset($context['app'])) { + $context['app'] = $this->appName; } - if ($this->normalizeTraversable($data)) { - return $this->normalizeTraversable($data); - } - - if ($this->normalizeObject($data)) { - return $this->normalizeObject($data); - } - - if (is_resource($data)) { - return '[resource]'; - } - - return '[unknown(' . gettype($data) . ')]'; + $this->logger->log($level, $message, $context); } /** - * Converts a traversable variable to String + * Normalises the context parameters and JSON encodes and cleans up the + * result * - * @param $data - * - * @return string - */ - private function normalizeTraversable($data) { - if (is_array($data) || $data instanceof \Traversable) { - $normalized = array(); - $count = 1; - foreach ($data as $key => $value) { - if ($count >= 1000) { - $normalized['...'] = - 'Over 1000 items, aborting normalization'; - break; - } - $normalized[$key] = $this->normalize($value); - } - - //return $normalized; - return $this->toJson($normalized); - } - - return null; - } - - /** - * Converts an Object to String + * @todo: could maybe do a better job removing slashes * - * @param $data + * @param array $data * * @return string */ - private function normalizeObject($data) { - if (is_object($data)) { - if ($data instanceof \Exception) { - return $this->normalizeException($data); - } - - $arrayObject = new \ArrayObject($data); - $serializedObject = $arrayObject->getArrayCopy(); - - return sprintf( - "[object] (%s: %s)", get_class($data), - $this->toJson($serializedObject) + private function contextNormalizer(&$data) { + $data = $this->normalizer->normalize($data); + if (!is_string($data)) { + $data = @json_encode( + $data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); + // Removing null byte and double slashes from object properties + $data = str_replace(['\\u0000', '\\\\'], ["", "\\"], $data); } - - return null; } - /** - * Converts an Exception to String - * - * @param \Exception $exception - * - * @return string - */ - private function normalizeException(\Exception $exception) { - $data = array( - 'class' => get_class($exception), - 'message' => $exception->getMessage(), - 'file' => $exception->getFile() . ':' . $exception->getLine(), - ); - $trace = $exception->getTraceAsString(); - $data['trace'][] = $trace; - - $previous = $exception->getPrevious(); - if ($previous) { - $data['previous'] = $this->normalizeException($previous); - } - - return $this->toJson($data); - } - - /** - * JSON encodes data - * - * @param $data - * - * @return string - */ - private function toJson($data) { - // suppress json_encode errors since it's twitchy with some inputs - return @json_encode( - $data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE - ); - } } \ No newline at end of file -- cgit v1.2.3