10000 Replace DbalLogger by DebugMiddleware · doctrine/DoctrineBundle@9583584 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9583584

Browse files
l-voostrolucky
authored andcommitted
Replace DbalLogger by DebugMiddleware
1 parent f9339b2 commit 9583584

File tree

10 files changed

+704
-40
lines changed

10 files changed

+704
-40
lines changed

DataCollector/DoctrineDataCollector.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Doctrine\Persistence\ManagerRegistry;
1414
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
1515
use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector as BaseCollector;
16+
use Symfony\Bridge\Doctrine\Middleware\Debug\DebugDataHolder;
1617
use Symfony\Component\HttpFoundation\Request;
1718
use Symfony\Component\HttpFoundation\Response;
1819
use Throwable;
@@ -64,12 +65,18 @@ class DoctrineDataCollector extends BaseCollector
6465
/** @var bool */
6566
private $shouldValidateSchema;
6667

67-
public function __construct(ManagerRegistry $registry, bool $shouldValidateSchema = true)
68+
/** @psalm-suppress UndefinedClass */
69+
public function __construct(ManagerRegistry $registry, bool $shouldValidateSchema = true, ?DebugDataHolder $debugDataHolder = null)
6870
{
6971
$this->registry = $registry;
7072
$this->shouldValidateSchema = $shouldValidateSchema;
7173

72-
parent::__construct($registry);
74+
if ($debugDataHolder === null) {
75+
parent::__construct($registry);
76+
} else {
77+
/** @psalm-suppress TooManyArguments */
78+
parent::__construct($registry, $debugDataHolder);
79+
}
7380
}
7481

7582
/**

DependencyInjection/DoctrineExtension.php

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
1717
use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface;
1818
use Doctrine\DBAL\Logging\LoggerChain;
19-
use Doctrine\DBAL\Logging\Middleware;
2019
use Doctrine\DBAL\Sharding\PoolingShardConnection;
2120
use Doctrine\DBAL\Sharding\PoolingShardManager;
2221
use Doctrine\DBAL\Tools\Console\Command\ImportCommand;
@@ -34,6 +33,7 @@
3433
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
3534
use Symfony\Bridge\Doctrine\Messenger\DoctrineClearEntityManagerWorkerSubscriber;
3635
use Symfony\Bridge\Doctrine\Messenger\DoctrineTransactionMiddleware;
36+
use Symfony\Bridge\Doctrine\Middleware\Debug\Middleware as SfDebugMiddleware;
3737
use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor;
3838
use Symfony\Bridge\Doctrine\SchemaListener\DoctrineDbalCacheAdapterSchemaSubscriber;
3939
use Symfony\Bridge\Doctrine\SchemaListener\MessengerTransportDoctrineSchemaSubscriber;
@@ -116,9 +116,6 @@ protected function dbalLoad(array $config, ContainerBuilder $container)
116116
{
117117
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
118118
$loader->load('dbal.xml');
119-
$chainLogger = $container->getDefinition('doctrine.dbal.logger.chain');
120-
$logger = new Reference('doctrine.dbal.logger');
121-
$chainLogger->addArgument([$logger]);
122119

123120
if (class_exists(ImportCommand::class)) {
124121
$container->register('doctrine.database_import_command', ImportDoctrineCommand::class)
@@ -147,12 +144,22 @@ protected function dbalLoad(array $config, ContainerBuilder $container)
147144
$container->setParameter('doctrine.connections', $connections);
148145
$container->setParameter('doctrine.default_connection', $this->defaultConnection);
149146

150-
$connWithLogging = [];
147+
$connWithLogging = [];
148+
$connWithProfiling = [];
149+
$connWithBacktrace = [];
151150
foreach ($config['connections'] as $name => $connection) {
152151
if ($connection['logging']) {
153152
$connWithLogging[] = $name;
154153
}
155154

155+
if ($connection['profiling']) {
156+
$connWithProfiling[] = $name;
157+
158+
if ($connection['profiling_collect_backtrace']) {
159+
$connWithBacktrace[] = $name;
160+
}
161+
}
162+
156163
$this->loadDbalConnection($name, $connection, $container);
157164
}
158165

@@ -173,7 +180,7 @@ protected function dbalLoad(array $config, ContainerBuilder $container)
173180
});
174181
}
175182

176-
$this->useMiddlewaresIfAvailable($container, $connWithLogging);
183+
$this->useMiddlewaresIfAvailable($container, $connWithLogging, $connWithProfiling, $connWithBacktrace);
177184
}
178185

179186
/**
@@ -187,7 +194,9 @@ protected function loadDbalConnection($name, array $connection, ContainerBuilder
187194
{
188195
$configuration = $container->setDefinition(sprintf('doctrine.dbal.%s_connection.configuration', $name), new ChildDefinition('doctrine.dbal.connection.configuration'));
189196
$logger = null;
190-
if ($connection['logging']) {
197+
198+
/** @psalm-suppress UndefinedClass */
199+
if (! interface_exists(MiddlewareInterface::class) && $connection['logging']) {
191200
$logger = new Reference('doctrine.dbal.logger');
192201
}
193202

