Skip to content

Commit

Permalink
[Messenger] Added FailedMessageErrorDetailsStamp
Browse files Browse the repository at this point in the history
  • Loading branch information
TimoBakx committed Nov 23, 2019
1 parent a8a9e69 commit 301734a
Show file tree
Hide file tree
Showing 13 changed files with 383 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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())],
Expand All @@ -71,14 +76,34 @@ 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) {
$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()],
]);
}
Expand All @@ -98,6 +123,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.');
Expand All @@ -122,13 +153,25 @@ protected function getReceiver(): ReceiverInterface

protected function getLastRedeliveryStampWithException(Envelope $envelope): ?RedeliveryStamp
{
// Use ErrorDetailsStamp instead if it is available
if ($envelope->last(ErrorDetailsStamp::class)) {
return null;
}

if (null === \func_get_args()[1]) {
@trigger_error(sprintf(
'Using the "getLastRedeliveryStampWithException" method in the "%s" class is deprecated, use the "Envelope::last(%s)" instead.',
self::class,
ErrorDetailsStamp::class
), E_USER_DEPRECATED);
}

/** @var RedeliveryStamp $stamp */
foreach (array_reverse($envelope->all(RedeliveryStamp::class)) as $stamp) {
if (null !== $stamp->getExceptionMessage()) {
return $stamp;
}
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -82,13 +84,16 @@ 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);

$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'),
null === $lastErrorDetailsStamp ? '' : $lastErrorDetailsStamp->getExceptionMessage(),
];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Messenger\Event;

use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Stamp\StampInterface;

abstract class AbstractWorkerMessageEvent
{
Expand All @@ -36,4 +37,12 @@ public function getReceiverName(): string
{
return $this->receiverName;
}

/**
* Add stamps to the envelope.
*/
public function addStamps(StampInterface ...$stamps): void
{
$this->envelope = $this->envelope->with(...$stamps);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?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
{
/** @var ErrorDetailsStamp|null $previousStamp */
$previousStamp = $event->getEnvelope()->last(ErrorDetailsStamp::class);
$stamp = new ErrorDetailsStamp($event->getThrowable());

// 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
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
82 changes: 82 additions & 0 deletions src/Symfony/Component/Messenger/Stamp/ErrorDetailsStamp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?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();
}

$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 ($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;
}
}
42 changes: 38 additions & 4 deletions src/Symfony/Component/Messenger/Stamp/RedeliveryStamp.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,30 @@ final class RedeliveryStamp implements StampInterface
private $retryCount;
private $redeliveredAt;
private $exceptionMessage;
private $flattenException;
private $flattenedException;

public function __construct(int $retryCount, string $exceptionMessage = null, FlattenException $flattenException = null)
{
$this->retryCount = $retryCount;
$this->exceptionMessage = $exceptionMessage;
$this->flattenException = $flattenException;
$this->redeliveredAt = new \DateTimeImmutable();

if (null !== $exceptionMessage) {
@trigger_error(sprintf(
'Using the "$exceptionMessage" parameter in the "%s" class is deprecated, use the "%s" class instead.',
self::class,
ErrorDetailsStamp::class
), E_USER_DEPRECATED);
}
$this->exceptionMessage = $exceptionMessage;

if (null !== $flattenException) {
@trigger_error(sprintf(
'Using the "$flattenException" parameter in the "%s" class is deprecated, use the "%s" class instead.',
self::class,
ErrorDetailsStamp::class
), E_USER_DEPRECATED);
}
$this->flattenedException = $flattenException;
}

public static function getRetryCountFromEnvelope(Envelope $envelope): int
Expand All @@ -45,14 +61,32 @@ public function getRetryCount(): int
return $this->retryCount;
}

/**
* @deprecated since Symfony 5.1, use ErrorDetailsStamp instead.
*/
public function getExceptionMessage(): ?string
{
@trigger_error(sprintf(
'Using the "getExceptionMessage()" method of the "%s" class is deprecated, use the "%s" class instead.',
self::class,
ErrorDetailsStamp::class
), E_USER_DEPRECATED);

return $this->exceptionMessage;
}

/**
* @deprecated since Symfony 5.1, use ErrorDetailsStamp instead.
*/
public function getFlattenException(): ?FlattenException
{
return $this->flattenException;
@trigger_error(sprintf(
'Using the "getFlattenException()" method of the "%s" class is deprecated, use the "%s" class instead.',
self::class,
ErrorDetailsStamp::class
), E_USER_DEPRECATED);

return $this->flattenedException;
}

public function getRedeliveredAt(): \DateTimeInterface
Expand Down
Loading

0 comments on commit 301734a

Please sign in to comment.