vendor/symfony/monolog-bridge/Formatter/ConsoleFormatter.php line 185

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  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 Symfony\Bridge\Monolog\Formatter;
  11. use Monolog\Formatter\FormatterInterface;
  12. use Monolog\Logger;
  13. use Symfony\Component\Console\Formatter\OutputFormatter;
  14. use Symfony\Component\VarDumper\Cloner\Data;
  15. use Symfony\Component\VarDumper\Cloner\Stub;
  16. use Symfony\Component\VarDumper\Cloner\VarCloner;
  17. use Symfony\Component\VarDumper\Dumper\CliDumper;
  18. /**
  19.  * Formats incoming records for console output by coloring them depending on log level.
  20.  *
  21.  * @author Tobias Schultze <http://tobion.de>
  22.  * @author GrĂ©goire Pineau <lyrixx@lyrixx.info>
  23.  */
  24. class ConsoleFormatter implements FormatterInterface
  25. {
  26.     public const SIMPLE_FORMAT "%datetime% %start_tag%%level_name%%end_tag% <comment>[%channel%]</> %message%%context%%extra%\n";
  27.     public const SIMPLE_DATE 'H:i:s';
  28.     private const LEVEL_COLOR_MAP = [
  29.         Logger::DEBUG => 'fg=white',
  30.         Logger::INFO => 'fg=green',
  31.         Logger::NOTICE => 'fg=blue',
  32.         Logger::WARNING => 'fg=cyan',
  33.         Logger::ERROR => 'fg=yellow',
  34.         Logger::CRITICAL => 'fg=red',
  35.         Logger::ALERT => 'fg=red',
  36.         Logger::EMERGENCY => 'fg=white;bg=red',
  37.     ];
  38.     private $options;
  39.     private $cloner;
  40.     private $outputBuffer;
  41.     private $dumper;
  42.     /**
  43.      * Available options:
  44.      *   * format: The format of the outputted log string. The following placeholders are supported: %datetime%, %start_tag%, %level_name%, %end_tag%, %channel%, %message%, %context%, %extra%;
  45.      *   * date_format: The format of the outputted date string;
  46.      *   * colors: If true, the log string contains ANSI code to add color;
  47.      *   * multiline: If false, "context" and "extra" are dumped on one line.
  48.      */
  49.     public function __construct(array $options = [])
  50.     {
  51.         $this->options array_replace([
  52.             'format' => self::SIMPLE_FORMAT,
  53.             'date_format' => self::SIMPLE_DATE,
  54.             'colors' => true,
  55.             'multiline' => false,
  56.             'level_name_format' => '%-9s',
  57.             'ignore_empty_context_and_extra' => true,
  58.         ], $options);
  59.         if (class_exists(VarCloner::class)) {
  60.             $this->cloner = new VarCloner();
  61.             $this->cloner->addCasters([
  62.                 '*' => [$this'castObject'],
  63.             ]);
  64.             $this->outputBuffer fopen('php://memory''r+');
  65.             if ($this->options['multiline']) {
  66.                 $output $this->outputBuffer;
  67.             } else {
  68.                 $output = [$this'echoLine'];
  69.             }
  70.             $this->dumper = new CliDumper($outputnullCliDumper::DUMP_LIGHT_ARRAY CliDumper::DUMP_COMMA_SEPARATOR);
  71.         }
  72.     }
  73.     /**
  74.      * {@inheritdoc}
  75.      *
  76.      * @return mixed
  77.      */
  78.     public function formatBatch(array $records)
  79.     {
  80.         foreach ($records as $key => $record) {
  81.             $records[$key] = $this->format($record);
  82.         }
  83.         return $records;
  84.     }
  85.     /**
  86.      * {@inheritdoc}
  87.      *
  88.      * @return mixed
  89.      */
  90.     public function format(array $record)
  91.     {
  92.         $record $this->replacePlaceHolder($record);
  93.         if (!$this->options['ignore_empty_context_and_extra'] || !empty($record['context'])) {
  94.             $context = ($this->options['multiline'] ? "\n" ' ').$this->dumpData($record['context']);
  95.         } else {
  96.             $context '';
  97.         }
  98.         if (!$this->options['ignore_empty_context_and_extra'] || !empty($record['extra'])) {
  99.             $extra = ($this->options['multiline'] ? "\n" ' ').$this->dumpData($record['extra']);
  100.         } else {
  101.             $extra '';
  102.         }
  103.         $formatted strtr($this->options['format'], [
  104.             '%datetime%' => $record['datetime'] instanceof \DateTimeInterface
  105.                 $record['datetime']->format($this->options['date_format'])
  106.                 : $record['datetime'],
  107.             '%start_tag%' => sprintf('<%s>'self::LEVEL_COLOR_MAP[$record['level']]),
  108.             '%level_name%' => sprintf($this->options['level_name_format'], $record['level_name']),
  109.             '%end_tag%' => '</>',
  110.             '%channel%' => $record['channel'],
  111.             '%message%' => $this->replacePlaceHolder($record)['message'],
  112.             '%context%' => $context,
  113.             '%extra%' => $extra,
  114.         ]);
  115.         return $formatted;
  116.     }
  117.     /**
  118.      * @internal
  119.      */
  120.     public function echoLine(string $lineint $depthstring $indentPad)
  121.     {
  122.         if (-!== $depth) {
  123.             fwrite($this->outputBuffer$line);
  124.         }
  125.     }
  126.     /**
  127.      * @internal
  128.      */
  129.     public function castObject($v, array $aStub $sbool $isNested): array
  130.     {
  131.         if ($this->options['multiline']) {
  132.             return $a;
  133.         }
  134.         if ($isNested && !$v instanceof \DateTimeInterface) {
  135.             $s->cut = -1;
  136.             $a = [];
  137.         }
  138.         return $a;
  139.     }
  140.     private function replacePlaceHolder(array $record): array
  141.     {
  142.         $message $record['message'];
  143.         if (!str_contains($message'{')) {
  144.             return $record;
  145.         }
  146.         $context $record['context'];
  147.         $replacements = [];
  148.         foreach ($context as $k => $v) {
  149.             // Remove quotes added by the dumper around string.
  150.             $v trim($this->dumpData($vfalse), '"');
  151.             $v OutputFormatter::escape($v);
  152.             $replacements['{'.$k.'}'] = sprintf('<comment>%s</>'$v);
  153.         }
  154.         $record['message'] = strtr($message$replacements);
  155.         return $record;
  156.     }
  157.     private function dumpData($databool $colors null): string
  158.     {
  159.         if (null === $this->dumper) {
  160.             return '';
  161.         }
  162.         if (null === $colors) {
  163.             $this->dumper->setColors($this->options['colors']);
  164.         } else {
  165.             $this->dumper->setColors($colors);
  166.         }
  167.         if (!$data instanceof Data) {
  168.             $data $this->cloner->cloneVar($data);
  169.         }
  170.         $data $data->withRefHandles(false);
  171.         $this->dumper->dump($data);
  172.         $dump stream_get_contents($this->outputBuffer, -10);
  173.         rewind($this->outputBuffer);
  174.         ftruncate($this->outputBuffer0);
  175.         return rtrim($dump);
  176.     }
  177. }