diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 255eba47..cad71230 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -386,6 +386,8 @@ public function getConfigTreeBuilder(): TreeBuilder $treeBuilder = new TreeBuilder('monolog'); $rootNode = $treeBuilder->getRootNode(); + $this->addChannelsSection($rootNode, 'handler_default_channels'); + $handlers = $rootNode ->fixXmlConfig('channel') ->fixXmlConfig('handler') @@ -625,6 +627,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->scalarNode('formatter')->end() ->booleanNode('nested')->defaultFalse()->end() + ->booleanNode('use_default_channels')->defaultTrue()->end() ->end(); $this->addGelfSection($handlerNode); @@ -801,9 +804,9 @@ public function getConfigTreeBuilder(): TreeBuilder return $treeBuilder; } - private function addGelfSection(ArrayNodeDefinition $handerNode) + private function addGelfSection(ArrayNodeDefinition $handlerNode) { - $handerNode + $handlerNode ->children() ->arrayNode('publisher') ->canBeUnset() @@ -832,9 +835,9 @@ private function addGelfSection(ArrayNodeDefinition $handerNode) ; } - private function addMongoSection(ArrayNodeDefinition $handerNode) + private function addMongoSection(ArrayNodeDefinition $handlerNode) { - $handerNode + $handlerNode ->children() ->arrayNode('mongo') ->canBeUnset() @@ -872,9 +875,9 @@ private function addMongoSection(ArrayNodeDefinition $handerNode) ; } - private function addElasticsearchSection(ArrayNodeDefinition $handerNode) + private function addElasticsearchSection(ArrayNodeDefinition $handlerNode) { - $handerNode + $handlerNode ->children() ->arrayNode('elasticsearch') ->canBeUnset() @@ -904,9 +907,9 @@ private function addElasticsearchSection(ArrayNodeDefinition $handerNode) ; } - private function addRedisSection(ArrayNodeDefinition $handerNode) + private function addRedisSection(ArrayNodeDefinition $handlerNode) { - $handerNode + $handlerNode ->children() ->arrayNode('redis') ->canBeUnset() @@ -937,9 +940,9 @@ private function addRedisSection(ArrayNodeDefinition $handerNode) ; } - private function addPredisSection(ArrayNodeDefinition $handerNode) + private function addPredisSection(ArrayNodeDefinition $handlerNode) { - $handerNode + $handlerNode ->children() ->arrayNode('predis') ->canBeUnset() @@ -966,9 +969,9 @@ private function addPredisSection(ArrayNodeDefinition $handerNode) ; } - private function addMailerSection(ArrayNodeDefinition $handerNode) + private function addMailerSection(ArrayNodeDefinition $handlerNode) { - $handerNode + $handlerNode ->children() ->scalarNode('from_email')->end() // swift_mailer, native_mailer, symfony_mailer and flowdock ->arrayNode('to_email') // swift_mailer, native_mailer and symfony_mailer @@ -1013,9 +1016,9 @@ private function addMailerSection(ArrayNodeDefinition $handerNode) ; } - private function addVerbosityLevelSection(ArrayNodeDefinition $handerNode) + private function addVerbosityLevelSection(ArrayNodeDefinition $handlerNode) { - $handerNode + $handlerNode ->children() ->arrayNode('verbosity_levels') // console ->beforeNormalization() @@ -1073,11 +1076,11 @@ private function addVerbosityLevelSection(ArrayNodeDefinition $handerNode) ; } - private function addChannelsSection(ArrayNodeDefinition $handerNode) + private function addChannelsSection(ArrayNodeDefinition $handlerNode, string $nodeName = 'channels') { - $handerNode + $handlerNode ->children() - ->arrayNode('channels') + ->arrayNode($nodeName) ->fixXmlConfig('channel', 'elements') ->canBeUnset() ->beforeNormalization() @@ -1093,7 +1096,7 @@ private function addChannelsSection(ArrayNodeDefinition $handerNode) ->thenUnset() ->end() ->validate() - ->always(function ($v) { + ->always(function ($v) use ($nodeName) { $isExclusive = null; if (isset($v['type'])) { $isExclusive = 'exclusive' === $v['type']; @@ -1103,13 +1106,13 @@ private function addChannelsSection(ArrayNodeDefinition $handerNode) foreach ($v['elements'] as $element) { if (0 === strpos($element, '!')) { if (false === $isExclusive) { - throw new InvalidConfigurationException('Cannot combine exclusive/inclusive definitions in channels list.'); + throw new InvalidConfigurationException(\sprintf('Cannot combine exclusive/inclusive definitions in %s list.', $nodeName)); } $elements[] = substr($element, 1); $isExclusive = true; } else { if (true === $isExclusive) { - throw new InvalidConfigurationException('Cannot combine exclusive/inclusive definitions in channels list'); + throw new InvalidConfigurationException(\sprintf('Cannot combine exclusive/inclusive definitions in %s list', $nodeName)); } $elements[] = $element; $isExclusive = false; @@ -1128,7 +1131,7 @@ private function addChannelsSection(ArrayNodeDefinition $handerNode) ->scalarNode('type') ->validate() ->ifNotInArray(['inclusive', 'exclusive']) - ->thenInvalid('The type of channels has to be inclusive or exclusive') + ->thenInvalid(\sprintf('The type of %s has to be inclusive or exclusive', $nodeName)) ->end() ->end() ->arrayNode('elements') diff --git a/DependencyInjection/MonologExtension.php b/DependencyInjection/MonologExtension.php index 1d47a984..df12b645 100644 --- a/DependencyInjection/MonologExtension.php +++ b/DependencyInjection/MonologExtension.php @@ -76,6 +76,7 @@ public function load(array $configs, ContainerBuilder $container) $handlers[$handler['priority']][] = [ 'id' => $this->buildHandler($container, $name, $handler), 'channels' => empty($handler['channels']) ? null : $handler['channels'], + 'use_default_channels' => $handler['use_default_channels'], ]; } @@ -92,10 +93,19 @@ public function load(array $configs, ContainerBuilder $container) } } + $defaultChannels = $config['handler_default_channels'] ?? null; $handlersToChannels = []; foreach ($sortedHandlers as $handler) { if (!\in_array($handler['id'], $this->nestedHandlers)) { - $handlersToChannels[$handler['id']] = $handler['channels']; + $channels = $handler['channels']; + if (null !== $defaultChannels && $handler['use_default_channels']) { + if (null === $channels) { + $channels = $defaultChannels; + } elseif ($channels['type'] === $defaultChannels['type']) { + $channels['elements'] = array_unique(array_merge($channels['elements'], $defaultChannels['elements'])); + } + } + $handlersToChannels[$handler['id']] = $channels; } } $container->setParameter('monolog.handlers_to_channels', $handlersToChannels); diff --git a/Resources/config/schema/monolog-1.0.xsd b/Resources/config/schema/monolog-1.0.xsd index b00e969e..898dbdf0 100644 --- a/Resources/config/schema/monolog-1.0.xsd +++ b/Resources/config/schema/monolog-1.0.xsd @@ -11,6 +11,7 @@ + @@ -92,6 +93,7 @@ + diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 516565e0..cb0ed3eb 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -323,6 +323,31 @@ public function testWithType() $this->assertEquals('B', $config['handlers']['foo']['channels']['elements'][1]); } + public function testWithUseDefaultChannels() + { + $configs = [ + [ + 'handlers' => [ + 'foo' => [ + 'type' => 'stream', + 'path' => '/foo', + 'use_default_channels' => true, + ], + 'bar' => [ + 'type' => 'stream', + 'path' => '/bar', + 'use_default_channels' => false, + ], + ], + ], + ]; + + $config = $this->process($configs); + + $this->assertTrue($config['handlers']['foo']['use_default_channels']); + $this->assertFalse($config['handlers']['bar']['use_default_channels']); + } + public function testWithFilePermission() { $configs = [ @@ -548,6 +573,28 @@ public function processPsr3MessagesProvider(): iterable ]; } + /** + * @dataProvider provideHandlerDefaultChannels + */ + public function testHandlerDefaultChannels($configuration, ?array $processedConfiguration) + { + $config = $this->process([['handler_default_channels' => $configuration]]); + + $this->assertEquals($processedConfiguration, $config['handler_default_channels']); + } + + public static function provideHandlerDefaultChannels(): iterable + { + yield 'None' => [null, null]; + yield 'Empty array' => [[], null]; + yield 'As string' => ['!foo', ['type' => 'exclusive', 'elements' => ['foo']]]; + yield 'As array' => [['foo', 'bar'], ['type' => 'inclusive', 'elements' => ['foo', 'bar']]]; + yield 'With elements key' => [['elements' => ['!foo', '!bar']], ['type' => 'exclusive', 'elements' => ['foo', 'bar']]]; + yield 'With type key' => [['type' => 'exclusive', 'elements' => ['!foo']], ['type' => 'exclusive', 'elements' => ['foo']]]; + yield 'XML' => [['channel' => ['foo', 'bar']], ['type' => 'inclusive', 'elements' => ['foo', 'bar']]]; + yield 'XML with type' => [['type' => 'exclusive', 'channel' => ['!foo']], ['type' => 'exclusive', 'elements' => ['foo']]]; + } + /** * Processes an array of configurations and returns a compiled version. * diff --git a/Tests/DependencyInjection/FixtureMonologExtensionTest.php b/Tests/DependencyInjection/FixtureMonologExtensionTest.php index dd0046f5..ced7f711 100644 --- a/Tests/DependencyInjection/FixtureMonologExtensionTest.php +++ b/Tests/DependencyInjection/FixtureMonologExtensionTest.php @@ -145,6 +145,61 @@ public function testHandlersWithChannels() ); } + public function testHandlersWithDefaultChannels() + { + $container = $this->getContainer('handlers_with_default_channels'); + + $this->assertFalse($container->hasParameter('monolog.handler_default_channels')); + + $this->assertEquals( + [ + 'monolog.handler.one' => ['type' => 'inclusive', 'elements' => ['foo']], + 'monolog.handler.two' => ['type' => 'exclusive', 'elements' => ['bar', 'baz']], + 'monolog.handler.three' => ['type' => 'exclusive', 'elements' => ['bar', 'baz', 'foo']], + 'monolog.handler.four' => ['type' => 'exclusive', 'elements' => ['foo', 'bar']], + 'monolog.handler.five' => null, + 'monolog.handler.six' => ['type' => 'exclusive', 'elements' => ['foo', 'bar']], + ], + $container->getParameter('monolog.handlers_to_channels') + ); + + $this->assertTrue($container->hasDefinition('monolog.logger')); + $this->assertTrue($container->hasDefinition('monolog.logger.foo')); + $this->assertTrue($container->hasDefinition('monolog.logger.bar')); + $this->assertTrue($container->hasDefinition('monolog.logger.baz')); + $this->assertTrue($container->hasDefinition('monolog.handler.one')); + $this->assertTrue($container->hasDefinition('monolog.handler.two')); + $this->assertTrue($container->hasDefinition('monolog.handler.three')); + $this->assertTrue($container->hasDefinition('monolog.handler.four')); + $this->assertTrue($container->hasDefinition('monolog.handler.five')); + $this->assertTrue($container->hasDefinition('monolog.handler.six')); + + $logger = $container->getDefinition('monolog.logger'); + $this->assertCount(6, $logger->getMethodCalls()); + $this->assertDICDefinitionMethodCallAt(5, $logger, 'pushHandler', [new Reference('monolog.handler.two')]); + $this->assertDICDefinitionMethodCallAt(4, $logger, 'pushHandler', [new Reference('monolog.handler.three')]); + $this->assertDICDefinitionMethodCallAt(3, $logger, 'pushHandler', [new Reference('monolog.handler.four')]); + $this->assertDICDefinitionMethodCallAt(2, $logger, 'pushHandler', [new Reference('monolog.handler.five')]); + $this->assertDICDefinitionMethodCallAt(1, $logger, 'pushHandler', [new Reference('monolog.handler.six')]); + $this->assertDICDefinitionMethodCallAt(0, $logger, 'useMicrosecondTimestamps', ['%monolog.use_microseconds%']); + + $logger = $container->getDefinition('monolog.logger.foo'); + $this->assertCount(3, $logger->getMethodCalls()); + $this->assertDICDefinitionMethodCallAt(2, $logger, 'pushHandler', [new Reference('monolog.handler.one')]); + $this->assertDICDefinitionMethodCallAt(1, $logger, 'pushHandler', [new Reference('monolog.handler.two')]); + $this->assertDICDefinitionMethodCallAt(0, $logger, 'pushHandler', [new Reference('monolog.handler.five')]); + + $logger = $container->getDefinition('monolog.logger.bar'); + $this->assertCount(1, $logger->getMethodCalls()); + $this->assertDICDefinitionMethodCallAt(0, $logger, 'pushHandler', [new Reference('monolog.handler.five')]); + + $logger = $container->getDefinition('monolog.logger.baz'); + $this->assertCount(3, $logger->getMethodCalls()); + $this->assertDICDefinitionMethodCallAt(2, $logger, 'pushHandler', [new Reference('monolog.handler.four')]); + $this->assertDICDefinitionMethodCallAt(1, $logger, 'pushHandler', [new Reference('monolog.handler.five')]); + $this->assertDICDefinitionMethodCallAt(0, $logger, 'pushHandler', [new Reference('monolog.handler.six')]); + } + /** @group legacy */ public function testSingleEmailRecipient() { diff --git a/Tests/DependencyInjection/Fixtures/xml/handlers_with_default_channels.xml b/Tests/DependencyInjection/Fixtures/xml/handlers_with_default_channels.xml new file mode 100644 index 00000000..eebd5e37 --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/xml/handlers_with_default_channels.xml @@ -0,0 +1,40 @@ + + + + + + foo + bar + baz + + + !foo + !bar + + + + + foo + + + + + !bar + !baz + + + + + !bar + !baz + + + + + + + diff --git a/Tests/DependencyInjection/Fixtures/yml/handlers_with_default_channels.yml b/Tests/DependencyInjection/Fixtures/yml/handlers_with_default_channels.yml new file mode 100644 index 00000000..ff9a113e --- /dev/null +++ b/Tests/DependencyInjection/Fixtures/yml/handlers_with_default_channels.yml @@ -0,0 +1,24 @@ +monolog: + channels: [ 'foo', 'bar', 'baz' ] + handler_default_channels: [ '!foo', '!bar' ] + handlers: + one: + type: stream + use_default_channels: true + channels: foo + two: + type: stream + use_default_channels: false + channels: [ '!bar', '!baz' ] + three: + type: stream + use_default_channels: true + channels: [ '!bar', '!baz' ] + four: + type: stream + use_default_channels: true + five: + type: stream + use_default_channels: false + six: + type: stream