diff --git a/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/AmqpExtIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/AmqpExtIntegrationTest.php
index 2313e1308f6f9..24dc1a23e9a29 100644
--- a/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/AmqpExtIntegrationTest.php
+++ b/src/Symfony/Component/Messenger/Bridge/Amqp/Tests/Transport/AmqpExtIntegrationTest.php
@@ -76,6 +76,10 @@ public function testItSendsAndReceivesMessages()
$this->assertEmpty(iterator_to_array($receiver->get()));
}
+ /**
+ * @group legacy
+ * ^ for now, deprecation errors are thrown during serialization.
+ */
public function testRetryAndDelay()
{
$serializer = $this->createSerializer();
diff --git a/src/Symfony/Component/Messenger/CHANGELOG.md b/src/Symfony/Component/Messenger/CHANGELOG.md
index 8a0e0b1dec057..5a8b784cc74d3 100644
--- a/src/Symfony/Component/Messenger/CHANGELOG.md
+++ b/src/Symfony/Component/Messenger/CHANGELOG.md
@@ -4,6 +4,7 @@ CHANGELOG
5.2.0
-----
+* The `RedeliveryStamp` will no longer be populated with error data. This information is now stored in the `ErrorDetailsStamp` instead.
* Added `FlattenExceptionNormalizer` to give more information about the exception on Messenger background processes. The `FlattenExceptionNormalizer` has a higher priority than `ProblemNormalizer` and it is only used when the Messenger serialization context is set.
* Added factory methods to `DelayStamp`.
* Removed the exception when dispatching a message with a `DispatchAfterCurrentBusStamp` and not in a context of another dispatch call
diff --git a/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php b/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php
index a95eb1fd41763..f33f3037b4184 100644
--- a/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php
+++ b/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php
@@ -15,6 +15,7 @@
use Symfony\Component\Console\Helper\Dumper;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Messenger\Envelope;
+use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp;
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp;
use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp;
@@ -61,7 +62,11 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io)
/** @var SentToFailureTransportStamp|null $sentToFailureTransportStamp */
$sentToFailureTransportStamp = $envelope->last(SentToFailureTransportStamp::class);
- $lastRedeliveryStampWithException = $this->getLastRedeliveryStampWithException($envelope);
+ /** @var RedeliveryStamp|null $lastRedeliveryStamp */
+ $lastRedeliveryStamp = $envelope->last(RedeliveryStamp::class);
+ /** @var ErrorDetailsStamp|null $lastErrorDetailsStamp */
+ $lastErrorDetailsStamp = $envelope->last(ErrorDetailsStamp::class);
+ $lastRedeliveryStampWithException = $this->getLastRedeliveryStampWithException($envelope, true);
$rows = [
['Class', \get_class($envelope->getMessage())],
@@ -71,14 +76,35 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io)
$rows[] = ['Message Id', $id];
}
- $flattenException = null === $lastRedeliveryStampWithException ? null : $lastRedeliveryStampWithException->getFlattenException();
if (null === $sentToFailureTransportStamp) {
$io->warning('Message does not appear to have been sent to this transport after failing');
} else {
+ $failedAt = '';
+ $errorMessage = '';
+ $errorCode = '';
+ $errorClass = '(unknown)';
+
+ if (null !== $lastRedeliveryStamp) {
+ $failedAt = $lastRedeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s');
+ }
+
+ if (null !== $lastErrorDetailsStamp) {
+ $errorMessage = $lastErrorDetailsStamp->getExceptionMessage();
+ $errorCode = $lastErrorDetailsStamp->getExceptionCode();
+ $errorClass = $lastErrorDetailsStamp->getExceptionClass();
+ } elseif (null !== $lastRedeliveryStampWithException) {
+ // Try reading the errorMessage for messages that are still in the queue without the new ErrorDetailStamps.
+ $errorMessage = $lastRedeliveryStampWithException->getExceptionMessage();
+ if (null !== $lastRedeliveryStampWithException->getFlattenException()) {
+ $errorClass = $lastRedeliveryStampWithException->getFlattenException()->getClass();
+ }
+ }
+
$rows = array_merge($rows, [
- ['Failed at', null === $lastRedeliveryStampWithException ? '' : $lastRedeliveryStampWithException->getRedeliveredAt()->format('Y-m-d H:i:s')],
- ['Error', null === $lastRedeliveryStampWithException ? '' : $lastRedeliveryStampWithException->getExceptionMessage()],
- ['Error Class', null === $flattenException ? '(unknown)' : $flattenException->getClass()],
+ ['Failed at', $failedAt],
+ ['Error', $errorMessage],
+ ['Error Code', $errorCode],
+ ['Error Class', $errorClass],
['Transport', $sentToFailureTransportStamp->getOriginalReceiverName()],
]);
}
@@ -98,6 +124,12 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io)
$dump = new Dumper($io);
$io->writeln($dump($envelope->getMessage()));
$io->title('Exception:');
+ $flattenException = null;
+ if (null !== $lastErrorDetailsStamp) {
+ $flattenException = $lastErrorDetailsStamp->getFlattenException();
+ } elseif (null !== $lastRedeliveryStampWithException) {
+ $flattenException = $lastRedeliveryStampWithException->getFlattenException();
+ }
$io->writeln(null === $flattenException ? '(no data)' : $flattenException->getTraceAsString());
} else {
$io->writeln(' Re-run command with -vv to see more message & error details.');
@@ -122,6 +154,23 @@ protected function getReceiver(): ReceiverInterface
protected function getLastRedeliveryStampWithException(Envelope $envelope): ?RedeliveryStamp
{
+ if (null === \func_get_args()[1]) {
+ trigger_deprecation(
+ 'symfony/messenger',
+ '5.2',
+ sprintf(
+ 'Using the "getLastRedeliveryStampWithException" method in the "%s" class is deprecated, use the "Envelope::last(%s)" instead.',
+ self::class,
+ ErrorDetailsStamp::class
+ )
+ );
+ }
+
+ // Use ErrorDetailsStamp instead if it is available
+ if (null !== $envelope->last(ErrorDetailsStamp::class)) {
+ return null;
+ }
+
/** @var RedeliveryStamp $stamp */
foreach (array_reverse($envelope->all(RedeliveryStamp::class)) as $stamp) {
if (null !== $stamp->getExceptionMessage()) {
diff --git a/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php b/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php
index 0baf7a419f190..bf8a72f906367 100644
--- a/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php
+++ b/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php
@@ -18,6 +18,8 @@
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
+use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp;
+use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface;
/**
@@ -82,13 +84,25 @@ private function listMessages(SymfonyStyle $io, int $max)
$rows = [];
foreach ($envelopes as $envelope) {
- $lastRedeliveryStampWithException = $this->getLastRedeliveryStampWithException($envelope);
+ /** @var RedeliveryStamp|null $lastRedeliveryStamp */
+ $lastRedeliveryStamp = $envelope->last(RedeliveryStamp::class);
+ /** @var ErrorDetailsStamp|null $lastErrorDetailsStamp */
+ $lastErrorDetailsStamp = $envelope->last(ErrorDetailsStamp::class);
+ $lastRedeliveryStampWithException = $this->getLastRedeliveryStampWithException($envelope, true);
+
+ $errorMessage = '';
+ if (null !== $lastErrorDetailsStamp) {
+ $errorMessage = $lastErrorDetailsStamp->getExceptionMessage();
+ } elseif (null !== $lastRedeliveryStampWithException) {
+ // Try reading the errorMessage for messages that are still in the queue without the new ErrorDetailStamps.
+ $errorMessage = $lastRedeliveryStampWithException->getExceptionMessage();
+ }
$rows[] = [
$this->getMessageId($envelope),
\get_class($envelope->getMessage()),
- null === $lastRedeliveryStampWithException ? '' : $lastRedeliveryStampWithException->getRedeliveredAt()->format('Y-m-d H:i:s'),
- null === $lastRedeliveryStampWithException ? '' : $lastRedeliveryStampWithException->getExceptionMessage(),
+ null === $lastRedeliveryStamp ? '' : $lastRedeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s'),
+ $errorMessage,
];
}
diff --git a/src/Symfony/Component/Messenger/Event/AbstractWorkerMessageEvent.php b/src/Symfony/Component/Messenger/Event/AbstractWorkerMessageEvent.php
index ce444d6b3cd83..e3ece8118e047 100644
--- a/src/Symfony/Component/Messenger/Event/AbstractWorkerMessageEvent.php
+++ b/src/Symfony/Component/Messenger/Event/AbstractWorkerMessageEvent.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Messenger\Event;
use Symfony\Component\Messenger\Envelope;
+use Symfony\Component\Messenger\Stamp\StampInterface;
abstract class AbstractWorkerMessageEvent
{
@@ -36,4 +37,9 @@ public function getReceiverName(): string
{
return $this->receiverName;
}
+
+ public function addStamps(StampInterface ...$stamps): void
+ {
+ $this->envelope = $this->envelope->with(...$stamps);
+ }
}
diff --git a/src/Symfony/Component/Messenger/EventListener/AddErrorDetailsStampListener.php b/src/Symfony/Component/Messenger/EventListener/AddErrorDetailsStampListener.php
new file mode 100644
index 0000000000000..4a9aaa1d79cee
--- /dev/null
+++ b/src/Symfony/Component/Messenger/EventListener/AddErrorDetailsStampListener.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Messenger\EventListener;
+
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
+use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp;
+
+final class AddErrorDetailsStampListener implements EventSubscriberInterface
+{
+ public function onMessageFailed(WorkerMessageFailedEvent $event): void
+ {
+ $stamp = new ErrorDetailsStamp($event->getThrowable());
+ $previousStamp = $event->getEnvelope()->last(ErrorDetailsStamp::class);
+
+ // Do not append duplicate information
+ if (null === $previousStamp || !$previousStamp->equals($stamp)) {
+ $event->addStamps($stamp);
+ }
+ }
+
+ public static function getSubscribedEvents(): array
+ {
+ return [
+ // must have higher priority than SendFailedMessageForRetryListener
+ WorkerMessageFailedEvent::class => ['onMessageFailed', 200],
+ ];
+ }
+}
diff --git a/src/Symfony/Component/Messenger/EventListener/SendFailedMessageToFailureTransportListener.php b/src/Symfony/Component/Messenger/EventListener/SendFailedMessageToFailureTransportListener.php
index 8c84cf7992786..729dd5fec4409 100644
--- a/src/Symfony/Component/Messenger/EventListener/SendFailedMessageToFailureTransportListener.php
+++ b/src/Symfony/Component/Messenger/EventListener/SendFailedMessageToFailureTransportListener.php
@@ -11,10 +11,8 @@
namespace Symfony\Component\Messenger\EventListener;
use Psr\Log\LoggerInterface;
-use Symfony\Component\ErrorHandler\Exception\FlattenException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
-use Symfony\Component\Messenger\Exception\HandlerFailedException;
use Symfony\Component\Messenger\Stamp\DelayStamp;
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp;
@@ -49,16 +47,10 @@ public function onMessageFailed(WorkerMessageFailedEvent $event)
return;
}
- $throwable = $event->getThrowable();
- if ($throwable instanceof HandlerFailedException) {
- $throwable = $throwable->getNestedExceptions()[0];
- }
-
- $flattenedException = class_exists(FlattenException::class) ? FlattenException::createFromThrowable($throwable) : null;
$envelope = $envelope->with(
new SentToFailureTransportStamp($event->getReceiverName()),
new DelayStamp(0),
- new RedeliveryStamp(0, $throwable->getMessage(), $flattenedException)
+ new RedeliveryStamp(0)
);
if (null !== $this->logger) {
diff --git a/src/Symfony/Component/Messenger/Stamp/ErrorDetailsStamp.php b/src/Symfony/Component/Messenger/Stamp/ErrorDetailsStamp.php
new file mode 100644
index 0000000000000..e06c3adf3ea41
--- /dev/null
+++ b/src/Symfony/Component/Messenger/Stamp/ErrorDetailsStamp.php
@@ -0,0 +1,86 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Messenger\Stamp;
+
+use Symfony\Component\ErrorHandler\Exception\FlattenException;
+use Symfony\Component\Messenger\Exception\HandlerFailedException;
+use Throwable;
+
+/**
+ * Stamp applied when a messages fails due to an exception in the handler.
+ */
+final class ErrorDetailsStamp implements StampInterface
+{
+ /** @var string */
+ private $exceptionClass;
+
+ /** @var int|mixed */
+ private $exceptionCode;
+
+ /** @var string */
+ private $exceptionMessage;
+
+ /** @var FlattenException|null */
+ private $flattenException;
+
+ public function __construct(Throwable $throwable)
+ {
+ if ($throwable instanceof HandlerFailedException) {
+ $throwable = $throwable->getPrevious();
+ }
+
+ $this->exceptionClass = \get_class($throwable);
+ $this->exceptionCode = $throwable->getCode();
+ $this->exceptionMessage = $throwable->getMessage();
+
+ if (class_exists(FlattenException::class)) {
+ $this->flattenException = FlattenException::createFromThrowable($throwable);
+ }
+ }
+
+ public function getExceptionClass(): string
+ {
+ return $this->exceptionClass;
+ }
+
+ public function getExceptionCode()
+ {
+ return $this->exceptionCode;
+ }
+
+ public function getExceptionMessage(): string
+ {
+ return $this->exceptionMessage;
+ }
+
+ public function getFlattenException(): ?FlattenException
+ {
+ return $this->flattenException;
+ }
+
+ public function equals(?self $that): bool
+ {
+ if (null === $that) {
+ return false;
+ }
+
+ if ($this->flattenException && $that->flattenException) {
+ return $this->flattenException->getClass() === $that->flattenException->getClass()
+ && $this->flattenException->getCode() === $that->flattenException->getCode()
+ && $this->flattenException->getMessage() === $that->flattenException->getMessage();
+ }
+
+ return $this->exceptionClass === $that->exceptionClass
+ && $this->exceptionCode === $that->exceptionCode
+ && $this->exceptionMessage === $that->exceptionMessage;
+ }
+}
diff --git a/src/Symfony/Component/Messenger/Stamp/RedeliveryStamp.php b/src/Symfony/Component/Messenger/Stamp/RedeliveryStamp.php
index f38b96d55a751..a633e7dbef326 100644
--- a/src/Symfony/Component/Messenger/Stamp/RedeliveryStamp.php
+++ b/src/Symfony/Component/Messenger/Stamp/RedeliveryStamp.php
@@ -27,9 +27,33 @@ final class RedeliveryStamp implements StampInterface
public function __construct(int $retryCount, string $exceptionMessage = null, FlattenException $flattenException = null, \DateTimeInterface $redeliveredAt = null)
{
$this->retryCount = $retryCount;
+ $this->redeliveredAt = $redeliveredAt ?? new \DateTimeImmutable();
+
+ if (null !== $exceptionMessage) {
+ trigger_deprecation(
+ 'symfony/messenger',
+ '5.2',
+ sprintf(
+ 'Using the "$exceptionMessage" parameter in the "%s" class is deprecated, use the "%s" class instead.',
+ self::class,
+ ErrorDetailsStamp::class
+ )
+ );
+ }
$this->exceptionMessage = $exceptionMessage;
+
+ if (null !== $flattenException) {
+ trigger_deprecation(
+ 'symfony/messenger',
+ '5.2',
+ sprintf(
+ 'Using the "$flattenException" parameter in the "%s" class is deprecated, use the "%s" class instead.',
+ self::class,
+ ErrorDetailsStamp::class
+ )
+ );
+ }
$this->flattenException = $flattenException;
- $this->redeliveredAt = $redeliveredAt ?? new \DateTimeImmutable();
}
public static function getRetryCountFromEnvelope(Envelope $envelope): int
@@ -45,13 +69,39 @@ public function getRetryCount(): int
return $this->retryCount;
}
+ /**
+ * @deprecated since Symfony 5.2, use ErrorDetailsStamp instead.
+ */
public function getExceptionMessage(): ?string
{
+ trigger_deprecation(
+ 'symfony/messenger',
+ '5.2',
+ sprintf(
+ 'Using the "getExceptionMessage()" method of the "%s" class is deprecated, use the "%s" class instead.',
+ self::class,
+ ErrorDetailsStamp::class
+ )
+ );
+
return $this->exceptionMessage;
}
+ /**
+ * @deprecated since Symfony 5.2, use ErrorDetailsStamp instead.
+ */
public function getFlattenException(): ?FlattenException
{
+ trigger_deprecation(
+ 'symfony/messenger',
+ '5.2',
+ sprintf(
+ 'Using the "getFlattenException()" method of the "%s" class is deprecated, use the "%s" class instead.',
+ self::class,
+ ErrorDetailsStamp::class
+ )
+ );
+
return $this->flattenException;
}
diff --git a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php
index e6d107eeaf1d1..884d432abda51 100644
--- a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php
@@ -15,6 +15,7 @@
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Messenger\Command\FailedMessagesShowCommand;
use Symfony\Component\Messenger\Envelope;
+use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp;
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp;
use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp;
@@ -29,11 +30,13 @@ class FailedMessagesShowCommandTest extends TestCase
public function testBasicRun()
{
$sentToFailureStamp = new SentToFailureTransportStamp('async');
- $redeliveryStamp = new RedeliveryStamp(0, 'Things are bad!');
+ $redeliveryStamp = new RedeliveryStamp(0);
+ $errorStamp = new ErrorDetailsStamp(new \Exception('Things are bad!', 123));
$envelope = new Envelope(new \stdClass(), [
new TransportMessageIdStamp(15),
$sentToFailureStamp,
$redeliveryStamp,
+ $errorStamp,
]);
$receiver = $this->createMock(ListableReceiverInterface::class);
$receiver->expects($this->once())->method('find')->with(15)->willReturn($envelope);
@@ -52,8 +55,9 @@ public function testBasicRun()
Message Id 15
Failed at %s
Error Things are bad!
- Error Class (unknown)
- Transport async
+ Error Code 123
+ Error Class Exception
+ Transport async
EOF
,
$redeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s')),
@@ -63,36 +67,71 @@ public function testBasicRun()
public function testMultipleRedeliveryFails()
{
$sentToFailureStamp = new SentToFailureTransportStamp('async');
- $redeliveryStamp1 = new RedeliveryStamp(0, 'Things are bad!');
+ $redeliveryStamp1 = new RedeliveryStamp(0);
+ $errorStamp = new ErrorDetailsStamp(new \Exception('Things are bad!', 123));
$redeliveryStamp2 = new RedeliveryStamp(0);
$envelope = new Envelope(new \stdClass(), [
new TransportMessageIdStamp(15),
$sentToFailureStamp,
$redeliveryStamp1,
+ $errorStamp,
$redeliveryStamp2,
]);
$receiver = $this->createMock(ListableReceiverInterface::class);
$receiver->expects($this->once())->method('find')->with(15)->willReturn($envelope);
-
$command = new FailedMessagesShowCommand(
'failure_receiver',
$receiver
);
-
$tester = new CommandTester($command);
$tester->execute(['id' => 15]);
+ $this->assertStringContainsString(sprintf(<<getRedeliveredAt()->format('Y-m-d H:i:s')),
+ $tester->getDisplay(true));
+ }
+ /**
+ * @group legacy
+ */
+ public function testLegacyFallback()
+ {
+ $sentToFailureStamp = new SentToFailureTransportStamp('async');
+ $redeliveryStamp = new RedeliveryStamp(0, 'Things are bad!');
+ $envelope = new Envelope(new \stdClass(), [
+ new TransportMessageIdStamp(15),
+ $sentToFailureStamp,
+ $redeliveryStamp,
+ ]);
+ $receiver = $this->createMock(ListableReceiverInterface::class);
+ $receiver->expects($this->once())->method('find')->with(15)->willReturn($envelope);
+ $command = new FailedMessagesShowCommand(
+ 'failure_receiver',
+ $receiver
+ );
+ $tester = new CommandTester($command);
+ $tester->execute(['id' => 15]);
$this->assertStringContainsString(sprintf(<<getRedeliveredAt()->format('Y-m-d H:i:s')),
+ $redeliveryStamp->getRedeliveredAt()->format('Y-m-d H:i:s')),
$tester->getDisplay(true));
}
@@ -113,11 +152,13 @@ public function testReceiverShouldBeListable()
public function testListMessages()
{
$sentToFailureStamp = new SentToFailureTransportStamp('async');
- $redeliveryStamp = new RedeliveryStamp(0, 'Things are bad!');
+ $redeliveryStamp = new RedeliveryStamp(0);
+ $errorStamp = new ErrorDetailsStamp(new \RuntimeException('Things are bad!'));
$envelope = new Envelope(new \stdClass(), [
new TransportMessageIdStamp(15),
$sentToFailureStamp,
$redeliveryStamp,
+ $errorStamp,
]);
$receiver = $this->createMock(ListableReceiverInterface::class);
$receiver->expects($this->once())->method('all')->with()->willReturn([$envelope]);
@@ -130,7 +171,7 @@ public function testListMessages()
$tester = new CommandTester($command);
$tester->execute([]);
$this->assertStringContainsString(sprintf(<<getRedeliveredAt()->format('Y-m-d H:i:s')),
@@ -158,7 +199,8 @@ public function testListMessagesReturnsPaginatedMessages()
$envelope = new Envelope(new \stdClass(), [
new TransportMessageIdStamp(15),
$sentToFailureStamp,
- new RedeliveryStamp(0, 'Things are bad!'),
+ new RedeliveryStamp(0),
+ new ErrorDetailsStamp(new \RuntimeException('Things are bad!')),
]);
$receiver = $this->createMock(ListableReceiverInterface::class);
$receiver->expects($this->once())->method('all')->with()->willReturn([$envelope]);
diff --git a/src/Symfony/Component/Messenger/Tests/EventListener/AddErrorDetailsStampListenerTest.php b/src/Symfony/Component/Messenger/Tests/EventListener/AddErrorDetailsStampListenerTest.php
new file mode 100644
index 0000000000000..0fb58973071a8
--- /dev/null
+++ b/src/Symfony/Component/Messenger/Tests/EventListener/AddErrorDetailsStampListenerTest.php
@@ -0,0 +1,56 @@
+onMessageFailed($event);
+
+ $this->assertEquals($expectedStamp, $event->getEnvelope()->last(ErrorDetailsStamp::class));
+ }
+
+ public function testWorkerAddsNewErrorDetailsStampOnFailure()
+ {
+ $listener = new AddErrorDetailsStampListener();
+
+ $envelope = new Envelope(new \stdClass(), [
+ new ErrorDetailsStamp(new \InvalidArgumentException('First error!')),
+ ]);
+
+ $exception = new \Exception('Second error!');
+ $event = new WorkerMessageFailedEvent($envelope, 'my_receiver', $exception);
+ $expectedStamp = new ErrorDetailsStamp($exception);
+
+ $listener->onMessageFailed($event);
+
+ $this->assertEquals($expectedStamp, $event->getEnvelope()->last(ErrorDetailsStamp::class));
+ $this->assertCount(2, $event->getEnvelope()->all(ErrorDetailsStamp::class));
+ }
+
+ public function testWorkerDoesNotAddDuplicateErrorDetailsStampOnFailure()
+ {
+ $listener = new AddErrorDetailsStampListener();
+
+ $envelope = new Envelope(new \stdClass(), [new \Exception('It failed!')]);
+ $event = new WorkerMessageFailedEvent($envelope, 'my_receiver', new \Exception('It failed!'));
+
+ $listener->onMessageFailed($event);
+
+ $this->assertCount(1, $event->getEnvelope()->all(ErrorDetailsStamp::class));
+ }
+}
diff --git a/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageToFailureTransportListenerTest.php b/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageToFailureTransportListenerTest.php
index 1f648b83e1e35..c489ac45e33c4 100644
--- a/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageToFailureTransportListenerTest.php
+++ b/src/Symfony/Component/Messenger/Tests/EventListener/SendFailedMessageToFailureTransportListenerTest.php
@@ -15,8 +15,6 @@
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
use Symfony\Component\Messenger\EventListener\SendFailedMessageToFailureTransportListener;
-use Symfony\Component\Messenger\Exception\HandlerFailedException;
-use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp;
use Symfony\Component\Messenger\Transport\Sender\SenderInterface;
@@ -34,11 +32,6 @@ public function testItSendsToTheFailureTransport()
$this->assertNotNull($sentToFailureTransportStamp);
$this->assertSame('my_receiver', $sentToFailureTransportStamp->getOriginalReceiverName());
- /** @var RedeliveryStamp $redeliveryStamp */
- $redeliveryStamp = $envelope->last(RedeliveryStamp::class);
- $this->assertSame('no!', $redeliveryStamp->getExceptionMessage());
- $this->assertSame('no!', $redeliveryStamp->getFlattenException()->getMessage());
-
return true;
}))->willReturnArgument(0);
$listener = new SendFailedMessageToFailureTransportListener($sender);
@@ -50,30 +43,6 @@ public function testItSendsToTheFailureTransport()
$listener->onMessageFailed($event);
}
- public function testItGetsNestedHandlerFailedException()
- {
- $sender = $this->createMock(SenderInterface::class);
- $sender->expects($this->once())->method('send')->with($this->callback(function ($envelope) {
- /** @var Envelope $envelope */
- /** @var RedeliveryStamp $redeliveryStamp */
- $redeliveryStamp = $envelope->last(RedeliveryStamp::class);
- $this->assertNotNull($redeliveryStamp);
- $this->assertSame('I am inside!', $redeliveryStamp->getExceptionMessage());
- $this->assertSame('Exception', $redeliveryStamp->getFlattenException()->getClass());
-
- return true;
- }))->willReturnArgument(0);
-
- $listener = new SendFailedMessageToFailureTransportListener($sender);
-
- $envelope = new Envelope(new \stdClass());
- $exception = new \Exception('I am inside!');
- $exception = new HandlerFailedException($envelope, [$exception]);
- $event = new WorkerMessageFailedEvent($envelope, 'my_receiver', $exception);
-
- $listener->onMessageFailed($event);
- }
-
public function testDoNothingOnRetry()
{
$sender = $this->createMock(SenderInterface::class);
diff --git a/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php b/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php
index 3ea7602d5237d..88e32dd845ea2 100644
--- a/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php
+++ b/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php
@@ -16,6 +16,7 @@
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
+use Symfony\Component\Messenger\EventListener\AddErrorDetailsStampListener;
use Symfony\Component\Messenger\EventListener\SendFailedMessageForRetryListener;
use Symfony\Component\Messenger\EventListener\SendFailedMessageToFailureTransportListener;
use Symfony\Component\Messenger\EventListener\StopWorkerOnMessageLimitListener;
@@ -27,7 +28,7 @@
use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware;
use Symfony\Component\Messenger\Middleware\SendMessageMiddleware;
use Symfony\Component\Messenger\Retry\MultiplierRetryStrategy;
-use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
+use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp;
use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp;
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface;
@@ -37,7 +38,7 @@
class FailureIntegrationTest extends TestCase
{
- public function testRequeMechanism()
+ public function testRequeueMechanism()
{
$transport1 = new DummyFailureTestSenderAndReceiver();
$transport2 = new DummyFailureTestSenderAndReceiver();
@@ -96,6 +97,7 @@ public function testRequeMechanism()
new SendMessageMiddleware($senderLocator),
new HandleMessageMiddleware($handlerLocator),
]);
+ $dispatcher->addSubscriber(new AddErrorDetailsStampListener());
$dispatcher->addSubscriber(new SendFailedMessageForRetryListener($locator, $retryStrategyLocator));
$dispatcher->addSubscriber(new SendFailedMessageToFailureTransportListener($failureTransport));
$dispatcher->addSubscriber(new StopWorkerOnMessageLimitListener(1));
@@ -156,10 +158,10 @@ public function testRequeMechanism()
/** @var SentToFailureTransportStamp $sentToFailureStamp */
$sentToFailureStamp = $failedEnvelope->last(SentToFailureTransportStamp::class);
$this->assertNotNull($sentToFailureStamp);
- /** @var RedeliveryStamp $redeliveryStamp */
- $redeliveryStamp = $failedEnvelope->last(RedeliveryStamp::class);
- $this->assertNotNull($redeliveryStamp);
- $this->assertSame('Failure from call 2', $redeliveryStamp->getExceptionMessage());
+ /** @var ErrorDetailsStamp $errorDetailsStamp */
+ $errorDetailsStamp = $failedEnvelope->last(ErrorDetailsStamp::class);
+ $this->assertNotNull($errorDetailsStamp);
+ $this->assertSame('Failure from call 2', $errorDetailsStamp->getExceptionMessage());
/*
* Failed message is handled, fails, and sent for a retry
diff --git a/src/Symfony/Component/Messenger/Tests/Stamp/ErrorDetailsStampTest.php b/src/Symfony/Component/Messenger/Tests/Stamp/ErrorDetailsStampTest.php
new file mode 100644
index 0000000000000..c6db1de147fe7
--- /dev/null
+++ b/src/Symfony/Component/Messenger/Tests/Stamp/ErrorDetailsStampTest.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Messenger\Tests\Stamp;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\ErrorHandler\Exception\FlattenException;
+use Symfony\Component\Messenger\Envelope;
+use Symfony\Component\Messenger\Exception\HandlerFailedException;
+use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp;
+
+class ErrorDetailsStampTest extends TestCase
+{
+ public function testGetters(): void
+ {
+ $exception = new \Exception('exception message');
+ $flattenException = FlattenException::createFromThrowable($exception);
+
+ $stamp = new ErrorDetailsStamp($exception);
+
+ $this->assertSame(\Exception::class, $stamp->getExceptionClass());
+ $this->assertSame('exception message', $stamp->getExceptionMessage());
+ $this->assertEquals($flattenException, $stamp->getFlattenException());
+ }
+
+ public function testUnwrappingHandlerFailedException(): void
+ {
+ $wrappedException = new \Exception('I am inside', 123);
+ $envelope = new Envelope(new \stdClass());
+ $exception = new HandlerFailedException($envelope, [$wrappedException]);
+ $flattenException = FlattenException::createFromThrowable($wrappedException);
+
+ $stamp = new ErrorDetailsStamp($exception);
+
+ $this->assertSame(\Exception::class, $stamp->getExceptionClass());
+ $this->assertSame('I am inside', $stamp->getExceptionMessage());
+ $this->assertSame(123, $stamp->getExceptionCode());
+ $this->assertEquals($flattenException, $stamp->getFlattenException());
+ }
+}
diff --git a/src/Symfony/Component/Messenger/Tests/Stamp/RedeliveryStampTest.php b/src/Symfony/Component/Messenger/Tests/Stamp/RedeliveryStampTest.php
index f8a2175e2bd8c..fa01b64f9da10 100644
--- a/src/Symfony/Component/Messenger/Tests/Stamp/RedeliveryStampTest.php
+++ b/src/Symfony/Component/Messenger/Tests/Stamp/RedeliveryStampTest.php
@@ -12,7 +12,6 @@
namespace Symfony\Component\Messenger\Tests\Stamp;
use PHPUnit\Framework\TestCase;
-use Symfony\Component\ErrorHandler\Exception\FlattenException;
use Symfony\Component\Messenger\Stamp\RedeliveryStamp;
class RedeliveryStampTest extends TestCase
@@ -22,16 +21,6 @@ public function testGetters()
$stamp = new RedeliveryStamp(10);
$this->assertSame(10, $stamp->getRetryCount());
$this->assertInstanceOf(\DateTimeInterface::class, $stamp->getRedeliveredAt());
- $this->assertNull($stamp->getExceptionMessage());
- $this->assertNull($stamp->getFlattenException());
- }
-
- public function testGettersPopulated()
- {
- $flattenException = new FlattenException();
- $stamp = new RedeliveryStamp(10, 'exception message', $flattenException);
- $this->assertSame('exception message', $stamp->getExceptionMessage());
- $this->assertSame($flattenException, $stamp->getFlattenException());
}
public function testSerialization()
diff --git a/src/Symfony/Component/Messenger/Worker.php b/src/Symfony/Component/Messenger/Worker.php
index a1cca6b8023cc..6f8d34b1cb2cf 100644
--- a/src/Symfony/Component/Messenger/Worker.php
+++ b/src/Symfony/Component/Messenger/Worker.php
@@ -104,6 +104,7 @@ private function handleMessage(Envelope $envelope, ReceiverInterface $receiver,
{
$event = new WorkerMessageReceivedEvent($envelope, $transportName);
$this->dispatchEvent($event);
+ $envelope = $event->getEnvelope();
if (!$event->shouldHandle()) {
return;
@@ -123,7 +124,9 @@ private function handleMessage(Envelope $envelope, ReceiverInterface $receiver,
$envelope = $throwable->getEnvelope();
}
- $this->dispatchEvent(new WorkerMessageFailedEvent($envelope, $transportName, $throwable));
+ $failedEvent = new WorkerMessageFailedEvent($envelope, $transportName, $throwable);
+ $this->dispatchEvent($failedEvent);
+ $envelope = $failedEvent->getEnvelope();
if (!$rejectFirst) {
$receiver->reject($envelope);
@@ -132,7 +135,9 @@ private function handleMessage(Envelope $envelope, ReceiverInterface $receiver,
return;
}
- $this->dispatchEvent(new WorkerMessageHandledEvent($envelope, $transportName));
+ $handledEvent = new WorkerMessageHandledEvent($envelope, $transportName);
+ $this->dispatchEvent($handledEvent);
+ $envelope = $handledEvent->getEnvelope();
if (null !== $this->logger) {
$message = $envelope->getMessage();