@@ -196,7 +205,7 @@ protected function loadDbalConnection($name, array $connection, ContainerBuilder
196205
$dataCollectorDefinition = $container->getDefinition('data_collector.doctrine');
197206
$dataCollectorDefinition->replaceArgument(1, $connection['profiling_collect_schema_errors']);
198207

199-
if ($connection['profiling']) {
208+
if (! $this->isSfDebugMiddlewareAvailable() && $connection['profiling']) {
200209
$profilingAbstractId = $connection['profiling_collect_backtrace'] ?
201210
'doctrine.dbal.logger.backtrace' :
202211
'doctrine.dbal.logger.profiling';
@@ -1116,24 +1125,50 @@ private function createArrayAdapterCachePool(ContainerBuilder $container, string
11161125
return $id;
11171126
}
11181127

1119-
/** @param string[] $connWithLogging */
1120-
private function useMiddlewaresIfAvailable(ContainerBuilder $container, array $connWithLogging): void
1121-
{
1128+
/**
1129+
* @param string[] $connWithLogging
1130+
* @param string[] $connWithProfiling
1131+
* @param string[] $connWithBacktrace
1132+
*/
1133+
private function useMiddlewaresIfAvailable(
1134+
ContainerBuilder $container,
1135+
array $connWithLogging,
1136+
array $connWithProfiling,
1137+
array $connWithBacktrace
1138+
): void {
11221139
/** @psalm-suppress UndefinedClass */
1123-
if (! class_exists(Middleware::class)) {
1140+
if (! interface_exists(MiddlewareInterface::class)) {
11241141
return;
11251142
}
11261143

1144+
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
1145+
$loader->load('middlewares.xml');
1146+
11271147
$container
11281148
->getDefinition('doctrine.dbal.logger')
11291149
->replaceArgument(0, null);
11301150

1131-
$loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
1132-
$loader->load('middlewares.xml');
1133-
11341151
$loggingMiddlewareAbstractDef = $container->getDefinition('doctrine.dbal.logging_middleware');
11351152
foreach ($connWithLogging as $connName) {
11361153
$loggingMiddlewareAbstractDef->addTag('doctrine.middleware', ['connection' => $connName]);
11371154
}
1155+
1156+
if ($this->isSfDebugMiddlewareAvailable()) {
1157+
$container->getDefinition('doctrine.debug_data_holder')->replaceArgument(0, $connWithBacktrace);
1158+
$debugMiddlewareAbstractDef = $container->getDefinition('doctrine.dbal.debug_middleware');
1159+
foreach ($connWithProfiling as $connName) {
1160+
$debugMiddlewareAbstractDef
1161+
->addTag('doctrine.middleware', ['connection' => $connName]);
1162+
}
1163+
} else {
1164+
$container->removeDefinition('doctrine.dbal.debug_middleware');
1165+
$container->removeDefinition('doctrine.debug_data_holder');
1166+
}
1167+
}
1168+
1169+
private function isSfDebugMiddlewareAvailable(): bool
1170+
{
1171+
/** @psalm-suppress UndefinedClass */
1172+
return interface_exists(MiddlewareInterface::class) && class_exists(SfDebugMiddleware::class);
11381173
}
11391174
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
namespace Doctrine\Bundle\DoctrineBundle\Middleware;
4+
5+
use Symfony\Bridge\Doctrine\Middleware\Debug\DebugDataHolder;
6+
use Symfony\Bridge\Doctrine\Middleware\Debug\Query;
7+
8+
use function array_slice;
9+
use function debug_backtrace;
10+
use function in_array;
11+
12+
use const DEBUG_BACKTRACE_IGNORE_ARGS;
13+
14+
/** @psalm-suppress MissingDependency */
15+
class BacktraceDebugDataHolder extends DebugDataHolder
16+
{
17+
/** @var string[] */
18+
private $connWithBacktraces;
19+
20+
/** @var array<string, array<string, mixed>[]> */
21+
private $backtraces = [];
22+
23+
/** @param string[] $connWithBacktraces */
24+
public function __construct(array $connWithBacktraces)
25+
{
26+
$this->connWithBacktraces = $connWithBacktraces;
27+
}
28+
29+
public function reset(): void
30+
{
31+
parent::reset();
32+
33+
$this->backtraces = [];
34+
}
35+
36+
public function addQuery(string $connectionName, Query $query): void
37+
{
38+
parent::addQuery($connectionName, $query);
39+
40+
if (! in_array($connectionName, $this->connWithBacktraces, true)) {
41+
return;
42+
}
43+
44+
// array_slice to skip middleware calls in the trace
45+
$this->backtraces[$connectionName][] = array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 2);
46+
}
47+
48+
/** @return array<string, array<string, mixed>[]> */
49+
public function getData(): array
50+
{
51+
$dataWithBacktraces = [];
52+
53+
$data = parent::getData();
54+
foreach ($data as $connectionName => $dataForConn) {
55+
$dataWithBacktraces[$connectionName] = $this->getDataForConnection($connectionName, $dataForConn);
56+
}
57+
58+
return $dataWithBacktraces;
59+
}
60+
61+
/**
62+
* @param mixed[][] $dataForConn
63+
*
64+
* @return mixed[][]
65+
*/
66+
private function getDataForConnection(string $connectionName, array $dataForConn): array
67+
{
68+
$data = [];
69+
70+
foreach ($dataForConn as $idx => $record) {
71+
$data[] = $this->addBacktracesIfAvailable($connectionName, $record, $idx);
72+
}
73+
74+
return $data;
75+
}
76+
77+
/**
78+
* @param mixed[] $record
79+
*
80+
* @return mixed[]
81+
*/
82+
private function addBacktracesIfAvailable(string $connectionName, array $record, int $idx): array
83+
{
84+
if (! isset($this->backtraces[$connectionName])) {
85+
return $record;
86+
}
87+
88+
$record['backtrace'] = $this->backtraces[$connectionName][$idx];
89+
90+
return $record;
91+
}
92+
}

Middleware/DebugMiddleware.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Doctrine\Bundle\DoctrineBundle\Middleware;
4+
5+
use Doctrine\DBAL\Driver as DriverInterface;
6+
use Doctrine\DBAL\Driver\Middleware;
7+
use Symfony\Bridge\Doctrine\Middleware\Debug\DebugDataHolder;
8+
use Symfony\Bridge\Doctrine\Middleware\Debug\Driver;
9+
use Symfony\Component\Stopwatch\Stopwatch;
10+
11+
class DebugMiddleware implements Middleware, ConnectionNameAwareInterface
12+
{
13+
/** @var DebugDataHolder */
14+
private $debugDataHolder;
15+
16+
/** @var Stopwatch|null */
17+
private $stopwatch;
18+
19+
/** @var string */
20+
private $connectionName = 'default';
21+
22+
public function __construct(DebugDataHolder $debugDataHolder, ?Stopwatch $stopwatch)
23+
{
24+
$this->debugDataHolder = $debugDataHolder;
25+
$this->stopwatch = $stopwatch;
26+
}
27+
28+
public function setConnectionName(string $name): void
29+
{
30+
$this->connectionName = $name;
31+
}
32+
33+
public function wrap(DriverInterface $driver): DriverInterface
34+
{
35+
return new Driver($driver, $this->debugDataHolder, $this->stopwatch, $this->connectionName);
36+
}
37+
}

Resources/config/middlewares.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,12 @@
99
<argument type="service" id="logger" />
1010
<tag name="monolog.logger" channel="doctrine" />
1111
</service>
12+
<service id="doctrine.debug_data_holder" class="Doctrine\Bundle\DoctrineBundle\Middleware\BacktraceDebugDataHolder">
13+
<argument type="collection" />
14+
</service>
15+
<service id="doctrine.dbal.debug_middleware" class="Doctrine\Bundle\DoctrineBundle\Middleware\DebugMiddleware" abstract="true">
16+
<argument type="service" id="doctrine.debug_data_holder" />
17+
<argument type="service" id="debug.stopwatch" on-invalid="null" />
18+
</service>
1219
</services>
1320
</container>

Tests/ContainerTest.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Doctrine\Common\EventManager;
1111
use Doctrine\DBAL\Configuration as DBALConfiguration;
1212
use Doctrine\DBAL\Connection;
13+
use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface;
1314
use Doctrine\DBAL\Logging\LoggerChain;
1415
use Doctrine\DBAL\Tools\Console\Command\ImportCommand;
1516
use Doctrine\DBAL\Types\Type;
@@ -21,6 +22,7 @@
2122
use Symfony\Bridge\Doctrine\CacheWarmer\ProxyCacheWarmer;
2223
use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector;
2324
use Symfony\Bridge\Doctrine\Logger\DbalLogger;
25+
use Symfony\Bridge\Doctrine\Middleware\Debug\Middleware as SfDebugMiddleware;
2426
use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor;
2527
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator;
2628
use Symfony\Bridge\Doctrine\Validator\DoctrineLoader;
@@ -32,16 +34,22 @@
3234

3335
class ContainerTest extends TestCase
3436
{
37+
/** @group legacy */
3538
public function testContainerWithDbalOnly(): void
3639
{
3740
$kernel = new DbalTestKernel();
3841
$kernel->boot();
3942

4043
$container = $kernel->getContainer();
41-
$this->assertInstanceOf(
42-
LoggerChain::class,
43-
$container->get('doctrine.dbal.logger.chain.default')
44-
);
44+
45+
/** @psalm-suppress UndefinedClass */
46+
if (! interface_exists(MiddlewareInterface::class)) {
47+
$this->assertInstanceOf(
48+
LoggerChain::class,
49+
$container->get('doctrine.dbal.logger.chain.default')
50+
);
51+
}
52+
4553
if (class_exists(ImportCommand::class)) {
4654
self::assertTrue($container->has('doctrine.database_import_command'));
4755
} else {
@@ -57,7 +65,11 @@ public function testContainer(): void
5765

5866
$container = $this->createXmlBundleTestContainer();
5967

60-
$this->assertInstanceOf(DbalLogger::class, $container->get('doctrine.dbal.logger'));
68+
/** @psalm-suppress UndefinedClass */
69+
if (! interface_exists(MiddlewareInterface::class) || ! class_exists(SfDebugMiddleware::class)) {
70+
$this->assertInstanceOf(DbalLogger::class, $container->get('doctrine.dbal.logger'));
71+
}
72+
6173
$this->assertInstanceOf(DoctrineDataCollector::class, $container->get('data_collector.doctrine'));
6274
$this->assertInstanceOf(DBALConfiguration::class, $container->get('doctrine.dbal.default_connection.configuration'));
6375
$this->assertInstanceOf(EventManager::class, $container->get('doctrine.dbal.default_connection.event_manager'));

Tests/DependencyInjection/AbstractDoctrineExtensionTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
1313
use Doctrine\DBAL\Configuration;
1414
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
15+
use Doctrine\DBAL\Driver\Middleware;
1516
use Doctrine\DBAL\DriverManager;
1617
use Doctrine\ORM\EntityManager;
1718
use Doctrine\ORM\EntityManagerInterface;
@@ -447,6 +448,11 @@ public function testLoadMultipleConnections(): void
447448

448449
public function testLoadLogging(): void
449450
{
451+
/** @psalm-suppress UndefinedClass */
452+
if (interface_exists(Middleware::class)) {
453+
$this->markTestSkipped('This test requires Debug middleware to not be activated');
454+
}
455+
450456
$container = $this->loadContainer('dbal_logging');
451457

452458
$definition = $container->getDefinition('doctrine.dbal.log_connection.configuration');

0 commit comments

Comments
 (0)
0