ConsoleHandler.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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\Handler;
  11. use Monolog\Formatter\FormatterInterface;
  12. use Monolog\Formatter\LineFormatter;
  13. use Monolog\Handler\AbstractProcessingHandler;
  14. use Monolog\Logger;
  15. use Symfony\Bridge\Monolog\Formatter\ConsoleFormatter;
  16. use Symfony\Component\Console\ConsoleEvents;
  17. use Symfony\Component\Console\Event\ConsoleCommandEvent;
  18. use Symfony\Component\Console\Event\ConsoleTerminateEvent;
  19. use Symfony\Component\Console\Output\ConsoleOutputInterface;
  20. use Symfony\Component\Console\Output\OutputInterface;
  21. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  22. use Symfony\Component\VarDumper\Dumper\CliDumper;
  23. /**
  24. * Writes logs to the console output depending on its verbosity setting.
  25. *
  26. * It is disabled by default and gets activated as soon as a command is executed.
  27. * Instead of listening to the console events, the output can also be set manually.
  28. *
  29. * The minimum logging level at which this handler will be triggered depends on the
  30. * verbosity setting of the console output. The default mapping is:
  31. * - OutputInterface::VERBOSITY_NORMAL will show all WARNING and higher logs
  32. * - OutputInterface::VERBOSITY_VERBOSE (-v) will show all NOTICE and higher logs
  33. * - OutputInterface::VERBOSITY_VERY_VERBOSE (-vv) will show all INFO and higher logs
  34. * - OutputInterface::VERBOSITY_DEBUG (-vvv) will show all DEBUG and higher logs, i.e. all logs
  35. *
  36. * This mapping can be customized with the $verbosityLevelMap constructor parameter.
  37. *
  38. * @author Tobias Schultze <http://tobion.de>
  39. */
  40. class ConsoleHandler extends AbstractProcessingHandler implements EventSubscriberInterface
  41. {
  42. private $output;
  43. private $verbosityLevelMap = [
  44. OutputInterface::VERBOSITY_QUIET => Logger::ERROR,
  45. OutputInterface::VERBOSITY_NORMAL => Logger::WARNING,
  46. OutputInterface::VERBOSITY_VERBOSE => Logger::NOTICE,
  47. OutputInterface::VERBOSITY_VERY_VERBOSE => Logger::INFO,
  48. OutputInterface::VERBOSITY_DEBUG => Logger::DEBUG,
  49. ];
  50. private $consoleFormatterOptions;
  51. /**
  52. * @param OutputInterface|null $output The console output to use (the handler remains disabled when passing null
  53. * until the output is set, e.g. by using console events)
  54. * @param bool $bubble Whether the messages that are handled can bubble up the stack
  55. * @param array $verbosityLevelMap Array that maps the OutputInterface verbosity to a minimum logging
  56. * level (leave empty to use the default mapping)
  57. */
  58. public function __construct(OutputInterface $output = null, bool $bubble = true, array $verbosityLevelMap = [], array $consoleFormatterOptions = [])
  59. {
  60. parent::__construct(Logger::DEBUG, $bubble);
  61. $this->output = $output;
  62. if ($verbosityLevelMap) {
  63. $this->verbosityLevelMap = $verbosityLevelMap;
  64. }
  65. $this->consoleFormatterOptions = $consoleFormatterOptions;
  66. }
  67. /**
  68. * {@inheritdoc}
  69. */
  70. public function isHandling(array $record): bool
  71. {
  72. return $this->updateLevel() && parent::isHandling($record);
  73. }
  74. /**
  75. * {@inheritdoc}
  76. */
  77. public function handle(array $record): bool
  78. {
  79. // we have to update the logging level each time because the verbosity of the
  80. // console output might have changed in the meantime (it is not immutable)
  81. return $this->updateLevel() && parent::handle($record);
  82. }
  83. /**
  84. * Sets the console output to use for printing logs.
  85. */
  86. public function setOutput(OutputInterface $output)
  87. {
  88. $this->output = $output;
  89. }
  90. /**
  91. * Disables the output.
  92. */
  93. public function close(): void
  94. {
  95. $this->output = null;
  96. parent::close();
  97. }
  98. /**
  99. * Before a command is executed, the handler gets activated and the console output
  100. * is set in order to know where to write the logs.
  101. */
  102. public function onCommand(ConsoleCommandEvent $event)
  103. {
  104. $output = $event->getOutput();
  105. if ($output instanceof ConsoleOutputInterface) {
  106. $output = $output->getErrorOutput();
  107. }
  108. $this->setOutput($output);
  109. }
  110. /**
  111. * After a command has been executed, it disables the output.
  112. */
  113. public function onTerminate(ConsoleTerminateEvent $event)
  114. {
  115. $this->close();
  116. }
  117. /**
  118. * {@inheritdoc}
  119. */
  120. public static function getSubscribedEvents()
  121. {
  122. return [
  123. ConsoleEvents::COMMAND => ['onCommand', 255],
  124. ConsoleEvents::TERMINATE => ['onTerminate', -255],
  125. ];
  126. }
  127. /**
  128. * {@inheritdoc}
  129. */
  130. protected function write(array $record): void
  131. {
  132. // at this point we've determined for sure that we want to output the record, so use the output's own verbosity
  133. $this->output->write((string) $record['formatted'], false, $this->output->getVerbosity());
  134. }
  135. /**
  136. * {@inheritdoc}
  137. */
  138. protected function getDefaultFormatter(): FormatterInterface
  139. {
  140. if (!class_exists(CliDumper::class)) {
  141. return new LineFormatter();
  142. }
  143. if (!$this->output) {
  144. return new ConsoleFormatter($this->consoleFormatterOptions);
  145. }
  146. return new ConsoleFormatter(array_replace([
  147. 'colors' => $this->output->isDecorated(),
  148. 'multiline' => OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity(),
  149. ], $this->consoleFormatterOptions));
  150. }
  151. /**
  152. * Updates the logging level based on the verbosity setting of the console output.
  153. *
  154. * @return bool Whether the handler is enabled and verbosity is not set to quiet
  155. */
  156. private function updateLevel(): bool
  157. {
  158. if (null === $this->output) {
  159. return false;
  160. }
  161. $verbosity = $this->output->getVerbosity();
  162. if (isset($this->verbosityLevelMap[$verbosity])) {
  163. $this->setLevel($this->verbosityLevelMap[$verbosity]);
  164. } else {
  165. $this->setLevel(Logger::DEBUG);
  166. }
  167. return true;
  168. }
  169. }