215 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
		
		
			
		
	
	
			215 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
| 
								 | 
							
								<?php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * This file is part of the Monolog package.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * (c) Jordi Boggiano <j.boggiano@seld.be>
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * For the full copyright and license information, please view the LICENSE
							 | 
						||
| 
								 | 
							
								 * file that was distributed with this source code.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								namespace Monolog\Formatter;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use Exception;
							 | 
						||
| 
								 | 
							
								use Monolog\Utils;
							 | 
						||
| 
								 | 
							
								use Throwable;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Encodes whatever record data is passed to it as json
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * This can be useful to log to databases or remote APIs
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @author Jordi Boggiano <j.boggiano@seld.be>
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class JsonFormatter extends NormalizerFormatter
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    const BATCH_MODE_JSON = 1;
							 | 
						||
| 
								 | 
							
								    const BATCH_MODE_NEWLINES = 2;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    protected $batchMode;
							 | 
						||
| 
								 | 
							
								    protected $appendNewline;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * @var bool
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected $includeStacktraces = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * @param int $batchMode
							 | 
						||
| 
								 | 
							
								     * @param bool $appendNewline
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function __construct($batchMode = self::BATCH_MODE_JSON, $appendNewline = true)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $this->batchMode = $batchMode;
							 | 
						||
| 
								 | 
							
								        $this->appendNewline = $appendNewline;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * The batch mode option configures the formatting style for
							 | 
						||
| 
								 | 
							
								     * multiple records. By default, multiple records will be
							 | 
						||
| 
								 | 
							
								     * formatted as a JSON-encoded array. However, for
							 | 
						||
| 
								 | 
							
								     * compatibility with some API endpoints, alternative styles
							 | 
						||
| 
								 | 
							
								     * are available.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return int
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function getBatchMode()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return $this->batchMode;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * True if newlines are appended to every formatted record
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return bool
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function isAppendingNewlines()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return $this->appendNewline;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * {@inheritdoc}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function format(array $record)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return $this->toJson($this->normalize($record), true) . ($this->appendNewline ? "\n" : '');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * {@inheritdoc}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function formatBatch(array $records)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        switch ($this->batchMode) {
							 | 
						||
| 
								 | 
							
								            case static::BATCH_MODE_NEWLINES:
							 | 
						||
| 
								 | 
							
								                return $this->formatBatchNewlines($records);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            case static::BATCH_MODE_JSON:
							 | 
						||
| 
								 | 
							
								            default:
							 | 
						||
| 
								 | 
							
								                return $this->formatBatchJson($records);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * @param bool $include
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function includeStacktraces($include = true)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $this->includeStacktraces = $include;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Return a JSON-encoded array of records.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param  array  $records
							 | 
						||
| 
								 | 
							
								     * @return string
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected function formatBatchJson(array $records)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return $this->toJson($this->normalize($records), true);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Use new lines to separate records instead of a
							 | 
						||
| 
								 | 
							
								     * JSON-encoded array.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param  array  $records
							 | 
						||
| 
								 | 
							
								     * @return string
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected function formatBatchNewlines(array $records)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $instance = $this;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $oldNewline = $this->appendNewline;
							 | 
						||
| 
								 | 
							
								        $this->appendNewline = false;
							 | 
						||
| 
								 | 
							
								        array_walk($records, function (&$value, $key) use ($instance) {
							 | 
						||
| 
								 | 
							
								            $value = $instance->format($value);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        $this->appendNewline = $oldNewline;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return implode("\n", $records);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Normalizes given $data.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param mixed $data
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return mixed
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected function normalize($data, $depth = 0)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if ($depth > 9) {
							 | 
						||
| 
								 | 
							
								            return 'Over 9 levels deep, aborting normalization';
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (is_array($data) || $data instanceof \Traversable) {
							 | 
						||
| 
								 | 
							
								            $normalized = array();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            $count = 1;
							 | 
						||
| 
								 | 
							
								            foreach ($data as $key => $value) {
							 | 
						||
| 
								 | 
							
								                if ($count++ > 1000) {
							 | 
						||
| 
								 | 
							
								                    $normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization';
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                $normalized[$key] = $this->normalize($value, $depth+1);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return $normalized;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if ($data instanceof Exception || $data instanceof Throwable) {
							 | 
						||
| 
								 | 
							
								            return $this->normalizeException($data);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return $data;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Normalizes given exception with or without its own stack trace based on
							 | 
						||
| 
								 | 
							
								     * `includeStacktraces` property.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param Exception|Throwable $e
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return array
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    protected function normalizeException($e)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        // TODO 2.0 only check for Throwable
							 | 
						||
| 
								 | 
							
								        if (!$e instanceof Exception && !$e instanceof Throwable) {
							 | 
						||
| 
								 | 
							
								            throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $data = array(
							 | 
						||
| 
								 | 
							
								            'class' => Utils::getClass($e),
							 | 
						||
| 
								 | 
							
								            'message' => $e->getMessage(),
							 | 
						||
| 
								 | 
							
								            'code' => $e->getCode(),
							 | 
						||
| 
								 | 
							
								            'file' => $e->getFile().':'.$e->getLine(),
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if ($this->includeStacktraces) {
							 | 
						||
| 
								 | 
							
								            $trace = $e->getTrace();
							 | 
						||
| 
								 | 
							
								            foreach ($trace as $frame) {
							 | 
						||
| 
								 | 
							
								                if (isset($frame['file'])) {
							 | 
						||
| 
								 | 
							
								                    $data['trace'][] = $frame['file'].':'.$frame['line'];
							 | 
						||
| 
								 | 
							
								                } elseif (isset($frame['function']) && $frame['function'] === '{closure}') {
							 | 
						||
| 
								 | 
							
								                    // We should again normalize the frames, because it might contain invalid items
							 | 
						||
| 
								 | 
							
								                    $data['trace'][] = $frame['function'];
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    // We should again normalize the frames, because it might contain invalid items
							 | 
						||
| 
								 | 
							
								                    $data['trace'][] = $this->normalize($frame);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if ($previous = $e->getPrevious()) {
							 | 
						||
| 
								 | 
							
								            $data['previous'] = $this->normalizeException($previous);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return $data;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |