Консольное логирование в json для кубера

Цель: отобразить в консоли выброшенное исключение в json-представлении

Протестируем, как работает казавшаяся правильным конфигурация:

Как видим, появляются 2 сообщения:

  1. исключение в json-представлении
  2. исключение в человеко-понятном представлении

Нас это не устраивает, попробуем включить как советуют в документации:

Как видим, появляются 3 сообщения:

  1. исключение в json-представлении
  2. исключение в json-представлении в укороченном виде
  3. исключение в человеко-понятном представлении

Нас это не устраивает, из monolog.yaml убираем handler с именем console. 

Копаем код и выясняем детали

Console\Application в случае выброса исключения в коде, пишет о проблеме 2 раза с такой последовательностью:

1. В \Symfony\Component\Console\Application::doRunCommand - ловится исключение и:
1.1 порождает $event = new \Symfony\Component\Console\Event\ConsoleErrorEvent($exception)
1.2 вызывает $this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
1.3 срабатывает \Symfony\Component\Console\EventListener\ErrorListener и логирует $exception с помощью
   логгера с именем "console", который с помощью хендлера "fingers_crossed" записывает ошибку

2. В \Symfony\Component\Console\Application::run - ловится исключение и:
2.1 вызывается кэлбэк-функция $renderException($e)
2.2 вызывается \Symfony\Component\Console\Application::renderThrowable($exception, OutputInterface $output)
2.3 Symfony\Component\Console\Output\ConsoleOutput $output печатает ошибку форматируя ее с помощью \Symfony\Component\Console\Formatter\OutputFormatter

Кстати, о пункте 1 написано в официальной документации https://symfony.com/doc/current/console.html#logging-command-errors

Сделаем второе сообщение тоже в json представлении

use Symfony\Bundle\FrameworkBundle\Console\Application;

class JsonApplication extends Application
{
    private LoggerInterface $logger;
    public function setLogger(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }
	public function renderThrowable(\Throwable $e, OutputInterface $output): void
	{
		$this->logger->error('JsonApplication error', ['exception' => $e]);
	}
}

$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$application = new JsonApplication($kernel);

$logger = new Logger('json_console_logger');
$handler = new StreamHandler(STDOUT, Logger::NOTICE);
$handler->setFormatter(new Monolog\Formatter\JsonFormatter());
$logger->pushHandler($handler);
$logger->pushProcessor(new My\Processor\AddInformationAboutExceptionProcessor());
$logger->pushProcessor(new My\Processor\EnvProcessor($application));

$application->setLogger($logger);
$application->run($input);

Изменяем консольное приложение

Два сообщения об ошибке - избыточно, и т.к. мы верим фреймворку, логике его работы и стабильности, то второе логирование можно отключить или сделать опциональным:

class ApplicationWithoutRepeatableInformationAboutException extends Application
{
	public function renderThrowable(\Throwable $e, OutputInterface $output): void
	{
        // http://yapro.ru/article/6438
	}
}

$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$application = in_array('--show-the-exception-information-again', $_SERVER['argv']) ?
	new Application($kernel) : new ApplicationWithoutRepeatableInformationAboutException($kernel);

$application->run($input);

Ловим неуловимое

На практике исключение может быть выброшено еще до создания Symfony Application. Например \Symfony\Component\Console\Input\ArgvInput::parseArgument() может выбросить исключение, если в команде указать опцию без значения: --my-option-without-value

bin/console my:command --env=prod --my-option-without-value
[09-Aug-2021 06:51:20 UTC] PHP Fatal error:  Uncaught Symfony\Component\Console\Exception\RuntimeException: No arguments expected, got "my:command". in /var/www/vendor/symfony/console/Input/ArgvInput.php:186
Stack trace:
#0 /var/www/vendor/symfony/console/Input/ArgvInput.php(80): Symfony\Component\Console\Input\ArgvInput->parseArgument('data-migration:...')
#1 /var/www/vendor/symfony/console/Input/Input.php(55): Symfony\Component\Console\Input\ArgvInput->parse()
#2 /var/www/vendor/symfony/console/Input/Input.php(41): Symfony\Component\Console\Input\Input->bind(Object(Symfony\Component\Console\Input\InputDefinition))
#3 /var/www/vendor/symfony/console/Input/ArgvInput.php(55): Symfony\Component\Console\Input\Input->__construct(Object(Symfony\Component\Console\Input\InputDefinition))
#4 /var/www/bin/console(25): Symfony\Component\Console\Input\ArgvInput->__construct(Array, Object(Symfony\Component\Console\Input\InputDefinition))
#5 {main}
  thrown in /var/www/vendor/symfony/console/Input/ArgvInput.php on line 186

А это значит, что для получения json-представления и в этом случае, нужно писать свой простой обработчик исключений и ошибок, удачи господа.

Источники: 1


21.01.2016 15:27