Skip to content

Commit 09fc1a7

Browse files
lyrixxchalasr
authored andcommitted
[Messenger] Add a middleware to log when transaction has been left open
1 parent d36f402 commit 09fc1a7

File tree

4 files changed

+117
-1
lines changed

4 files changed

+117
-1
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
CHANGELOG
22
=========
33

4+
5.4
5+
---
6+
* Add a middleware to log when transaction has been left open `DoctrineOpenTransactionLoggerMiddleware`
7+
48
5.3
59
---
610

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Doctrine\Messenger;
13+
14+
use Doctrine\ORM\EntityManagerInterface;
15+
use Doctrine\Persistence\ManagerRegistry;
16+
use Psr\Log\LoggerInterface;
17+
use Psr\Log\NullLogger;
18+
use Symfony\Component\Messenger\Envelope;
19+
use Symfony\Component\Messenger\Middleware\StackInterface;
20+
21+
/**
22+
* Middleware to log when transaction has been left open.
23+
*
24+
* @author Grégoire Pineau <[email protected]>
25+
*/
26+
class DoctrineOpenTransactionLoggerMiddleware extends AbstractDoctrineMiddleware
27+
{
28+
private $logger;
29+
30+
public function __construct(ManagerRegistry $managerRegistry, string $entityManagerName = null, LoggerInterface $logger = null)
31+
{
32+
parent::__construct($managerRegistry, $entityManagerName);
33+
34+
$this->logger = $logger ?? new NullLogger();
35+
}
36+
37+
protected function handleForManager(EntityManagerInterface $entityManager, Envelope $envelope, StackInterface $stack): Envelope
38+
{
39+
try {
40+
return $stack->next()->handle($envelope, $stack);
41+
} finally {
42+
if ($entityManager->getConnection()->isTransactionActive()) {
43+
$this->logger->error('A handler opened a transaction but did not close it.', [
44+
'message' => $envelope->getMessage(),
45+
]);
46+
}
47+
}
48+
}
49+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Doctrine\Tests\Messenger;
13+
14+
use Doctrine\DBAL\Connection;
15+
use Doctrine\ORM\EntityManagerInterface;
16+
use Doctrine\Persistence\ManagerRegistry;
17+
use Psr\Log\AbstractLogger;
18+
use Symfony\Bridge\Doctrine\Messenger\DoctrineOpenTransactionLoggerMiddleware;
19+
use Symfony\Component\Messenger\Envelope;
20+
use Symfony\Component\Messenger\Test\Middleware\MiddlewareTestCase;
21+
22+
class DoctrineOpenTransactionLoggerMiddlewareTest extends MiddlewareTestCase
23+
{
24+
private $logger;
25+
private $connection;
26+
private $entityManager;
27+
private $middleware;
28+
29+
protected function setUp(): void
30+
{
31+
$this->logger = new class() extends AbstractLogger {
32+
public $logs = [];
33+
34+
public function log($level, $message, $context = []): void
35+
{
36+
$this->logs[$level][] = $message;
37+
}
38+
};
39+
40+
$this->connection = $this->createMock(Connection::class);
41+
42+
$this->entityManager = $this->createMock(EntityManagerInterface::class);
43+
$this->entityManager->method('getConnection')->willReturn($this->connection);
44+
45+
$managerRegistry = $this->createMock(ManagerRegistry::class);
46+
$managerRegistry->method('getManager')->willReturn($this->entityManager);
47+
48+
$this->middleware = new DoctrineOpenTransactionLoggerMiddleware($managerRegistry, null, $this->logger);
49+
}
50+
51+
public function testMiddlewareWrapsInTransactionAndFlushes()
52+
{
53+
$this->connection->expects($this->exactly(1))
54+
->method('isTransactionActive')
55+
->will($this->onConsecutiveCalls(true, true, false))
56+
;
57+
58+
$this->middleware->handle(new Envelope(new \stdClass()), $this->getStackMock());
59+
60+
$this->assertSame(['error' => ['A handler opened a transaction but did not close it.']], $this->logger->logs);
61+
}
62+
}

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
"doctrine/collections": "~1.0",
4949
"doctrine/data-fixtures": "^1.1",
5050
"doctrine/dbal": "^2.13|^3.0",
51-
"doctrine/orm": "^2.7.3"
51+
"doctrine/orm": "^2.7.3",
52+
"psr/log": "^1|^2|^3"
5253
},
5354
"conflict": {
5455
"doctrine/dbal": "<2.13",

0 commit comments

Comments
 (0)