Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -26,6 +27,8 @@ jobs:
- php: '8.4'
deps: highest
monolog: '3.*'
- php: '8.4'
extensions: mongodb

env:
SYMFONY_REQUIRE: ${{ matrix.symfony }}
Expand All @@ -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' }}"
Expand All @@ -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:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
10 changes: 10 additions & 0 deletions config/schema/monolog-1.0.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<xsd:element name="channels" type="channels" minOccurs="0" maxOccurs="1" />
<xsd:element name="publisher" type="publisher" minOccurs="0" maxOccurs="1" />
<xsd:element name="mongo" type="mongo" minOccurs="0" maxOccurs="1" />
<xsd:element name="mongodb" type="mongodb" minOccurs="0" maxOccurs="1" />
<xsd:element name="elasticsearch" type="elasticsearch" minOccurs="0" maxOccurs="1" />
<xsd:element name="config" type="xsd:anyType" minOccurs="0" maxOccurs="1" />
<xsd:element name="excluded-404" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
Expand Down Expand Up @@ -163,6 +164,15 @@
<xsd:attribute name="collection" type="xsd:string" />
</xsd:complexType>

<xsd:complexType name="mongodb">
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="uri" type="xsd:string" />
<xsd:attribute name="username" type="xsd:string" />
<xsd:attribute name="password" type="xsd:string" />
<xsd:attribute name="database" type="xsd:string" />
<xsd:attribute name="collection" type="xsd:string" />
</xsd:complexType>

<xsd:complexType name="redis">
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="host" type="xsd:string" />
Expand Down
52 changes: 50 additions & 2 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand All @@ -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()
;
}
Expand Down
48 changes: 46 additions & 2 deletions src/DependencyInjection/MonologExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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([
Expand All @@ -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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have writeConcern: 0 or 1 by default for logs?
You can accept driver options.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That can be specified in the uri option. I only wanted to break out username and password since (a) they might come from separate env vars and (b) they were supported in the original mongo syntax and I wanted to avoid dealing with URI encoding.

We can revisit when adding more options across the board.

You can accept driver options

Were you actually referring to $driverOptions (i.e. third ctor param) or just URI options?

]);
}

$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
Expand Down Expand Up @@ -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',
Expand Down
18 changes: 18 additions & 0 deletions tests/DependencyInjection/FixtureMonologExtensionTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
14 changes: 14 additions & 0 deletions tests/DependencyInjection/Fixtures/xml/mongodb.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" ?>

<srv:container xmlns="http://symfony.com/schema/dic/monolog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/monolog http://symfony.com/schema/dic/monolog/monolog-1.0.xsd">

<config>
<handler name="mongodb" type="mongodb">
<mongodb uri="mongodb://localhost:27018" username="username" password="password" database="db" collection="coll" />
</handler>
</config>
</srv:container>
10 changes: 10 additions & 0 deletions tests/DependencyInjection/Fixtures/yml/mongodb.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
monolog:
handlers:
mongodb:
type: mongodb
mongodb:
uri: "mongodb://localhost:27018"
username: username
password: password
database: db
collection: coll
Loading