diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 28302c82..6267c1c9 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -29,6 +29,7 @@ 'modernize_strpos' => false, 'trailing_comma_in_multiline' => ['elements' => ['arrays', 'match', 'parameters']], ]) + ->setUnsupportedPhpVersionAllowed(true) ->setRiskyAllowed(true) ->setFinder( PhpCsFixer\Finder::create() diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 36e8d007..f96e0920 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -13,6 +13,7 @@ use Composer\InstalledVersions; use Monolog\Logger; +use Symfony\Bundle\MonologBundle\DependencyInjection\Handler\HandlerExtensionInterface; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -402,6 +403,14 @@ */ class Configuration implements ConfigurationInterface { + /** + * @param list $handlerExtensions + */ + public function __construct( + private array $handlerExtensions = [], + ) { + } + /** * Generates the configuration tree builder. */ @@ -665,7 +674,7 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addGelfSection($handlerNode); $this->addMongoSection($handlerNode); - $this->addMongoDBSection($handlerNode); + // $this->addMongoDBSection($handlerNode); $this->addElasticsearchSection($handlerNode); $this->addRedisSection($handlerNode); $this->addPredisSection($handlerNode); @@ -673,13 +682,45 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addVerbosityLevelSection($handlerNode); $this->addChannelsSection($handlerNode); + $types = []; + \assert($handlerNode instanceof ArrayNodeDefinition); + foreach ($this->handlerExtensions as $handlerExtension) { + $types[$handlerExtension->getName()] = []; + $arrayNode = $handlerNode->children()->arrayNode($handlerExtension->getName()); + $handlerExtension->buildArrayNode($arrayNode); + foreach ($arrayNode->getChildNodeDefinitions() as $name => $childNodeDefinition) { + $types[$handlerExtension->getName()][] = $name; + } + } + $handlerNode ->beforeNormalization() - ->always(static function ($v) { + ->always(static function ($v) use ($types) { if (empty($v['console_formatter_options']) && !empty($v['console_formater_options'])) { $v['console_formatter_options'] = $v['console_formater_options']; } + if (!\array_key_exists('type', $v)) { + $handlerFields = array_intersect_key($types, $v); + if (0 === \count($handlerFields)) { + throw new InvalidConfigurationException('The handler type could not be determined automatically. Please specify the "type" option.'); + } + + if (1 < \count($handlerFields)) { + throw new InvalidConfigurationException('The handler type could not be determined automatically as multiple handler types are configured: '.implode(', ', array_keys($handlerFields)).'. Please specify the "type" option.'); + } + + $v['type'] = key($handlerFields); + } elseif (isset($types[$type = $v['type'] ?? 'null']) && !isset($v[$type])) { + // migrate old config where type was also a key + foreach ($types[$type] as $field) { + if (isset($v[$field])) { + $v[$type][$field] = $v[$field]; + unset($v[$field]); + } + } + } + return $v; }) ->end() diff --git a/src/DependencyInjection/Handler/DefaultHandlerExtension.php b/src/DependencyInjection/Handler/DefaultHandlerExtension.php new file mode 100644 index 00000000..079abd3f --- /dev/null +++ b/src/DependencyInjection/Handler/DefaultHandlerExtension.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle\DependencyInjection\Handler; + +use Monolog\Handler\AbstractHandler; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\Definition; + +/** + * Handlers using the constructor of {@see AbstractHandler} without adding their own arguments. + */ +final class DefaultHandlerExtension implements HandlerExtensionInterface +{ + /** + * @param non-empty-string $name + * @param class-string $handlerClass + */ + public function __construct( + private string $name, + private string $handlerClass, + ) { + } + + public function getName(): string + { + return $this->name; + } + + public function buildArrayNode(ArrayNodeDefinition $handlerNode): void + { + // No specific configuration + } + + public function getDefinition(array $config, array $handler): Definition + { + $definition = new Definition($this->handlerClass); + $definition->setArguments([ + $config['level'], + $config['bubble'], + ]); + + return $definition; + } +} diff --git a/src/DependencyInjection/Handler/HandlerExtensionInterface.php b/src/DependencyInjection/Handler/HandlerExtensionInterface.php new file mode 100644 index 00000000..02795b6b --- /dev/null +++ b/src/DependencyInjection/Handler/HandlerExtensionInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle\DependencyInjection\Handler; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\Definition; + +interface HandlerExtensionInterface +{ + /** + * Returns the name of the handler and configuration subtree. + */ + public function getName(): string; + + /** + * Add properties for the handler type. + */ + public function buildArrayNode(ArrayNodeDefinition $handlerNode): void; + + /** + * @param array $config The full normalized handler config + * @param array $handler The handler-type-specific subtree (e.g. $config['stream']) + * bubble: bool, + * formatter: string|null, + * } $config Generic options + * @param array{} $handler Specific handler options + */ + public function getDefinition(array $config, array $handler): Definition; +} diff --git a/src/DependencyInjection/Handler/MongoDBHandlerExtension.php b/src/DependencyInjection/Handler/MongoDBHandlerExtension.php new file mode 100644 index 00000000..72027db7 --- /dev/null +++ b/src/DependencyInjection/Handler/MongoDBHandlerExtension.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle\DependencyInjection\Handler; + +use MongoDB\Client; +use Monolog\Formatter\MongoDBFormatter; +use Monolog\Handler\MongoDBHandler; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +final class MongoDBHandlerExtension implements HandlerExtensionInterface +{ + public function getName(): string + { + return 'mongodb'; + } + + public function buildArrayNode(ArrayNodeDefinition $handlerNode): void + { + $handlerNode + ->canBeUnset() + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return ['id' => $v]; }) + ->end() + ->children() + ->scalarNode('id') + ->info('ID of a MongoDB\Client service') + ->example('doctrine_mongodb.odm.logs_connection') + ->end() + ->scalarNode('uri')->end() + ->scalarNode('username')->end() + ->scalarNode('password')->end() + ->scalarNode('database')->defaultValue('monolog')->end() + ->scalarNode('collection')->defaultValue('logs')->end() + ->end() + ->validate() + ->ifTrue(function ($v) { + return !isset($v['id']) && !isset($v['uri']); + }) + ->thenInvalid('The "mongodb" handler configuration requires either a service "id" or a connection "uri".') + ->end() + ->example([ + 'uri' => 'mongodb://localhost:27017', + 'database' => 'monolog', + 'collection' => 'logs', + ]) + ; + } + + /** + * @param array{ + * id?: string, + * uri?: string, + * username?: string, + * password?: string, + * database: string, + * collection: string, + * } $handler + */ + public function getDefinition(array $config, array $handler): Definition + { + $definition = new Definition(MongoDBHandler::class); + + if (!class_exists(Client::class)) { + throw new \RuntimeException('The "mongodb" handler requires the mongodb/mongodb package to be installed.'); + } + + if (isset($handler['id'])) { + $client = new Reference($handler['id']); + } else { + $uriOptions = ['appname' => 'monolog-bundle']; + + if (isset($handler['username'])) { + $uriOptions['username'] = $handler['username']; + } + + if (isset($handler['password'])) { + $uriOptions['password'] = $handler['password']; + } + + $client = new Definition(Client::class, [ + $handler['uri'], + $uriOptions, + ]); + } + + $definition->setArguments([ + $client, + $handler['database'], + $handler['collection'], + $config['level'], + $config['bubble'], + ]); + + if (empty($config['formatter'])) { + $formatter = new Definition(MongoDBFormatter::class); + $definition->addMethodCall('setFormatter', [$formatter]); + } + + return $definition; + } +} diff --git a/src/DependencyInjection/Handler/MongoHandlerExtension.php b/src/DependencyInjection/Handler/MongoHandlerExtension.php new file mode 100644 index 00000000..154a364e --- /dev/null +++ b/src/DependencyInjection/Handler/MongoHandlerExtension.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle\DependencyInjection\Handler; + +use MongoDB\Client; +use Monolog\Handler\MongoDBHandler; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +final class MongoHandlerExtension implements HandlerExtensionInterface +{ + public function getName(): string + { + return 'mongo'; + } + + public function buildArrayNode(ArrayNodeDefinition $handlerNode): void + { + $handlerNode + ->canBeUnset() + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return ['id' => $v]; }) + ->end() + ->children() + ->scalarNode('id')->end() + ->scalarNode('host')->end() + ->scalarNode('port')->defaultValue(27017)->end() + ->scalarNode('user')->end() + ->scalarNode('pass')->end() + ->scalarNode('database')->defaultValue('monolog')->end() + ->scalarNode('collection')->defaultValue('logs')->end() + ->end() + ->validate() + ->ifTrue(function ($v) { return !isset($v['id']) && !isset($v['host']); }) + ->thenInvalid('The "mongo" handler configuration requires either a service "id" or a connection "host".') + ->end() + ->validate() + ->ifTrue(function ($v) { return isset($v['user']) && !isset($v['pass']); }) + ->thenInvalid('If you set user, you must provide a password.') + ->end() + ->example([ + 'host' => 'localhost', + 'port' => 27017, + 'database' => 'monolog', + 'collection' => 'logs', + ]) + ; + } + + /** + * @param array{ + * mongo: array{ + * id?: string, + * host?: string, + * port: int|string, + * user?: string, + * pass?: string, + * database: string, + * collection: string, + * }, + * level: int|string, + * bubble: bool, + * formatter?: string, + * } $handler + */ + public function getDefinition(array $config, array $handler): Definition + { + trigger_deprecation('symfony/monolog-bundle', '3.11', 'The "mongo" handler type is deprecated in MonologBundle since version 3.11.0, use the "mongodb" type instead.'); + + if (!class_exists(Client::class)) { + throw new \RuntimeException('The "mongo" handler requires the mongodb/mongodb package to be installed.'); + } + + $definition = new Definition(MongoDBHandler::class); + + if (isset($handler['id'])) { + $client = new Reference($handler['id']); + } else { + $server = 'mongodb://'; + if (isset($handler['user'])) { + $server .= $handler['user'].':'.$handler['pass'].'@'; + } + $server .= $handler['host'].':'.$handler['port']; + + $client = new Definition(Client::class, [ + $server, + ['appname' => 'monolog-bundle'], + ]); + } + + $definition->setArguments([ + $client, + $handler['database'], + $handler['collection'], + $config['level'], + $config['bubble'], + ]); + + return $definition; + } +} diff --git a/src/DependencyInjection/Handler/StreamHandlerExtension.php b/src/DependencyInjection/Handler/StreamHandlerExtension.php new file mode 100644 index 00000000..6bfd82a5 --- /dev/null +++ b/src/DependencyInjection/Handler/StreamHandlerExtension.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\MonologBundle\DependencyInjection\Handler; + +use Monolog\Handler\StreamHandler; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\DependencyInjection\Definition; + +class StreamHandlerExtension implements HandlerExtensionInterface +{ + public function getName(): string + { + return 'stream'; + } + + public function buildArrayNode(ArrayNodeDefinition $handlerNode): void + { + $handlerNode + ->canBeUnset() + ->children() + ->scalarNode('path')->defaultValue('%kernel.logs_dir%/%kernel.environment%.log')->end() + ->integerNode('file_permission')->defaultValue(0644)->info('File permission. Default is 0644')->end() + ->booleanNode('use_locking')->defaultFalse()->end() + ->end() + ->example([ + 'path' => '%kernel.logs_dir%/%kernel.environment%.log', + 'file_permission' => null, + 'use_locking' => false, + ]) + ; + } + + /** + * @param array{ + * path: string, + * file_permission: int|null, + * use_locking: bool, + * } $handler + */ + public function getDefinition(array $config, array $handler): Definition + { + $definition = new Definition(StreamHandler::class); + $definition->setArguments([ + $handler['path'], + $config['level'], + $config['bubble'], + $handler['file_permission'], + $handler['use_locking'], + ]); + + return $definition; + } +} diff --git a/src/DependencyInjection/MonologExtension.php b/src/DependencyInjection/MonologExtension.php index 235f3804..dd579721 100644 --- a/src/DependencyInjection/MonologExtension.php +++ b/src/DependencyInjection/MonologExtension.php @@ -13,16 +13,23 @@ use Monolog\Attribute\AsMonologProcessor; use Monolog\Attribute\WithMonologChannel; +use Monolog\Handler; use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; use Monolog\Handler\HandlerInterface; use Monolog\Logger; use Monolog\Processor\ProcessorInterface; use Monolog\Processor\PsrLogMessageProcessor; use Monolog\ResettableInterface; +use Symfony\Bridge\Monolog\Handler\DebugHandler; use Symfony\Bridge\Monolog\Handler\FingersCrossed\HttpCodeActivationStrategy; use Symfony\Bridge\Monolog\Processor\SwitchUserTokenProcessor; use Symfony\Bridge\Monolog\Processor\TokenProcessor; use Symfony\Bridge\Monolog\Processor\WebProcessor; +use Symfony\Bundle\MonologBundle\DependencyInjection\Handler\DefaultHandlerExtension; +use Symfony\Bundle\MonologBundle\DependencyInjection\Handler\HandlerExtensionInterface; +use Symfony\Bundle\MonologBundle\DependencyInjection\Handler\MongoDBHandlerExtension; +use Symfony\Bundle\MonologBundle\DependencyInjection\Handler\MongoHandlerExtension; +use Symfony\Bundle\MonologBundle\DependencyInjection\Handler\StreamHandlerExtension; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Argument\BoundArgument; use Symfony\Component\DependencyInjection\ChildDefinition; @@ -47,6 +54,33 @@ class MonologExtension extends Extension private $swiftMailerHandlers = []; + /** + * @var array + */ + private array $handlerExtensions = []; + + public function __construct() + { + $this->addHandlerExtension(new DefaultHandlerExtension('null', Handler\NullHandler::class)); + $this->addHandlerExtension(new DefaultHandlerExtension('noop', Handler\NoopHandler::class)); + $this->addHandlerExtension(new DefaultHandlerExtension('debug', DebugHandler::class)); + $this->addHandlerExtension(new DefaultHandlerExtension('test', Handler\TestHandler::class)); + $this->addHandlerExtension(new DefaultHandlerExtension('browser_console', Handler\BrowserConsoleHandler::class)); + $this->addHandlerExtension(new MongoHandlerExtension()); + $this->addHandlerExtension(new MongoDBHandlerExtension()); + $this->addHandlerExtension(new StreamHandlerExtension()); + } + + public function addHandlerExtension(HandlerExtensionInterface $extension): void + { + $this->handlerExtensions[$extension->getName()] = $extension; + } + + public function getConfiguration(array $config, ContainerBuilder $container) + { + return new Configuration($this->handlerExtensions); + } + /** * Loads the Monolog configuration. * @@ -156,7 +190,8 @@ public function getNamespace() private function buildHandler(ContainerBuilder $container, $name, array $handler) { $handlerId = $this->getHandlerId($name); - if ('service' === $handler['type']) { + $handlerType = $handler['type']; + if ('service' === $handlerType) { $container->setAlias($handlerId, $handler['id']); if (!empty($handler['nested']) && true === $handler['nested']) { @@ -166,8 +201,13 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler return $handlerId; } - $handlerClass = $this->getHandlerClassByType($handler['type']); - $definition = new Definition($handlerClass); + if (\array_key_exists($handlerType, $this->handlerExtensions)) { + $definition = $this->handlerExtensions[$handlerType]->getDefinition($handler, $handler[$handlerType] ?? []); + $container->setDefinition($handlerId, $definition); + } else { + $handlerClass = $this->getHandlerClassByType($handlerType); + $definition = new Definition($handlerClass); + } if ($handler['include_stacktraces']) { $definition->setConfigurator(['Symfony\\Bundle\\MonologBundle\\MonologBundle', 'includeStacktraces']); @@ -177,12 +217,12 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler $handler['process_psr_3_messages']['enabled'] = !isset($handler['handler']) && !$handler['members']; } - if ($handler['process_psr_3_messages']['enabled'] && method_exists($handlerClass, 'pushProcessor')) { + if ($handler['process_psr_3_messages']['enabled'] && method_exists($definition->getClass(), 'pushProcessor')) { $processorId = $this->buildPsrLogMessageProcessor($container, $handler['process_psr_3_messages']); $definition->addMethodCall('pushProcessor', [new Reference($processorId)]); } - switch ($handler['type']) { + switch ($handlerType) { case 'stream': $definition->setArguments([ $handler['path'], @@ -264,77 +304,6 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler ]); break; - case 'mongo': - trigger_deprecation('symfony/monolog-bundle', '3.11', 'The "mongo" handler type is deprecated in MonologBundle since version 3.11.0, use the "mongodb" type instead.'); - - if (!class_exists('MongoDB\Client')) { - throw new \RuntimeException('The "mongo" handler requires the mongodb/mongodb package to be installed.'); - } - - if (isset($handler['mongo']['id'])) { - $client = new Reference($handler['mongo']['id']); - } else { - $server = 'mongodb://'; - - if (isset($handler['mongo']['user'])) { - $server .= $handler['mongo']['user'].':'.$handler['mongo']['pass'].'@'; - } - - $server .= $handler['mongo']['host'].':'.$handler['mongo']['port']; - - $client = new Definition('MongoDB\Client', [ - $server, - ['appname' => 'monolog-bundle'], - ]); - } - - $definition->setArguments([ - $client, - $handler['mongo']['database'], - $handler['mongo']['collection'], - $handler['level'], - $handler['bubble'], - ]); - break; - - case 'mongodb': - if (!class_exists('MongoDB\Client')) { - throw new \RuntimeException('The "mongodb" handler requires the mongodb/mongodb package to be installed.'); - } - - if (isset($handler['mongodb']['id'])) { - $client = new Reference($handler['mongodb']['id']); - } else { - $uriOptions = ['appname' => 'monolog-bundle']; - - if (isset($handler['mongodb']['username'])) { - $uriOptions['username'] = $handler['mongodb']['username']; - } - - if (isset($handler['mongodb']['password'])) { - $uriOptions['password'] = $handler['mongodb']['password']; - } - - $client = new Definition('MongoDB\Client', [ - $handler['mongodb']['uri'], - $uriOptions, - ]); - } - - $definition->setArguments([ - $client, - $handler['mongodb']['database'], - $handler['mongodb']['collection'], - $handler['level'], - $handler['bubble'], - ]); - - if (empty($handler['formatter'])) { - $formatter = new Definition('Monolog\Formatter\MongoDBFormatter'); - $definition->addMethodCall('setFormatter', [$formatter]); - } - break; - case 'elasticsearch': trigger_deprecation('symfony/monolog-bundle', '3.8', 'The "elasticsearch" handler type is deprecated in MonologBundle since version 3.8.0, use the "elastica" type instead, or switch to the official Elastic client using the "elastic_search" type.'); // no break @@ -971,25 +940,15 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler ]); break; - // Handlers using the constructor of AbstractHandler without adding their own arguments - case 'browser_console': - case 'test': - case 'null': - case 'noop': - case 'debug': - $definition->setArguments([ - $handler['level'], - $handler['bubble'], - ]); - break; - default: - $nullWarning = ''; - if ('' == $handler['type']) { - $nullWarning = ', if you meant to define a null handler in a yaml config, make sure you quote "null" so it does not get converted to a php null'; - } + if (!isset($this->handlerExtensions[$handlerType])) { + $nullWarning = ''; + if ('' == $handlerType) { + $nullWarning = ', if you meant to define a null handler in a yaml config, make sure you quote "null" so it does not get converted to a php null'; + } - throw new \InvalidArgumentException(\sprintf('Invalid handler type "%s" given for handler "%s".'.$nullWarning, $handler['type'], $name)); + throw new \InvalidArgumentException(\sprintf('Invalid handler type "%s" given for handler "%s".'.$nullWarning, $handler['type'], $name)); + } } if (!empty($handler['nested']) && true === $handler['nested']) { @@ -1000,7 +959,7 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler $definition->addMethodCall('setFormatter', [new Reference($handler['formatter'])]); } - if (!\in_array($handlerId, $this->nestedHandlers) && is_subclass_of($handlerClass, ResettableInterface::class)) { + if (!\in_array($handlerId, $this->nestedHandlers) && is_subclass_of($definition->getClass(), ResettableInterface::class)) { $definition->addTag('kernel.reset', ['method' => 'reset']); } @@ -1063,8 +1022,6 @@ private function getHandlerClassByType($handlerType) 'whatfailuregroup' => 'Monolog\Handler\WhatFailureGroupHandler', 'fingers_crossed' => 'Monolog\Handler\FingersCrossedHandler', 'filter' => 'Monolog\Handler\FilterHandler', - 'mongo' => 'Monolog\Handler\MongoDBHandler', - 'mongodb' => 'Monolog\Handler\MongoDBHandler', 'elasticsearch' => 'Monolog\Handler\ElasticSearchHandler', 'telegram' => 'Monolog\Handler\TelegramBotHandler', 'server_log' => 'Symfony\Bridge\Monolog\Handler\ServerLogHandler', diff --git a/tests/DependencyInjection/MonologExtensionTest.php b/tests/DependencyInjection/MonologExtensionTest.php index aec3ccfb..5b6abeb6 100644 --- a/tests/DependencyInjection/MonologExtensionTest.php +++ b/tests/DependencyInjection/MonologExtensionTest.php @@ -692,7 +692,7 @@ public function testV2AddedOnV1() /** * @dataProvider provideLoglevelParameterConfig */ - public function testLogLevelfromParameter(array $parameters, array $config, $expectedClass, array $expectedArgs) + public function testLogLevelFromParameter(array $parameters, array $config, $expectedClass, array $expectedArgs) { $container = new ContainerBuilder(); foreach ($parameters as $name => $value) { @@ -1045,6 +1045,11 @@ public function testMongoDB() 'uri' => 'mongodb://localhost:27018', ], ], + 'mongodb_without_explicit_type' => [ + 'mongodb' => [ + 'uri' => 'mongodb://localhost:27018', + ], + ], ], ]]; @@ -1088,6 +1093,13 @@ public function testMongoDB() $this->assertDICConstructorArguments($client, ['mongodb://localhost:27018', ['appname' => 'monolog-bundle']]); $this->assertDICConstructorArguments($handler, [$client, 'monolog', 'logs', 'DEBUG', true]); $this->assertDICDefinitionMethodCallAt(1, $handler, 'setFormatter', [$formatter]); + + // MongoDB handler without an explicit type + $handler = $container->getDefinition('monolog.handler.mongodb_without_explicit_type'); + $this->assertDICDefinitionClass($handler, MongoDBHandler::class); + $client = $handler->getArgument(0); + $this->assertDICDefinitionClass($client, 'MongoDB\Client'); + $this->assertDICConstructorArguments($client, ['mongodb://localhost:27018', ['appname' => 'monolog-bundle']]); } protected function getContainer(array $config = [], array $thirdPartyDefinitions = []): ContainerBuilder