Skip to content

Commit 7533626

Browse files
committed
Use a compiler pass to register data collector conditionally
1 parent 905a594 commit 7533626

File tree

9 files changed

+142
-108
lines changed

9 files changed

+142
-108
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"symfony/framework-bundle": "^6.3.5 || ^7.0",
2929
"symfony/phpunit-bridge": "~6.3.10 || ^6.4.1 || ^7.0.1",
3030
"symfony/yaml": "^6.3 || ^7.0",
31+
"symfony/web-profiler-bundle": "^6.3 || ^7.0",
3132
"zenstruck/browser": "^1.6"
3233
},
3334
"scripts": {

config/services.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020

2121
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
2222

23+
use MongoDB\Bundle\Client;
2324
use MongoDB\Bundle\Command\DebugCommand;
2425
use MongoDB\Bundle\DataCollector\MongoDBDataCollector;
25-
use MongoDB\Bundle\TraceableClient;
2626

2727
return static function (ContainerConfigurator $container): void {
2828
// default configuration for services in *this* file
@@ -39,14 +39,13 @@
3939
->tag('console.command');
4040

4141
$services
42-
->set('mongodb.prototype.client', TraceableClient::class)
42+
->set('mongodb.prototype.client', Client::class)
4343
->arg('$uri', abstract_arg('Should be defined by pass'))
4444
->arg('$uriOptions', abstract_arg('Should be defined by pass'))
45-
->arg('$driverOptions', abstract_arg('Should be defined by pass'))
46-
->tag('mongodb.client');
45+
->arg('$driverOptions', abstract_arg('Should be defined by pass'));
4746

4847
$services
49-
->set('data_collector.mongodb', MongoDBDataCollector::class)
48+
->set('mongodb.data_collector', MongoDBDataCollector::class)
5049
->tag('data_collector', [
5150
'template' => '@MongoDB/Collector/mongodb.html.twig',
5251
'id' => 'mongodb',

src/Client.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright 2023-present MongoDB, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* https://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
namespace MongoDB\Bundle;
22+
23+
use MongoDB\Client as MongoDBClient;
24+
use MongoDB\Driver\Monitoring\Subscriber;
25+
26+
final class Client extends MongoDBClient
27+
{
28+
/**
29+
* @internal
30+
*/
31+
public function addSubscriber(Subscriber $subscriber): void
32+
{
33+
$this->getManager()->addSubscriber($subscriber);
34+
}
35+
}

src/DataCollector/MongoDBDataCollector.php

Lines changed: 40 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@
2020

2121
namespace MongoDB\Bundle\DataCollector;
2222

23-
use MongoDB\BSON\Document;
24-
use MongoDB\Bundle\TraceableClient;
25-
use MongoDB\Client;
23+
use MongoDB\Bundle\Client;
2624
use MongoDB\Driver\Monitoring\CommandFailedEvent;
2725
use MongoDB\Driver\Monitoring\CommandStartedEvent;
2826
use MongoDB\Driver\Monitoring\CommandSucceededEvent;
@@ -33,67 +31,61 @@
3331
final class MongoDBDataCollector extends DataCollector
3432
{
3533
/**
36-
* @var list<Client>
34+
* @var list<array{client:Client, subscriber:DriverEventSubscriber}>
3735
*/
3836
private array $clients = [];
3937

40-
public function addClient(string $name, Client $client): void
38+
public function addClient(string $name, Client $client, DriverEventSubscriber $subscriber): void
4139
{
42-
$this->clients[$name] = $client;
40+
$this->clients[$name] = [
41+
'client' => $client,
42+
'subscriber' => $subscriber,
43+
];
4344
}
4445

4546
public function collect(Request $request, Response $response, ?\Throwable $exception = null): void
4647
{
47-
foreach ($this->clients as $name => $client) {
48+
foreach ($this->clients as $name => ['client' => $client, 'subscriber' => $subscriber]) {
4849
$totalTime = 0;
4950
$requestCount = 0;
5051
$errorCount = 0;
5152
$requests = [];
5253

53-
if ($client instanceof TraceableClient) {
54-
foreach ($client->getEvents() as $event) {
55-
$requestId = $event->getRequestId();
54+
foreach ($subscriber->getEvents() as $event) {
55+
$requestId = $event->getRequestId();
5656

57-
$eventData = [
58-
'class' => $event::class,
59-
'commandName' => $event->getCommandName(),
60-
'server' => $event->getServer()->getInfo(),
57+
if ($event instanceof CommandStartedEvent) {
58+
$command = (array) $event->getCommand();
59+
unset($command['lsid'], $command['$clusterTime']);
60+
61+
$requests[$requestId] = [
6162
'client' => $name,
63+
'startedAt' => hrtime(true),
64+
'commandName' => $event->getCommandName(),
65+
'command' => $command,
66+
// 'server' => $event->getServer()->getInfo(),
67+
'operationId' => $event->getOperationId(),
68+
'database' => $event->getDatabaseName(),
69+
'serviceId' => $event->getServiceId(),
6270
];
63-
64-
if ($event instanceof CommandStartedEvent) {
65-
$command = (array) $event->getCommand();
66-
unset($command['lsid'], $command['$clusterTime']);
67-
68-
$requests[$requestId] = [
69-
'client' => $name,
70-
'startedAt' => hrtime(true),
71-
'commandName' => $event->getCommandName(),
72-
'command' => $command,
73-
// 'server' => $event->getServer()->getInfo(),
74-
'operationId' => $event->getOperationId(),
75-
'database' => $event->getDatabaseName(),
76-
'serviceId' => $event->getServiceId(),
77-
];
78-
++$requestCount;
79-
} elseif ($event instanceof CommandSucceededEvent) {
80-
$requests[$requestId] += [
81-
// 'reply' => Document::fromPHP($event->getReply()),
82-
'duration' => $event->getDurationMicros(),
83-
'endedAt' => hrtime(true),
84-
'success' => true,
85-
];
86-
$totalTime += $event->getDurationMicros();
87-
} elseif ($event instanceof CommandFailedEvent) {
88-
$requests[$requestId] += [
89-
// 'reply' => Document::fromPHP($event->getReply()),
90-
'duration' => $event->getDurationMicros(),
91-
'error' => $event->getError(),
92-
'success' => false,
93-
];
94-
$totalTime += $event->getDurationMicros();
95-
++$errorCount;
96-
}
71+
++$requestCount;
72+
} elseif ($event instanceof CommandSucceededEvent) {
73+
$requests[$requestId] += [
74+
// 'reply' => Document::fromPHP($event->getReply()),
75+
'duration' => $event->getDurationMicros(),
76+
'endedAt' => hrtime(true),
77+
'success' => true,
78+
];
79+
$totalTime += $event->getDurationMicros();
80+
} elseif ($event instanceof CommandFailedEvent) {
81+
$requests[$requestId] += [
82+
// 'reply' => Document::fromPHP($event->getReply()),
83+
'duration' => $event->getDurationMicros(),
84+
'error' => $event->getError(),
85+
'success' => false,
86+
];
87+
$totalTime += $event->getDurationMicros();
88+
++$errorCount;
9789
}
9890
}
9991

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Copyright 2023-present MongoDB, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* https://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
21+
namespace MongoDB\Bundle\DependencyInjection\Compiler;
22+
23+
use MongoDB\Bundle\DataCollector\DriverEventSubscriber;
24+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
25+
use Symfony\Component\DependencyInjection\ContainerBuilder;
26+
use Symfony\Component\DependencyInjection\Definition;
27+
use Symfony\Component\DependencyInjection\Reference;
28+
29+
/** @internal */
30+
final class DataCollectorPass implements CompilerPassInterface
31+
{
32+
public function process(ContainerBuilder $container): void
33+
{
34+
if (!$container->has('profiler')) {
35+
return;
36+
}
37+
38+
$dataCollector = $container->getDefinition('mongodb.data_collector');
39+
40+
// Add a subscriber to each client to collect driver events, and register the client to the data collector.
41+
foreach ($container->findTaggedServiceIds('mongodb.client', true) as $clientId => $attributes) {
42+
$subscriberId = sprintf('%s.subscriber', $clientId);
43+
$container->setDefinition($subscriberId, new Definition(DriverEventSubscriber::class));
44+
$container->getDefinition($clientId)->addMethodCall('addSubscriber', [new Reference($subscriberId)]);
45+
$dataCollector->addMethodCall('addClient', [
46+
$attributes[0]['name'] ?? $clientId,
47+
new Reference($clientId),
48+
new Reference($subscriberId),
49+
]);
50+
}
51+
}
52+
}

src/DependencyInjection/MongoDBExtension.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
use Symfony\Component\DependencyInjection\Definition;
2828
use Symfony\Component\DependencyInjection\Extension\Extension;
2929
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
30-
use Symfony\Component\DependencyInjection\Reference;
3130

3231
use function dirname;
3332
use function sprintf;
@@ -73,11 +72,9 @@ private function createClients(string $defaultClient, array $clients, ContainerB
7372
$clientDefinition->setArgument('$uri', $configuration['uri']);
7473
$clientDefinition->setArgument('$uriOptions', $configuration['uriOptions'] ?? []);
7574
$clientDefinition->setArgument('$driverOptions', $configuration['driverOptions'] ?? []);
76-
75+
$clientDefinition->addTag('mongodb.client', ['name' => $client]);
7776
$container->setDefinition($serviceId, $clientDefinition);
7877

79-
$dataCollector->addMethodCall('addClient', [$client, new Reference($serviceId)]);
80-
8178
if (isset($configuration['default_database'])) {
8279
$container->setParameter(sprintf('%s.default_database', $serviceId), $configuration['default_database']);
8380
}

src/MongoDBBundle.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
namespace MongoDB\Bundle;
2222

2323
use MongoDB\Bundle\DependencyInjection\MongoDBExtension;
24+
use Symfony\Component\DependencyInjection\ContainerBuilder;
2425
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
2526
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
2627

@@ -30,4 +31,9 @@ public function getContainerExtension(): ?ExtensionInterface
3031
{
3132
return new MongoDBExtension();
3233
}
34+
35+
public function build(ContainerBuilder $container): void
36+
{
37+
$container->addCompilerPass(new DependencyInjection\Compiler\DataCollectorPass());
38+
}
3339
}

src/TraceableClient.php

Lines changed: 0 additions & 48 deletions
This file was deleted.

tests/Unit/DependencyInjection/MongoDBExtensionTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function testLoadWithSingleClient(): void
5959

6060
// Check service definition
6161
$definition = $container->getDefinition('mongodb.client.default');
62-
$this->assertSame(Client::class, $definition->getClass());
62+
$this->assertTrue(is_a($definition->getClass(), Client::class, true), sprintf('Expected "%s" to be a "%s"', $definition->getClass(), Client::class));
6363
$this->assertSame('mongodb://localhost:27017', $definition->getArgument('$uri'));
6464

6565
// Check alias definition
@@ -93,12 +93,12 @@ public function testLoadWithMultipleClients(): void
9393

9494
// Check service definitions
9595
$definition = $container->getDefinition('mongodb.client.default');
96-
$this->assertSame(Client::class, $definition->getClass());
96+
$this->assertTrue(is_a($definition->getClass(), Client::class, true), sprintf('Expected "%s" to be a "%s"', $definition->getClass(), Client::class));
9797
$this->assertSame('mongodb://localhost:27017', $definition->getArgument('$uri'));
9898
$this->assertSame(['readPreference' => 'primary'], $definition->getArgument('$uriOptions'));
9999

100100
$definition = $container->getDefinition('mongodb.client.secondary');
101-
$this->assertSame(Client::class, $definition->getClass());
101+
$this->assertTrue(is_a($definition->getClass(), Client::class, true), sprintf('Expected "%s" to be a "%s"', $definition->getClass(), Client::class));
102102
$this->assertSame('mongodb://localhost:27018', $definition->getArgument('$uri'));
103103
$this->assertEquals(['serverApi' => new ServerApi((string) ServerApi::V1)], $definition->getArgument('$driverOptions'));
104104
}

0 commit comments

Comments
 (0)