From a464c02ddf88aecae8987ffa590d0bdaf367356a Mon Sep 17 00:00:00 2001 From: Jesper Noordsij Date: Tue, 1 Apr 2025 14:04:40 +0200 Subject: [PATCH 1/3] Add logger that logs exceptions to RoundRobinTransport --- .../Component/Mailer/Transport/RoundRobinTransport.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php b/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php index e48644f790b56..cfec1249bae4a 100644 --- a/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php +++ b/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Mailer\Transport; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use Symfony\Component\Mailer\Envelope; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\Exception\TransportExceptionInterface; @@ -24,6 +26,7 @@ */ class RoundRobinTransport implements TransportInterface { + private LoggerInterface $logger; /** * @var \SplObjectStorage */ @@ -36,11 +39,13 @@ class RoundRobinTransport implements TransportInterface public function __construct( private array $transports, private int $retryPeriod = 60, + ?LoggerInterface $logger = null, ) { if (!$transports) { throw new TransportException(\sprintf('"%s" must have at least one transport configured.', static::class)); } + $this->logger = $logger ?? new NullLogger(); $this->deadTransports = new \SplObjectStorage(); } @@ -54,6 +59,7 @@ public function send(RawMessage $message, ?Envelope $envelope = null): ?SentMess } catch (TransportExceptionInterface $e) { $exception ??= new TransportException('All transports failed.'); $exception->appendDebug(\sprintf("Transport \"%s\": %s\n", $transport, $e->getDebug())); + $this->logger->error(\sprintf("Transport \"%s\" failed.", $transport), ['exception' => $e]); $this->deadTransports[$transport] = microtime(true); } } From ef5c8c6efbd31b8d25b5b868fe15630622e33cc8 Mon Sep 17 00:00:00 2001 From: Jesper Noordsij Date: Tue, 1 Apr 2025 14:25:55 +0200 Subject: [PATCH 2/3] Add tests for logger property on RoundRobinTransport --- .../Transport/RoundRobinTransportTest.php | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Mailer/Tests/Transport/RoundRobinTransportTest.php b/src/Symfony/Component/Mailer/Tests/Transport/RoundRobinTransportTest.php index 5de88e71fa247..fc5379a9521ed 100644 --- a/src/Symfony/Component/Mailer/Tests/Transport/RoundRobinTransportTest.php +++ b/src/Symfony/Component/Mailer/Tests/Transport/RoundRobinTransportTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Mailer\Tests\Transport; use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\Exception\TransportExceptionInterface; use Symfony\Component\Mailer\Transport\RoundRobinTransport; @@ -167,19 +168,45 @@ public function testSendOneDeadMessageAlterationsDoNotPersist() $this->assertFalse($message->getHeaders()->has('X-Transport-1')); } + public function testLoggerReceivesExceptions() + { + $t1 = $this->createMock(TransportInterface::class); + $t1->expects($this->exactly(2))->method('send'); + + $ex = new TransportException(); + $t2 = $this->createMock(TransportInterface::class); + $t2->expects($this->exactly(1)) + ->method('send') + ->willReturnCallback(fn () => throw $ex); + $t2->expects($this->atLeast(1))->method('__toString')->willReturn('t2'); + + $log = $this->createMock(LoggerInterface::class); + $log->expects($this->exactly(1)) + ->method('error') + ->with('Transport "t2" failed.', ['exception' => $ex]); + + $t = new RoundRobinTransport([$t1, $t2], logger: $log); + $p = new \ReflectionProperty($t, 'cursor'); + $p->setValue($t, 0); + $t->send(new RawMessage('')); + $this->assertTransports($t, 1, []); + $t->send(new RawMessage('')); + $this->assertTransports($t, 1, [$t2]); + } + public function testFailureDebugInformation() { $t1 = $this->createMock(TransportInterface::class); $e1 = new TransportException(); $e1->appendDebug('Debug message 1'); $t1->expects($this->once())->method('send')->willThrowException($e1); - $t1->expects($this->once())->method('__toString')->willReturn('t1'); + $t1->expects($this->atLeast(1))->method('__toString')->willReturn('t1'); $t2 = $this->createMock(TransportInterface::class); $e2 = new TransportException(); $e2->appendDebug('Debug message 2'); $t2->expects($this->once())->method('send')->willThrowException($e2); - $t2->expects($this->once())->method('__toString')->willReturn('t2'); + $t2->expects($this->atLeast(1))->method('__toString')->willReturn('t2'); $t = new RoundRobinTransport([$t1, $t2]); From 4e62b59fe95e37a36f029195832d0e89c731cd1b Mon Sep 17 00:00:00 2001 From: Jesper Noordsij Date: Thu, 12 Jun 2025 15:18:28 +0200 Subject: [PATCH 3/3] Apply review suggestions to RoundRobinTransport.php Co-authored-by: Fabien Potencier --- src/Symfony/Component/Mailer/CHANGELOG.md | 5 +++++ .../Component/Mailer/Transport/RoundRobinTransport.php | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Mailer/CHANGELOG.md b/src/Symfony/Component/Mailer/CHANGELOG.md index 3816cc474948b..1102438a092e9 100644 --- a/src/Symfony/Component/Mailer/CHANGELOG.md +++ b/src/Symfony/Component/Mailer/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.4 +--- + + * Add `logger` (constructor) property to `RoundRobinTransport` + 7.3 --- diff --git a/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php b/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php index cfec1249bae4a..b381b5ecec4eb 100644 --- a/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php +++ b/src/Symfony/Component/Mailer/Transport/RoundRobinTransport.php @@ -26,7 +26,6 @@ */ class RoundRobinTransport implements TransportInterface { - private LoggerInterface $logger; /** * @var \SplObjectStorage */ @@ -39,13 +38,12 @@ class RoundRobinTransport implements TransportInterface public function __construct( private array $transports, private int $retryPeriod = 60, - ?LoggerInterface $logger = null, + private LoggerInterface $logger = new NullLogger(), ) { if (!$transports) { throw new TransportException(\sprintf('"%s" must have at least one transport configured.', static::class)); } - $this->logger = $logger ?? new NullLogger(); $this->deadTransports = new \SplObjectStorage(); }