LineFormatter.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <?php
  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. /**
  13. * Formats incoming records into a one-line string
  14. *
  15. * This is especially useful for logging to files
  16. *
  17. * @author Jordi Boggiano <j.boggiano@seld.be>
  18. * @author Christophe Coevoet <stof@notk.org>
  19. */
  20. class LineFormatter extends NormalizerFormatter
  21. {
  22. const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n";
  23. protected $format;
  24. protected $allowInlineLineBreaks;
  25. protected $ignoreEmptyContextAndExtra;
  26. protected $includeStacktraces;
  27. /**
  28. * @param string $format The format of the message
  29. * @param string $dateFormat The format of the timestamp: one supported by DateTime::format
  30. * @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries
  31. * @param bool $ignoreEmptyContextAndExtra
  32. */
  33. public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = false)
  34. {
  35. $this->format = $format ?: static::SIMPLE_FORMAT;
  36. $this->allowInlineLineBreaks = $allowInlineLineBreaks;
  37. $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra;
  38. parent::__construct($dateFormat);
  39. }
  40. public function includeStacktraces($include = true)
  41. {
  42. $this->includeStacktraces = $include;
  43. if ($this->includeStacktraces) {
  44. $this->allowInlineLineBreaks = true;
  45. }
  46. }
  47. public function allowInlineLineBreaks($allow = true)
  48. {
  49. $this->allowInlineLineBreaks = $allow;
  50. }
  51. public function ignoreEmptyContextAndExtra($ignore = true)
  52. {
  53. $this->ignoreEmptyContextAndExtra = $ignore;
  54. }
  55. /**
  56. * {@inheritdoc}
  57. */
  58. public function format(array $record)
  59. {
  60. $vars = parent::format($record);
  61. $output = $this->format;
  62. foreach ($vars['extra'] as $var => $val) {
  63. if (false !== strpos($output, '%extra.'.$var.'%')) {
  64. $output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output);
  65. unset($vars['extra'][$var]);
  66. }
  67. }
  68. foreach ($vars['context'] as $var => $val) {
  69. if (false !== strpos($output, '%context.'.$var.'%')) {
  70. $output = str_replace('%context.'.$var.'%', $this->stringify($val), $output);
  71. unset($vars['context'][$var]);
  72. }
  73. }
  74. if ($this->ignoreEmptyContextAndExtra) {
  75. if (empty($vars['context'])) {
  76. unset($vars['context']);
  77. $output = str_replace('%context%', '', $output);
  78. }
  79. if (empty($vars['extra'])) {
  80. unset($vars['extra']);
  81. $output = str_replace('%extra%', '', $output);
  82. }
  83. }
  84. foreach ($vars as $var => $val) {
  85. if (false !== strpos($output, '%'.$var.'%')) {
  86. $output = str_replace('%'.$var.'%', $this->stringify($val), $output);
  87. }
  88. }
  89. // remove leftover %extra.xxx% and %context.xxx% if any
  90. if (false !== strpos($output, '%')) {
  91. $output = preg_replace('/%(?:extra|context)\..+?%/', '', $output);
  92. }
  93. return $output;
  94. }
  95. public function formatBatch(array $records)
  96. {
  97. $message = '';
  98. foreach ($records as $record) {
  99. $message .= $this->format($record);
  100. }
  101. return $message;
  102. }
  103. public function stringify($value)
  104. {
  105. return $this->replaceNewlines($this->convertToString($value));
  106. }
  107. protected function normalizeException($e)
  108. {
  109. // TODO 2.0 only check for Throwable
  110. if (!$e instanceof \Exception && !$e instanceof \Throwable) {
  111. throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.Utils::getClass($e));
  112. }
  113. $previousText = '';
  114. if ($previous = $e->getPrevious()) {
  115. do {
  116. $previousText .= ', '.Utils::getClass($previous).'(code: '.$previous->getCode().'): '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine();
  117. } while ($previous = $previous->getPrevious());
  118. }
  119. $str = '[object] ('.Utils::getClass($e).'(code: '.$e->getCode().'): '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')';
  120. if ($this->includeStacktraces) {
  121. $str .= "\n[stacktrace]\n".$e->getTraceAsString()."\n";
  122. }
  123. return $str;
  124. }
  125. protected function convertToString($data)
  126. {
  127. if (null === $data || is_bool($data)) {
  128. return var_export($data, true);
  129. }
  130. if (is_scalar($data)) {
  131. return (string) $data;
  132. }
  133. if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
  134. return $this->toJson($data, true);
  135. }
  136. return str_replace('\\/', '/', $this->toJson($data, true));
  137. }
  138. protected function replaceNewlines($str)
  139. {
  140. if ($this->allowInlineLineBreaks) {
  141. if (0 === strpos($str, '{')) {
  142. return str_replace(array('\r', '\n'), array("\r", "\n"), $str);
  143. }
  144. return $str;
  145. }
  146. return str_replace(array("\r\n", "\r", "\n"), ' ', $str);
  147. }
  148. }