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;
 | |
|     }
 | |
| }
 |