* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Monolog\Formatter; use Exception; /** * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets * * @author Jordi Boggiano */ class NormalizerFormatter implements FormatterInterface { const SIMPLE_DATE = "Y-m-d H:i:s"; protected $dateFormat; /** * @param string $dateFormat The format of the timestamp: one supported by DateTime::format */ public function __construct($dateFormat = null) { $this->dateFormat = $dateFormat ?: static::SIMPLE_DATE; if (!function_exists('json_encode')) { throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s NormalizerFormatter'); } } /** * {@inheritdoc} */ public function format(array $record) { return $this->normalize($record); } /** * {@inheritdoc} */ public function formatBatch(array $records) { foreach ($records as $key => $record) { $records[$key] = $this->format($record); } return $records; } protected function normalize($data) { if (null === $data || is_scalar($data)) { return $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; } if ($data instanceof \DateTime) { return $data->format($this->dateFormat); } if (is_object($data)) { if ($data instanceof Exception) { return $this->normalizeException($data); } return sprintf("[object] (%s: %s)", get_class($data), $this->toJson($data, true)); } if (is_resource($data)) { return '[resource]'; } return '[unknown('.gettype($data).')]'; } protected function normalizeException(Exception $e) { $data = array( 'class' => get_class($e), 'message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile().':'.$e->getLine(), ); $trace = $e->getTrace(); foreach ($trace as $frame) { if (isset($frame['file'])) { $data['trace'][] = $frame['file'].':'.$frame['line']; } else { // We should again normalize the frames, because it might contain invalid items $data['trace'][] = $this->toJson($this->normalize($frame), true); } } if ($previous = $e->getPrevious()) { $data['previous'] = $this->normalizeException($previous); } return $data; } protected function toJson($data, $ignoreErrors = false) { // suppress json_encode errors since it's twitchy with some inputs if ($ignoreErrors) { if (version_compare(PHP_VERSION, '5.4.0', '>=')) { return @json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } return @json_encode($data); } if (version_compare(PHP_VERSION, '5.4.0', '>=')) { return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } return json_encode($data); } }