From de43de4ca0e68b9d32ff256621198d78cefd2d46 Mon Sep 17 00:00:00 2001 From: Julien Rosset Date: Wed, 9 Aug 2023 19:16:56 +0200 Subject: [PATCH] Generalize application for any LoggerInterface --- composer.json | 3 +- .../ApplicationWithCommandMonolog.php | 51 +------ .../ApplicationWithCommandOutputInterface.php | 29 +--- .../Monolog/ConsoleOutputWithMonolog.php | 45 +++--- .../Monolog/TMonologApplication.php | 57 ++++++++ .../Monolog/TOutputInterfaceWithMonolog.php | 76 ---------- src/CliProgram/Output/OutputWithLogger.php | 130 ++++++++++++++++++ src/CliProgram/Output/OutputWrapper.php | 112 +++++++++++++++ .../Output/TCommandOutputApplication.php | 38 +++++ tests/Commands/Hello.php | 4 +- 10 files changed, 372 insertions(+), 173 deletions(-) create mode 100644 src/CliProgram/Monolog/TMonologApplication.php delete mode 100644 src/CliProgram/Monolog/TOutputInterfaceWithMonolog.php create mode 100644 src/CliProgram/Output/OutputWithLogger.php create mode 100644 src/CliProgram/Output/OutputWrapper.php create mode 100644 src/CliProgram/Output/TCommandOutputApplication.php diff --git a/composer.json b/composer.json index d333b36..b968086 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ "symfony/console": "^5.4", "jrosset/betterphptoken": "^1.0", "jrosset/collections": "^2.3", - "jrosset/extendedmonolog": "^1.1" + "jrosset/extendedmonolog": "^1.1", + "psr/log": "^2.0" }, "autoload": { "psr-4": { diff --git a/src/CliProgram/ApplicationWithCommandMonolog.php b/src/CliProgram/ApplicationWithCommandMonolog.php index cf291cc..1fc3e8b 100644 --- a/src/CliProgram/ApplicationWithCommandMonolog.php +++ b/src/CliProgram/ApplicationWithCommandMonolog.php @@ -2,21 +2,14 @@ namespace jrosset\CliProgram; -use jrosset\CliProgram\Monolog\ConsoleOutputWithMonolog; -use jrosset\ExtendedMonolog\ExceptionLogger; +use jrosset\CliProgram\Monolog\TMonologApplication; use jrosset\ExtendedMonolog\LogDirectoryHandler; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; /** * An application with a {@see LogDirectoryHandler Monolog log directory} for each command */ class ApplicationWithCommandMonolog extends ApplicationWithCommandOutputInterface { - /** - * @var string The main log directory for Monolog: on subdirectory by command - */ - private string $logMainDirectory; + use TMonologApplication; /** * Initialization @@ -29,44 +22,4 @@ class ApplicationWithCommandMonolog extends ApplicationWithCommandOutputInterfac parent::__construct($name, $version); $this->setLogMainDirectory($logMainDirectory); } - - /** - * @inheritDoc - */ - protected function getOutputInterfaceForCommand (Command $command, InputInterface $input, OutputInterface $output): OutputInterface { - return new ConsoleOutputWithMonolog( - new ExceptionLogger( - $command->getName(), - [ - new LogDirectoryHandler( - $this->getLogMainDirectory() . DIRECTORY_SEPARATOR - . str_replace(':', DIRECTORY_SEPARATOR, $command->getName()) - ), - ] - ), - $output->getVerbosity(), - $output->isDecorated(), - $output->getFormatter() - ); - } - - /** - * The main log directory for Monolog: on subdirectory by command - * - * @return string The main log directory for Monolog: on subdirectory by command - */ - public function getLogMainDirectory (): string { - return $this->logMainDirectory; - } - /** - * Set the main log directory for Monolog: on subdirectory by command - * - * @param string $logMainDirectory The main log directory for Monolog: on subdirectory by command - * - * @return $this - */ - public function setLogMainDirectory (string $logMainDirectory): self { - $this->logMainDirectory = $logMainDirectory; - return $this; - } } \ No newline at end of file diff --git a/src/CliProgram/ApplicationWithCommandOutputInterface.php b/src/CliProgram/ApplicationWithCommandOutputInterface.php index d0338a1..a7809f9 100644 --- a/src/CliProgram/ApplicationWithCommandOutputInterface.php +++ b/src/CliProgram/ApplicationWithCommandOutputInterface.php @@ -2,38 +2,13 @@ namespace jrosset\CliProgram; +use jrosset\CliProgram\Output\TCommandOutputApplication; use Symfony\Component\Console\Application; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Throwable; /** * An application with a by-command {@see OutputInterface} */ class ApplicationWithCommandOutputInterface extends Application { - /** - * Get the {@see OutputInterface} for a command - * - * @param Command $command The command - * @param InputInterface $input The input - * @param OutputInterface $output The existing output - * - * @return OutputInterface The output for the command - * - * @throws Throwable On error - * - * @noinspection PhpUnusedParameterInspection - */ - protected function getOutputInterfaceForCommand (Command $command, InputInterface $input, OutputInterface $output): OutputInterface { - return $output; - } - - /** - * @inheritDoc - * @throws Throwable - */ - protected function doRunCommand (Command $command, InputInterface $input, OutputInterface $output): int { - return parent::doRunCommand($command, $input, $this->getOutputInterfaceForCommand($command, $input, $output)); - } + use TCommandOutputApplication; } \ No newline at end of file diff --git a/src/CliProgram/Monolog/ConsoleOutputWithMonolog.php b/src/CliProgram/Monolog/ConsoleOutputWithMonolog.php index 615a76e..c34140c 100644 --- a/src/CliProgram/Monolog/ConsoleOutputWithMonolog.php +++ b/src/CliProgram/Monolog/ConsoleOutputWithMonolog.php @@ -2,29 +2,38 @@ namespace jrosset\CliProgram\Monolog; +use jrosset\CliProgram\Output\OutputWithLogger; +use Monolog\Logger; use Psr\Log\LoggerInterface; -use Symfony\Component\Console\Formatter\OutputFormatterInterface; -use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\OutputInterface; /** - * A {@see ConsoleOutput} with a {@see LoggerInterface Monolog Logger} + * A generic output interface with a {@see LoggerInterface Monolog Logger} + * + * @implements OutputWithLogger */ -class ConsoleOutputWithMonolog extends ConsoleOutput { - use TOutputInterfaceWithMonolog; - - /** - * Option pour ne pas écrire un message dans Monolog - */ - public const OPTION_SKIP_MONOLOG = 4096; - +class ConsoleOutputWithMonolog extends OutputWithLogger { /** - * @param LoggerInterface $logger The Monolog Logger - * @param int $verbosity The verbosity level (one of the VERBOSITY constants in OutputInterface) - * @param bool|null $decorated Whether to decorate messages (null for auto-guessing) - * @param OutputFormatterInterface|null $formatter Output formatter instance (null to use default OutputFormatter) + * @inheritDoc */ - public function __construct (LoggerInterface $logger, int $verbosity = self::VERBOSITY_NORMAL, bool $decorated = null, OutputFormatterInterface $formatter = null) { - parent::__construct($verbosity, $decorated, $formatter); - $this->setLogger($logger); + protected function getLoggerLevelFromVerbosity (int $verbosity): int { + if ($verbosity <= OutputInterface::VERBOSITY_QUIET) { + return Logger::ERROR; + } + elseif ($verbosity <= OutputInterface::VERBOSITY_NORMAL) { + return Logger::NOTICE; + } + elseif ($verbosity <= OutputInterface::VERBOSITY_VERBOSE) { + return Logger::INFO; + } + elseif ($verbosity <= OutputInterface::VERBOSITY_VERY_VERBOSE) { + return Logger::INFO; + } + elseif ($verbosity <= OutputInterface::VERBOSITY_DEBUG) { + return Logger::DEBUG; + } + else { + return Logger::NOTICE; + } } } \ No newline at end of file diff --git a/src/CliProgram/Monolog/TMonologApplication.php b/src/CliProgram/Monolog/TMonologApplication.php new file mode 100644 index 0000000..c09eb78 --- /dev/null +++ b/src/CliProgram/Monolog/TMonologApplication.php @@ -0,0 +1,57 @@ +getName(), + [ + new LogDirectoryHandler( + $this->getLogMainDirectory() . DIRECTORY_SEPARATOR + . str_replace(':', DIRECTORY_SEPARATOR, $command->getName()) + ), + ] + ) + ); + } + + /** + * The main log directory for Monolog: on subdirectory by command + * + * @return string The main log directory for Monolog: on subdirectory by command + */ + public function getLogMainDirectory (): string { + return $this->logMainDirectory; + } + /** + * Set the main log directory for Monolog: on subdirectory by command + * + * @param string $logMainDirectory The main log directory for Monolog: on subdirectory by command + * + * @return $this + */ + public function setLogMainDirectory (string $logMainDirectory): self { + $this->logMainDirectory = $logMainDirectory; + return $this; + } +} \ No newline at end of file diff --git a/src/CliProgram/Monolog/TOutputInterfaceWithMonolog.php b/src/CliProgram/Monolog/TOutputInterfaceWithMonolog.php deleted file mode 100644 index 76fc7e4..0000000 --- a/src/CliProgram/Monolog/TOutputInterfaceWithMonolog.php +++ /dev/null @@ -1,76 +0,0 @@ -logger; - } - /** - * Set the logger - * - * @param LoggerInterface|null $logger The logger - * - * @return $this - */ - public function setLogger (?LoggerInterface $logger): self { - $this->logger = $logger; - return $this; - } - - /** - * @inheritDoc - */ - public function write($messages, bool $newline = false, int $options = 0): void { - if (!is_iterable($messages)) { - $messages = [$messages]; - } - - if ($this->logger !== null && (($options & ConsoleOutputWithMonolog::OPTION_SKIP_MONOLOG) !== ConsoleOutputWithMonolog::OPTION_SKIP_MONOLOG)) { - $verbosities = OutputInterface::VERBOSITY_QUIET | OutputInterface::VERBOSITY_NORMAL | OutputInterface::VERBOSITY_VERBOSE | OutputInterface::VERBOSITY_VERY_VERBOSE | OutputInterface::VERBOSITY_DEBUG; - $verbosity = $verbosities & $options ?: OutputInterface::VERBOSITY_NORMAL; - - if ($verbosity <= OutputInterface::VERBOSITY_QUIET) { - $loggerLevel = Logger::ERROR; - } - elseif ($verbosity <= OutputInterface::VERBOSITY_NORMAL) { - $loggerLevel = Logger::NOTICE; - } - elseif ($verbosity <= OutputInterface::VERBOSITY_VERBOSE) { - $loggerLevel = Logger::INFO; - } - elseif ($verbosity <= OutputInterface::VERBOSITY_VERY_VERBOSE) { - $loggerLevel = Logger::INFO; - } - elseif ($verbosity <= OutputInterface::VERBOSITY_DEBUG) { - $loggerLevel = Logger::DEBUG; - } - else { - $loggerLevel = Logger::NOTICE; - } - - foreach ($messages as $message) { - $this->logger->log($loggerLevel, strip_tags($message)); - } - } - - parent::write($messages, $newline, $options); - } -} \ No newline at end of file diff --git a/src/CliProgram/Output/OutputWithLogger.php b/src/CliProgram/Output/OutputWithLogger.php new file mode 100644 index 0000000..4dc163b --- /dev/null +++ b/src/CliProgram/Output/OutputWithLogger.php @@ -0,0 +1,130 @@ +setLogger($logger); + } + + /** + * The logger + * + * @return TLogger|null The logger + */ + public function getLogger () { + return $this->logger; + } + /** + * Set the logger + * + * @param TLogger|null $logger The logger + * + * @return $this + * + * @throws InvalidArgumentException If the logger is not null or an instance for {@see LoggerInterface} + * + * @noinspection PhpMissingParamTypeInspection + */ + public function setLogger ($logger): self { + if ($logger !== null && !$logger instanceof LoggerInterface) { + throw new InvalidArgumentException('The logger must be null or a ' . LoggerInterface::class . ' instance'); + } + + $this->logger = $logger; + return $this; + } + + /** + * Write into the logger + * + * @param iterable|string $messages The messages to write + * @param bool $newline Newline at the end ? + * @param int $options A bitmask of options + * + * @return void + * + * @noinspection PhpUnusedParameterInspection + */ + protected function writeToLogger ($messages, bool $newline = false, int $options = 0): void { + if ($this->logger === null || ($options & static::OPTION_SKIP_LOGGER) === static::OPTION_SKIP_LOGGER) { + return; + } + + //region Calcul log level + $verbosities = OutputInterface::VERBOSITY_QUIET + | OutputInterface::VERBOSITY_NORMAL + | OutputInterface::VERBOSITY_VERBOSE + | OutputInterface::VERBOSITY_VERY_VERBOSE + | OutputInterface::VERBOSITY_DEBUG; + $loggerLevel = $this->getLoggerLevelFromVerbosity( + $verbosities & $options ? : OutputInterface::VERBOSITY_NORMAL + ); + //endregion + + if (!is_iterable($messages)) { + $messages = [$messages]; + } + foreach ($messages as $message) { + $this->getLogger()->log($loggerLevel, strip_tags($message)); + } + } + /** + * OGet the logger level from verbosity + * + * @param int $verbosity The verbosity + * + * @return mixed The logger level + * @noinspection PhpReturnDocTypeMismatchInspection + */ + protected function getLoggerLevelFromVerbosity (int $verbosity) { + return $verbosity; + } + + /** + * @inheritDoc + */ + public function write ($messages, bool $newline = false, int $options = 0) { + $this->writeToLogger($messages, $newline, $options); + return parent::write($messages, $newline, $options); + } + /** + * @inheritDoc + */ + public function writeLn ($messages, int $options = 0) { + $this->writeToLogger($messages, true, $options); + return parent::writeLn($messages, $options); + } +} \ No newline at end of file diff --git a/src/CliProgram/Output/OutputWrapper.php b/src/CliProgram/Output/OutputWrapper.php new file mode 100644 index 0000000..d183e66 --- /dev/null +++ b/src/CliProgram/Output/OutputWrapper.php @@ -0,0 +1,112 @@ +output = $output; + } + + /** + * The real output interface + * + * @return OutputInterface The real output interface + */ + public function getOutput (): OutputInterface { + return $this->output; + } + + /** + * @inheritDoc + */ + public function write ($messages, bool $newline = false, int $options = 0) { + return $this->getOutput()->write($messages, $newline, $options); + } + /** + * @inheritDoc + */ + public function writeln ($messages, int $options = 0) { + return $this->getOutput()->writeln($messages, $options); + } + + /** + * @inheritDoc + */ + public function setVerbosity (int $level) { + return $this->getOutput()->setVerbosity($level); + } + /** + * @inheritDoc + */ + public function getVerbosity (): int { + return $this->getOutput()->getVerbosity(); + } + + /** + * @inheritDoc + */ + public function isQuiet (): bool { + return $this->getOutput()->isQuiet(); + } + /** + * @inheritDoc + */ + public function isVerbose (): bool { + return $this->getOutput()->isVerbose(); + } + /** + * @inheritDoc + */ + public function isVeryVerbose (): bool { + return $this->getOutput()->isVeryVerbose(); + } + /** + * @inheritDoc + */ + public function isDebug (): bool { + return $this->getOutput()->isDebug(); + } + + /** + * @inheritDoc + */ + public function setDecorated (bool $decorated) { + return $this->getOutput()->setDecorated($decorated); + } + /** + * @inheritDoc + */ + public function isDecorated (): bool { + return $this->getOutput()->isDecorated(); + } + + /** + * @inheritDoc + * @noinspection PhpInappropriateInheritDocUsageInspection + */ + public function setFormatter (OutputFormatterInterface $formatter) { + return $this->getOutput()->setFormatter($formatter); + } + /** + * @inheritDoc + */ + public function getFormatter (): OutputFormatterInterface { + return $this->getOutput()->getFormatter(); + } +} \ No newline at end of file diff --git a/src/CliProgram/Output/TCommandOutputApplication.php b/src/CliProgram/Output/TCommandOutputApplication.php new file mode 100644 index 0000000..134ad42 --- /dev/null +++ b/src/CliProgram/Output/TCommandOutputApplication.php @@ -0,0 +1,38 @@ +getOutputInterfaceForCommand($command, $input, $output)); + } +} \ No newline at end of file diff --git a/tests/Commands/Hello.php b/tests/Commands/Hello.php index 1412e16..d31ce21 100644 --- a/tests/Commands/Hello.php +++ b/tests/Commands/Hello.php @@ -4,7 +4,7 @@ namespace jrosset\Tests\Commands; use DateTimeImmutable; use DateTimeInterface; -use jrosset\CliProgram\Monolog\ConsoleOutputWithMonolog; +use jrosset\CliProgram\Output\OutputWithLogger; use jrosset\CliProgram\Validation\CommandWithValidation; use jrosset\CliProgram\Validation\Validators\DateValidator; use jrosset\CliProgram\Validation\Validators\IntegerValidator; @@ -69,7 +69,7 @@ class Hello extends CommandWithValidation { for ($curr = 0; $curr < $repeat; $curr++) { $output->writeln($text); } - $output->writeln('FIN', ConsoleOutputWithMonolog::OPTION_SKIP_MONOLOG); + $output->writeln('FIN', OutputWithLogger::OPTION_SKIP_LOGGER); return Command::SUCCESS; } } \ No newline at end of file