vendor/symfony/monolog-bridge/Handler/ConsoleHandler.php line 171

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