vendor/sentry/sentry-symfony/src/EventListener/MessengerListener.php line 49

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Sentry\SentryBundle\EventListener;
  4. use Sentry\Event;
  5. use Sentry\EventHint;
  6. use Sentry\ExceptionMechanism;
  7. use Sentry\State\HubInterface;
  8. use Sentry\State\Scope;
  9. use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
  10. use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent;
  11. use Symfony\Component\Messenger\Exception\DelayedMessageHandlingException;
  12. use Symfony\Component\Messenger\Exception\HandlerFailedException;
  13. use Symfony\Component\Messenger\Exception\WrappedExceptionsInterface;
  14. use Symfony\Component\Messenger\Stamp\BusNameStamp;
  15. final class MessengerListener
  16. {
  17. /**
  18. * @var HubInterface The current hub
  19. */
  20. private $hub;
  21. /**
  22. * @var bool Whether to capture errors thrown while processing a message that
  23. * will be retried
  24. */
  25. private $captureSoftFails;
  26. /**
  27. * @param HubInterface $hub The current hub
  28. * @param bool $captureSoftFails Whether to capture errors thrown
  29. * while processing a message that
  30. * will be retried
  31. */
  32. public function __construct(HubInterface $hub, bool $captureSoftFails = true)
  33. {
  34. $this->hub = $hub;
  35. $this->captureSoftFails = $captureSoftFails;
  36. }
  37. /**
  38. * This method is called for each message that failed to be handled.
  39. *
  40. * @param WorkerMessageFailedEvent $event The event
  41. */
  42. public function handleWorkerMessageFailedEvent(WorkerMessageFailedEvent $event): void
  43. {
  44. if (!$this->captureSoftFails && $event->willRetry()) {
  45. return;
  46. }
  47. $this->hub->withScope(function (Scope $scope) use ($event): void {
  48. $envelope = $event->getEnvelope();
  49. $exception = $event->getThrowable();
  50. $scope->setTag('messenger.receiver_name', $event->getReceiverName());
  51. $scope->setTag('messenger.message_class', \get_class($envelope->getMessage()));
  52. /** @var BusNameStamp|null $messageBusStamp */
  53. $messageBusStamp = $envelope->last(BusNameStamp::class);
  54. if (null !== $messageBusStamp) {
  55. $scope->setTag('messenger.message_bus', $messageBusStamp->getBusName());
  56. }
  57. $this->captureException($exception, $event->willRetry());
  58. });
  59. $this->flushClient();
  60. }
  61. /**
  62. * This method is called for each handled message.
  63. *
  64. * @param WorkerMessageHandledEvent $event The event
  65. */
  66. public function handleWorkerMessageHandledEvent(WorkerMessageHandledEvent $event): void
  67. {
  68. // Flush normally happens at shutdown... which only happens in the worker if it is run with a lifecycle limit
  69. // such as --time=X or --limit=Y. Flush immediately in a background worker.
  70. $this->flushClient();
  71. }
  72. /**
  73. * Creates Sentry events from the given exception.
  74. *
  75. * Unpacks multiple exceptions wrapped in a HandlerFailedException and notifies
  76. * Sentry of each individual exception.
  77. *
  78. * If the message will be retried the exceptions will be marked as handled
  79. * in Sentry.
  80. */
  81. private function captureException(\Throwable $exception, bool $willRetry): void
  82. {
  83. if ($exception instanceof WrappedExceptionsInterface) {
  84. $exception = $exception->getWrappedExceptions();
  85. } elseif ($exception instanceof HandlerFailedException && method_exists($exception, 'getNestedExceptions')) {
  86. $exception = $exception->getNestedExceptions();
  87. } elseif ($exception instanceof DelayedMessageHandlingException && method_exists($exception, 'getExceptions')) {
  88. $exception = $exception->getExceptions();
  89. }
  90. if (\is_array($exception)) {
  91. foreach ($exception as $nestedException) {
  92. $this->captureException($nestedException, $willRetry);
  93. }
  94. return;
  95. }
  96. $hint = EventHint::fromArray([
  97. 'exception' => $exception,
  98. 'mechanism' => new ExceptionMechanism(ExceptionMechanism::TYPE_GENERIC, $willRetry),
  99. ]);
  100. $this->hub->captureEvent(Event::createEvent(), $hint);
  101. }
  102. private function flushClient(): void
  103. {
  104. $client = $this->hub->getClient();
  105. if (null !== $client) {
  106. $client->flush();
  107. }
  108. }
  109. }