-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Messenger] Added ErrorDetailsStamp #32904
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 <info>-vv</info> 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]) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this method only has one argument. isn't that accessing a non-existing argument which will result in a php warning? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As discussed on Slack, I'll add a new PR in which I replace this check (that causes a warning if |
||
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()) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* 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], | ||
]; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* 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(); | ||
TimoBakx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
$this->exceptionClass = \get_class($throwable); | ||
$this->exceptionCode = $throwable->getCode(); | ||
$this->exceptionMessage = $throwable->getMessage(); | ||
|
||
if (class_exists(FlattenException::class)) { | ||
$this->flattenException = FlattenException::createFromThrowable($throwable); | ||
TimoBakx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
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; | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.