diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 459ed51f..f3f9639b 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -14,6 +14,7 @@ jobs:
php: [ '8.1', '8.2', '8.3', '8.4' ]
monolog: [ '2.*' ]
symfony: [ false ]
+ extensions: [ '' ]
include:
- php: '8.1'
deps: lowest
@@ -26,6 +27,8 @@ jobs:
- php: '8.4'
deps: highest
monolog: '3.*'
+ - php: '8.4'
+ extensions: mongodb
env:
SYMFONY_REQUIRE: ${{ matrix.symfony }}
@@ -40,6 +43,7 @@ jobs:
php-version: ${{ matrix.php }}
ini-values: zend.exception_ignore_args=false
tools: flex
+ extensions: ${{ matrix.extensions }}
- name: Configure composer
if: "${{ matrix.deps == 'highest' }}"
@@ -49,6 +53,10 @@ jobs:
if: "${{ matrix.monolog != '' }}"
run: composer require --no-update monolog/monolog:${{ matrix.monolog }}
+ - name: Require mongodb/mongodb if ext-mongodb is available
+ if: "${{ contains(matrix.extensions, 'mongodb') }}"
+ run: composer require --no-update mongodb/mongodb
+
- name: Composer install
uses: ramsey/composer-install@v3
with:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2eebab41..8f54b078 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@
* Add `hosts` configuration for `elastica` handler
* Add `enabled` option to `handlers` configuration
* Add `priority` field to `processor` tag
+* Add `mongodb` handler and deprecate `mongo`
## 3.10.0 (2023-11-06)
diff --git a/config/schema/monolog-1.0.xsd b/config/schema/monolog-1.0.xsd
index 1e73118d..bc9c6e55 100644
--- a/config/schema/monolog-1.0.xsd
+++ b/config/schema/monolog-1.0.xsd
@@ -21,6 +21,7 @@
+
@@ -163,6 +164,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index 2585f411..6fa53ed4 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -90,6 +90,17 @@
* - [level]: level name or int value, defaults to DEBUG
* - [bubble]: bool, defaults to true
*
+ * - mongodb:
+ * - mongodb:
+ * - id: optional if uri is given
+ * - uri: MongoDB connection string, optional if id is given
+ * - [username]: Username for database authentication
+ * - [password]: Password for database authentication
+ * - [database]: Database to which logs are written (not used for auth), defaults to "monolog"
+ * - [collection]: Collection to which logs are written, defaults to "logs"
+ * - [level]: level name or int value, defaults to DEBUG
+ * - [bubble]: bool, defaults to true
+ *
* - elastic_search:
* - elasticsearch:
* - id: optional if host is given
@@ -648,6 +659,7 @@ public function getConfigTreeBuilder(): TreeBuilder
$this->addGelfSection($handlerNode);
$this->addMongoSection($handlerNode);
+ $this->addMongoDBSection($handlerNode);
$this->addElasticsearchSection($handlerNode);
$this->addRedisSection($handlerNode);
$this->addPredisSection($handlerNode);
@@ -879,7 +891,7 @@ private function addMongoSection(ArrayNodeDefinition $handlerNode)
->ifTrue(function ($v) {
return !isset($v['id']) && !isset($v['host']);
})
- ->thenInvalid('What must be set is either the host or the id.')
+ ->thenInvalid('The "mongo" handler configuration requires either a service "id" or a connection "host".')
->end()
->validate()
->ifTrue(function ($v) {
@@ -891,7 +903,43 @@ private function addMongoSection(ArrayNodeDefinition $handlerNode)
->end()
->validate()
->ifTrue(function ($v) { return 'mongo' === $v['type'] && !isset($v['mongo']); })
- ->thenInvalid('The mongo configuration has to be specified to use a MongoHandler')
+ ->thenInvalid('The "mongo" configuration has to be specified to use a "mongo" handler type.')
+ ->end()
+ ;
+ }
+
+ private function addMongoDBSection(ArrayNodeDefinition $handlerNode)
+ {
+ $handlerNode
+ ->children()
+ ->arrayNode('mongodb')
+ ->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()
+ ->end()
+ ->end()
+ ->validate()
+ ->ifTrue(function ($v) { return 'mongodb' === $v['type'] && !isset($v['mongodb']); })
+ ->thenInvalid('The "mongodb" configuration has to be specified to use a "mongodb" handler type.')
->end()
;
}
diff --git a/src/DependencyInjection/MonologExtension.php b/src/DependencyInjection/MonologExtension.php
index f6dfb574..235f3804 100644
--- a/src/DependencyInjection/MonologExtension.php
+++ b/src/DependencyInjection/MonologExtension.php
@@ -265,6 +265,12 @@ 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 {
@@ -278,9 +284,8 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler
$client = new Definition('MongoDB\Client', [
$server,
+ ['appname' => 'monolog-bundle'],
]);
-
- $client->setPublic(false);
}
$definition->setArguments([
@@ -292,6 +297,44 @@ private function buildHandler(ContainerBuilder $container, $name, array $handler
]);
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
@@ -1021,6 +1064,7 @@ private function getHandlerClassByType($handlerType)
'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/FixtureMonologExtensionTestCase.php b/tests/DependencyInjection/FixtureMonologExtensionTestCase.php
index 3bb2264a..30529e4f 100644
--- a/tests/DependencyInjection/FixtureMonologExtensionTestCase.php
+++ b/tests/DependencyInjection/FixtureMonologExtensionTestCase.php
@@ -12,6 +12,7 @@
namespace Symfony\Bundle\MonologBundle\Tests\DependencyInjection;
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
+use Monolog\Handler\MongoDBHandler;
use Monolog\Handler\NoopHandler;
use Monolog\Handler\NullHandler;
use Monolog\Processor\PsrLogMessageProcessor;
@@ -332,6 +333,23 @@ public function testEnabledHandleOption()
$this->assertFalse($container->hasDefinition('monolog.handler.disabled'));
}
+ public function testMongoDB()
+ {
+ if (!class_exists('MongoDB\Client')) {
+ $this->markTestSkipped('mongodb/mongodb is not installed.');
+ }
+
+ $container = $this->getContainer('mongodb');
+
+ $this->assertTrue($container->hasDefinition('monolog.handler.mongodb'));
+ $handler = $container->getDefinition('monolog.handler.mongodb');
+ $this->assertDICDefinitionClass($handler, MongoDBHandler::class);
+ $client = $handler->getArgument(0);
+ $this->assertDICDefinitionClass($client, 'MongoDB\Client');
+ $this->assertDICConstructorArguments($client, ['mongodb://localhost:27018', ['appname' => 'monolog-bundle', 'username' => 'username', 'password' => 'password']]);
+ $this->assertDICConstructorArguments($handler, [$client, 'db', 'coll', 'DEBUG', true]);
+ }
+
protected function getContainer($fixture)
{
$container = new ContainerBuilder();
diff --git a/tests/DependencyInjection/Fixtures/xml/mongodb.xml b/tests/DependencyInjection/Fixtures/xml/mongodb.xml
new file mode 100644
index 00000000..cce38b62
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/xml/mongodb.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/DependencyInjection/Fixtures/yml/mongodb.yml b/tests/DependencyInjection/Fixtures/yml/mongodb.yml
new file mode 100644
index 00000000..4311f8ee
--- /dev/null
+++ b/tests/DependencyInjection/Fixtures/yml/mongodb.yml
@@ -0,0 +1,10 @@
+monolog:
+ handlers:
+ mongodb:
+ type: mongodb
+ mongodb:
+ uri: "mongodb://localhost:27018"
+ username: username
+ password: password
+ database: db
+ collection: coll
diff --git a/tests/DependencyInjection/MonologExtensionTest.php b/tests/DependencyInjection/MonologExtensionTest.php
index 4a5ad2c1..aec3ccfb 100644
--- a/tests/DependencyInjection/MonologExtensionTest.php
+++ b/tests/DependencyInjection/MonologExtensionTest.php
@@ -16,6 +16,7 @@
use Monolog\Handler\ElasticaHandler;
use Monolog\Handler\ElasticsearchHandler;
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
+use Monolog\Handler\MongoDBHandler;
use Monolog\Handler\RollbarHandler;
use Monolog\Logger;
use Monolog\Processor\UidProcessor;
@@ -932,6 +933,163 @@ public function testElasticsearchAndElasticaHandlers()
$this->assertSame(['hosts' => ['es:9200'], 'transport' => 'Http'], $elasticaClient->getArgument(0));
}
+ /** @group legacy */
+ public function testMongo()
+ {
+ if (!class_exists('MongoDB\Client')) {
+ $this->markTestSkipped('mongodb/mongodb is not installed.');
+ }
+
+ $this->expectDeprecation('Since symfony/monolog-bundle 3.11: The "mongo" handler type is deprecated in MonologBundle since version 3.11.0, use the "mongodb" type instead.');
+
+ $container = new ContainerBuilder();
+ $container->setDefinition('mongodb.client', new Definition('MongoDB\Client'));
+
+ $config = [[
+ 'handlers' => [
+ 'mongo_with_id' => [
+ 'type' => 'mongo',
+ 'mongo' => ['id' => 'mongodb.client'],
+ ],
+ 'mongo_with_string_id' => [
+ 'type' => 'mongo',
+ 'mongo' => 'mongodb.client',
+ ],
+ 'mongo_with_host' => [
+ 'type' => 'mongo',
+ 'mongo' => [
+ 'host' => 'localhost',
+ 'port' => '27018',
+ 'user' => 'username',
+ 'pass' => 'password',
+ 'database' => 'db',
+ 'collection' => 'coll',
+ ],
+ ],
+ 'mongo_with_host_and_default_args' => [
+ 'type' => 'mongo',
+ 'mongo' => [
+ 'host' => 'localhost',
+ ],
+ ],
+ ],
+ ]];
+
+ $extension = new MonologExtension();
+ $extension->load($config, $container);
+
+ $this->assertTrue($container->hasDefinition('monolog.handler.mongo_with_id'));
+ $this->assertTrue($container->hasDefinition('monolog.handler.mongo_with_string_id'));
+ $this->assertTrue($container->hasDefinition('monolog.handler.mongo_with_host'));
+ $this->assertTrue($container->hasDefinition('monolog.handler.mongo_with_host_and_default_args'));
+
+ // MongoDB handler should receive the mongodb.client as first argument
+ $handler = $container->getDefinition('monolog.handler.mongo_with_id');
+ $this->assertDICDefinitionClass($handler, MongoDBHandler::class);
+ $this->assertDICConstructorArguments($handler, [new Reference('mongodb.client'), 'monolog', 'logs', 'DEBUG', true]);
+
+ // MongoDB handler should receive the mongodb.client as first argument
+ $handler = $container->getDefinition('monolog.handler.mongo_with_string_id');
+ $this->assertDICDefinitionClass($handler, MongoDBHandler::class);
+ $this->assertDICConstructorArguments($handler, [new Reference('mongodb.client'), 'monolog', 'logs', 'DEBUG', true]);
+
+ // MongoDB handler with host and arguments
+ $handler = $container->getDefinition('monolog.handler.mongo_with_host');
+ $this->assertDICDefinitionClass($handler, MongoDBHandler::class);
+ $client = $handler->getArgument(0);
+ $this->assertDICDefinitionClass($client, 'MongoDB\Client');
+ $this->assertDICConstructorArguments($client, ['mongodb://username:password@localhost:27018', ['appname' => 'monolog-bundle']]);
+ $this->assertDICConstructorArguments($handler, [$client, 'db', 'coll', 'DEBUG', true]);
+
+ // MongoDB handler with host and default arguments
+ $handler = $container->getDefinition('monolog.handler.mongo_with_host_and_default_args');
+ $this->assertDICDefinitionClass($handler, MongoDBHandler::class);
+ $client = $handler->getArgument(0);
+ $this->assertDICDefinitionClass($client, 'MongoDB\Client');
+ $this->assertDICConstructorArguments($client, ['mongodb://localhost:27017', ['appname' => 'monolog-bundle']]);
+ $this->assertDICConstructorArguments($handler, [$client, 'monolog', 'logs', 'DEBUG', true]);
+ }
+
+ public function testMongoDB()
+ {
+ if (!class_exists('MongoDB\Client')) {
+ $this->markTestSkipped('mongodb/mongodb is not installed.');
+ }
+
+ $container = new ContainerBuilder();
+ $container->setDefinition('mongodb.client', new Definition('MongoDB\Client'));
+
+ $config = [[
+ 'handlers' => [
+ 'mongodb_with_id' => [
+ 'type' => 'mongodb',
+ 'mongodb' => ['id' => 'mongodb.client'],
+ ],
+ 'mongodb_with_string_id' => [
+ 'type' => 'mongodb',
+ 'mongodb' => 'mongodb.client',
+ ],
+ 'mongodb_with_uri' => [
+ 'type' => 'mongodb',
+ 'mongodb' => [
+ 'uri' => 'mongodb://localhost:27018',
+ 'username' => 'username',
+ 'password' => 'password',
+ 'database' => 'db',
+ 'collection' => 'coll',
+ ],
+ ],
+ 'mongodb_with_uri_and_default_args' => [
+ 'type' => 'mongodb',
+ 'mongodb' => [
+ 'uri' => 'mongodb://localhost:27018',
+ ],
+ ],
+ ],
+ ]];
+
+ $extension = new MonologExtension();
+ $extension->load($config, $container);
+
+ $this->assertTrue($container->hasDefinition('monolog.handler.mongodb_with_id'));
+ $this->assertTrue($container->hasDefinition('monolog.handler.mongodb_with_string_id'));
+ $this->assertTrue($container->hasDefinition('monolog.handler.mongodb_with_uri'));
+ $this->assertTrue($container->hasDefinition('monolog.handler.mongodb_with_uri_and_default_args'));
+
+ // A MongoDBFormatter will be applied to each handler by default
+ $formatter = new Definition('Monolog\Formatter\MongoDBFormatter');
+
+ // MongoDB handler should receive the mongodb.client as first argument
+ $handler = $container->getDefinition('monolog.handler.mongodb_with_id');
+ $this->assertDICDefinitionClass($handler, MongoDBHandler::class);
+ $this->assertDICConstructorArguments($handler, [new Reference('mongodb.client'), 'monolog', 'logs', 'DEBUG', true]);
+ $this->assertDICDefinitionMethodCallAt(1, $handler, 'setFormatter', [$formatter]);
+
+ // MongoDB handler should receive the mongodb.client as first argument
+ $handler = $container->getDefinition('monolog.handler.mongodb_with_string_id');
+ $this->assertDICDefinitionClass($handler, MongoDBHandler::class);
+ $this->assertDICConstructorArguments($handler, [new Reference('mongodb.client'), 'monolog', 'logs', 'DEBUG', true]);
+ $this->assertDICDefinitionMethodCallAt(1, $handler, 'setFormatter', [$formatter]);
+
+ // MongoDB handler with arguments
+ $handler = $container->getDefinition('monolog.handler.mongodb_with_uri');
+ $this->assertDICDefinitionClass($handler, MongoDBHandler::class);
+ $client = $handler->getArgument(0);
+ $this->assertDICDefinitionClass($client, 'MongoDB\Client');
+ $this->assertDICConstructorArguments($client, ['mongodb://localhost:27018', ['appname' => 'monolog-bundle', 'username' => 'username', 'password' => 'password']]);
+ $this->assertDICConstructorArguments($handler, [$client, 'db', 'coll', 'DEBUG', true]);
+ $this->assertDICDefinitionMethodCallAt(1, $handler, 'setFormatter', [$formatter]);
+
+ // MongoDB handler with host and default arguments
+ $handler = $container->getDefinition('monolog.handler.mongodb_with_uri_and_default_args');
+ $this->assertDICDefinitionClass($handler, MongoDBHandler::class);
+ $client = $handler->getArgument(0);
+ $this->assertDICDefinitionClass($client, 'MongoDB\Client');
+ $this->assertDICConstructorArguments($client, ['mongodb://localhost:27018', ['appname' => 'monolog-bundle']]);
+ $this->assertDICConstructorArguments($handler, [$client, 'monolog', 'logs', 'DEBUG', true]);
+ $this->assertDICDefinitionMethodCallAt(1, $handler, 'setFormatter', [$formatter]);
+ }
+
protected function getContainer(array $config = [], array $thirdPartyDefinitions = []): ContainerBuilder
{
$container = new ContainerBuilder(new EnvPlaceholderParameterBag());