diff --git a/CHANGELOG.md b/CHANGELOG.md index c5cebbf3..e79b3c2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 3.6.0 (xxxx-xx-xx) -* Add support for Symfony Mailer +* Added support for Symfony Mailer +* Added support for setting log levels from parameters or environment variables ## 3.5.0 (2019-11-13) diff --git a/DependencyInjection/MonologExtension.php b/DependencyInjection/MonologExtension.php index 5a7177ec..3e24d1fd 100644 --- a/DependencyInjection/MonologExtension.php +++ b/DependencyInjection/MonologExtension.php @@ -23,6 +23,7 @@ use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\DependencyInjection\Extension; @@ -41,9 +42,31 @@ class MonologExtension extends Extension private $swiftMailerHandlers = []; - private function levelToMonologConst($level) + private function levelToMonologConst($level, ContainerBuilder $container) { - return is_int($level) ? $level : constant('Monolog\Logger::'.strtoupper($level)); + if (null === $level || is_numeric($level)) { + return $level; + } + + if (defined('Monolog\Logger::'.strtoupper($level))) { + return constant('Monolog\Logger::' . strtoupper($level)); + } + + if ($container->hasParameter($level)) { + return $this->levelToMonologConst($container->getParameter($level), $container); + } + + try { + $logLevel = $container->resolveEnvPlaceholders($level, true); + } catch (ParameterNotFoundException $notFoundException) { + throw new \InvalidArgumentException(sprintf('Could not match "%s" to a log level.', $level)); + } + + if ($logLevel !== '') { + return $this->levelToMonologConst($logLevel, $container); + } + + throw new \InvalidArgumentException(sprintf('Could not match "%s" to a log level.', $level)); } /** @@ -169,7 +192,7 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler $handlerClass = $this->getHandlerClassByType($handler['type']); $definition = new Definition($handlerClass); - $handler['level'] = $this->levelToMonologConst($handler['level']); + $handler['level'] = $this->levelToMonologConst($handler['level'], $container); if ($handler['include_stacktraces']) { $definition->setConfigurator(['Symfony\\Bundle\\MonologBundle\\MonologBundle', 'includeStacktraces']); @@ -383,9 +406,9 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler break; case 'fingers_crossed': - $handler['action_level'] = $this->levelToMonologConst($handler['action_level']); + $handler['action_level'] = $this->levelToMonologConst($handler['action_level'], $container); if (null !== $handler['passthru_level']) { - $handler['passthru_level'] = $this->levelToMonologConst($handler['passthru_level']); + $handler['passthru_level'] = $this->levelToMonologConst($handler['passthru_level'], $container); } $nestedHandlerId = $this->getHandlerId($handler['handler']); $this->markNestedHandler($nestedHandlerId); @@ -429,10 +452,10 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler break; case 'filter': - $handler['min_level'] = $this->levelToMonologConst($handler['min_level']); - $handler['max_level'] = $this->levelToMonologConst($handler['max_level']); + $handler['min_level'] = $this->levelToMonologConst($handler['min_level'], $container); + $handler['max_level'] = $this->levelToMonologConst($handler['max_level'], $container); foreach (array_keys($handler['accepted_levels']) as $k) { - $handler['accepted_levels'][$k] = $this->levelToMonologConst($handler['accepted_levels'][$k]); + $handler['accepted_levels'][$k] = $this->levelToMonologConst($handler['accepted_levels'][$k], $container); } $nestedHandlerId = $this->getHandlerId($handler['handler']); diff --git a/Tests/DependencyInjection/MonologExtensionTest.php b/Tests/DependencyInjection/MonologExtensionTest.php index 732d4cd6..d3742027 100644 --- a/Tests/DependencyInjection/MonologExtensionTest.php +++ b/Tests/DependencyInjection/MonologExtensionTest.php @@ -18,6 +18,7 @@ use Symfony\Bundle\MonologBundle\DependencyInjection\Compiler\LoggerChannelPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\HttpFoundation\RequestStack; @@ -611,10 +612,94 @@ public function v1AddedDataProvider() ]; } + public function testLogLevelfromInvalidparameterThrowsException() + { + $container = new ContainerBuilder(); + $loader = new MonologExtension(); + $config = [['handlers' => ['main' => ['type' => 'stream', 'level' => '%some_param%']]]]; - protected function getContainer(array $config = [], array $thirdPartyDefinitions = []) + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Could not match "%some_param%" to a log level.'); + + $loader->load($config, $container); + } + + /** + * @dataProvider provideLoglevelParameterConfig + */ + public function testLogLevelfromParameter(array $parameters, array $config, $expectedClass, array $expectedArgs) { $container = new ContainerBuilder(); + foreach ($parameters as $name => $value) { + $container->setParameter($name, $value); + } + $loader = new MonologExtension(); + $config = [['handlers' => ['main' => $config]]]; + $loader->load($config, $container); + + $definition = $container->getDefinition('monolog.handler.main'); + $this->assertDICDefinitionClass($definition, $expectedClass); + $this->assertDICConstructorArguments($definition, $expectedArgs); + } + + public function provideLoglevelParameterConfig() + { + return [ + 'browser console with parameter level' => [ + ['%log_level%' => 'info'], + ['type' => 'browser_console', 'level' => '%log_level%'], + 'Monolog\Handler\BrowserConsoleHandler', + [200, true] + ], + 'browser console with envvar level' => [ + ['%env(LOG_LEVEL)%' => 'info'], + ['type' => 'browser_console', 'level' => '%env(LOG_LEVEL)%'], + 'Monolog\Handler\BrowserConsoleHandler', + [200, true] + ], + 'stream with envvar level null or "~" (in yaml config)' => [ + ['%env(LOG_LEVEL)%' => null], + ['type' => 'stream', 'level' => '%env(LOG_LEVEL)%'], + 'Monolog\Handler\StreamHandler', + [ + '%kernel.logs_dir%/%kernel.environment%.log', + null, + true, + null, + false, + ] + ], + 'stream with envvar level' => [ + ['%env(LOG_LEVEL)%' => '400'], + ['type' => 'stream', 'level' => '%env(LOG_LEVEL)%'], + 'Monolog\Handler\StreamHandler', + [ + '%kernel.logs_dir%/%kernel.environment%.log', + 400, + true, + null, + false, + ] + ], + 'stream with envvar and fallback parameter' => [ + ['%env(LOG_LEVEL)%' => '500', '%log_level%' => '%env(LOG_LEVEL)%'], + ['type' => 'stream', 'level' => '%log_level%'], + 'Monolog\Handler\StreamHandler', + [ + '%kernel.logs_dir%/%kernel.environment%.log', + 500, + true, + null, + false, + ] + ], + ]; + } + + + protected function getContainer(array $config = [], array $thirdPartyDefinitions = []) + { + $container = new ContainerBuilder(new EnvPlaceholderParameterBag()); foreach ($thirdPartyDefinitions as $id => $definition) { $container->setDefinition($id, $definition); }