JsonFormatter.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <?php declare(strict_types=1);
  2. /*
  3. * This file is part of the Monolog package.
  4. *
  5. * (c) Jordi Boggiano <j.boggiano@seld.be>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Monolog\Formatter;
  11. use Monolog\Utils;
  12. use Throwable;
  13. /**
  14. * Encodes whatever record data is passed to it as json
  15. *
  16. * This can be useful to log to databases or remote APIs
  17. *
  18. * @author Jordi Boggiano <j.boggiano@seld.be>
  19. */
  20. class JsonFormatter extends NormalizerFormatter
  21. {
  22. public const BATCH_MODE_JSON = 1;
  23. public const BATCH_MODE_NEWLINES = 2;
  24. protected $batchMode;
  25. protected $appendNewline;
  26. protected $ignoreEmptyContextAndExtra;
  27. /**
  28. * @var bool
  29. */
  30. protected $includeStacktraces = false;
  31. public function __construct(int $batchMode = self::BATCH_MODE_JSON, bool $appendNewline = true, bool $ignoreEmptyContextAndExtra = false)
  32. {
  33. $this->batchMode = $batchMode;
  34. $this->appendNewline = $appendNewline;
  35. $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra;
  36. }
  37. /**
  38. * The batch mode option configures the formatting style for
  39. * multiple records. By default, multiple records will be
  40. * formatted as a JSON-encoded array. However, for
  41. * compatibility with some API endpoints, alternative styles
  42. * are available.
  43. */
  44. public function getBatchMode(): int
  45. {
  46. return $this->batchMode;
  47. }
  48. /**
  49. * True if newlines are appended to every formatted record
  50. */
  51. public function isAppendingNewlines(): bool
  52. {
  53. return $this->appendNewline;
  54. }
  55. /**
  56. * {@inheritdoc}
  57. *
  58. * @suppress PhanTypeComparisonToArray
  59. */
  60. public function format(array $record): string
  61. {
  62. $normalized = $this->normalize($record);
  63. if (isset($normalized['context']) && $normalized['context'] === []) {
  64. if ($this->ignoreEmptyContextAndExtra) {
  65. unset($normalized['context']);
  66. } else {
  67. $normalized['context'] = new \stdClass;
  68. }
  69. }
  70. if (isset($normalized['extra']) && $normalized['extra'] === []) {
  71. if ($this->ignoreEmptyContextAndExtra) {
  72. unset($normalized['extra']);
  73. } else {
  74. $normalized['extra'] = new \stdClass;
  75. }
  76. }
  77. return $this->toJson($normalized, true) . ($this->appendNewline ? "\n" : '');
  78. }
  79. /**
  80. * {@inheritdoc}
  81. */
  82. public function formatBatch(array $records): string
  83. {
  84. switch ($this->batchMode) {
  85. case static::BATCH_MODE_NEWLINES:
  86. return $this->formatBatchNewlines($records);
  87. case static::BATCH_MODE_JSON:
  88. default:
  89. return $this->formatBatchJson($records);
  90. }
  91. }
  92. public function includeStacktraces(bool $include = true)
  93. {
  94. $this->includeStacktraces = $include;
  95. }
  96. /**
  97. * Return a JSON-encoded array of records.
  98. */
  99. protected function formatBatchJson(array $records): string
  100. {
  101. return $this->toJson($this->normalize($records), true);
  102. }
  103. /**
  104. * Use new lines to separate records instead of a
  105. * JSON-encoded array.
  106. */
  107. protected function formatBatchNewlines(array $records): string
  108. {
  109. $instance = $this;
  110. $oldNewline = $this->appendNewline;
  111. $this->appendNewline = false;
  112. array_walk($records, function (&$value, $key) use ($instance) {
  113. $value = $instance->format($value);
  114. });
  115. $this->appendNewline = $oldNewline;
  116. return implode("\n", $records);
  117. }
  118. /**
  119. * Normalizes given $data.
  120. *
  121. * @param mixed $data
  122. *
  123. * @return mixed
  124. */
  125. protected function normalize($data, int $depth = 0)
  126. {
  127. if ($depth > $this->maxNormalizeDepth) {
  128. return 'Over '.$this->maxNormalizeDepth.' levels deep, aborting normalization';
  129. }
  130. if (is_array($data)) {
  131. $normalized = [];
  132. $count = 1;
  133. foreach ($data as $key => $value) {
  134. if ($count++ > $this->maxNormalizeItemCount) {
  135. $normalized['...'] = 'Over '.$this->maxNormalizeItemCount.' items ('.count($data).' total), aborting normalization';
  136. break;
  137. }
  138. $normalized[$key] = $this->normalize($value, $depth + 1);
  139. }
  140. return $normalized;
  141. }
  142. if ($data instanceof Throwable) {
  143. return $this->normalizeException($data, $depth);
  144. }
  145. if (is_resource($data)) {
  146. return parent::normalize($data);
  147. }
  148. return $data;
  149. }
  150. /**
  151. * Normalizes given exception with or without its own stack trace based on
  152. * `includeStacktraces` property.
  153. */
  154. protected function normalizeException(Throwable $e, int $depth = 0): array
  155. {
  156. $data = parent::normalizeException($e, $depth);
  157. if (!$this->includeStacktraces) {
  158. unset($data['trace']);
  159. }
  160. return $data;
  161. }
  162. }