From 3ed616fc7c626566178ef8f8342b8c127999ee13 Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Mon, 8 Sep 2025 09:21:05 +0200 Subject: [PATCH 1/6] feat(breadcrumbs): add flag to reset breadcrumbs between messages --- src/DependencyInjection/Configuration.php | 1 + src/DependencyInjection/SentryExtension.php | 2 +- src/EventListener/MessengerListener.php | 71 +++++++++++---- src/Resources/config/schema/sentry-1.0.xsd | 1 + src/Resources/config/services.xml | 1 + .../DependencyInjection/ConfigurationTest.php | 1 + .../DependencyInjection/Fixtures/php/full.php | 1 + .../DependencyInjection/Fixtures/xml/full.xml | 2 +- .../DependencyInjection/Fixtures/yml/full.yml | 1 + .../SentryExtensionTest.php | 7 ++ .../App/Controller/MessengerController.php | 10 +- .../App/Messenger/FooMessageHandler.php | 15 ++- tests/End2End/App/messenger.yml | 14 +++ tests/End2End/End2EndTest.php | 26 ++++++ tests/EventListener/MessengerListenerTest.php | 91 ++++++++++++++++++- 15 files changed, 222 insertions(+), 22 deletions(-) diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 7a3c9a39..8defe37c 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -187,6 +187,7 @@ private function addMessengerSection(ArrayNodeDefinition $rootNode): void ->{interface_exists(MessageBusInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}() ->children() ->booleanNode('capture_soft_fails')->defaultTrue()->end() + ->booleanNode('reset_breadcrumbs')->defaultFalse()->end() ->end() ->end() ->end(); diff --git a/src/DependencyInjection/SentryExtension.php b/src/DependencyInjection/SentryExtension.php index a127069b..c7a52481 100644 --- a/src/DependencyInjection/SentryExtension.php +++ b/src/DependencyInjection/SentryExtension.php @@ -200,7 +200,7 @@ private function registerMessengerListenerConfiguration(ContainerBuilder $contai return; } - $container->getDefinition(MessengerListener::class)->setArgument(1, $config['capture_soft_fails']); + $container->getDefinition(MessengerListener::class)->setArgument(1, $config['capture_soft_fails'])->setArgument(2, $config['reset_breadcrumbs']); } /** diff --git a/src/EventListener/MessengerListener.php b/src/EventListener/MessengerListener.php index b66b962c..6a36c1f9 100644 --- a/src/EventListener/MessengerListener.php +++ b/src/EventListener/MessengerListener.php @@ -11,6 +11,7 @@ use Sentry\State\Scope; use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent; use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent; +use Symfony\Component\Messenger\Event\WorkerMessageReceivedEvent; use Symfony\Component\Messenger\Exception\DelayedMessageHandlingException; use Symfony\Component\Messenger\Exception\HandlerFailedException; use Symfony\Component\Messenger\Exception\WrappedExceptionsInterface; @@ -29,16 +30,26 @@ final class MessengerListener */ private $captureSoftFails; + /** + * @var bool When this is enabled, a new scope is pushed on the stack when a message + * is received and will pop it again after the message was finished (either success or fail). + * This allows us to have breadcrumbs only for one message and no breadcrumb is leaked into + * future messages. + */ + private $resetBreadcrumbs; + /** * @param HubInterface $hub The current hub * @param bool $captureSoftFails Whether to capture errors thrown * while processing a message that * will be retried + * @param bool $resetBreadcrumbs Whether to reset all breadcrumbs after a message */ - public function __construct(HubInterface $hub, bool $captureSoftFails = true) + public function __construct(HubInterface $hub, bool $captureSoftFails = true, bool $resetBreadcrumbs = false) { $this->hub = $hub; $this->captureSoftFails = $captureSoftFails; + $this->resetBreadcrumbs = $resetBreadcrumbs; } /** @@ -48,28 +59,36 @@ public function __construct(HubInterface $hub, bool $captureSoftFails = true) */ public function handleWorkerMessageFailedEvent(WorkerMessageFailedEvent $event): void { - if (!$this->captureSoftFails && $event->willRetry()) { - return; - } + try { + if (!$this->captureSoftFails && $event->willRetry()) { + return; + } - $this->hub->withScope(function (Scope $scope) use ($event): void { - $envelope = $event->getEnvelope(); - $exception = $event->getThrowable(); + $this->hub->withScope(function (Scope $scope) use ($event): void { + $envelope = $event->getEnvelope(); + $exception = $event->getThrowable(); - $scope->setTag('messenger.receiver_name', $event->getReceiverName()); - $scope->setTag('messenger.message_class', \get_class($envelope->getMessage())); + $scope->setTag('messenger.receiver_name', $event->getReceiverName()); + $scope->setTag('messenger.message_class', \get_class($envelope->getMessage())); - /** @var BusNameStamp|null $messageBusStamp */ - $messageBusStamp = $envelope->last(BusNameStamp::class); + /** @var BusNameStamp|null $messageBusStamp */ + $messageBusStamp = $envelope->last(BusNameStamp::class); - if (null !== $messageBusStamp) { - $scope->setTag('messenger.message_bus', $messageBusStamp->getBusName()); - } + if (null !== $messageBusStamp) { + $scope->setTag('messenger.message_bus', $messageBusStamp->getBusName()); + } - $this->captureException($exception, $event->willRetry()); - }); + $this->captureException($exception, $event->willRetry()); + }); - $this->flushClient(); + $this->flushClient(); + } finally { + // We always want to pop the scope at the end of this method to add the breadcrumbs + // to any potential event that is produced. + if ($this->resetBreadcrumbs) { + $this->hub->popScope(); + } + } } /** @@ -82,6 +101,24 @@ public function handleWorkerMessageHandledEvent(WorkerMessageHandledEvent $event // Flush normally happens at shutdown... which only happens in the worker if it is run with a lifecycle limit // such as --time=X or --limit=Y. Flush immediately in a background worker. $this->flushClient(); + if ($this->resetBreadcrumbs) { + $this->hub->popScope(); + } + } + + /** + * Method that will push a new scope on the hub to create message local breadcrumbs that will not + * "leak" into future messages. + * + * @param WorkerMessageReceivedEvent $event + * + * @return void + */ + public function handleWorkerMessageReceivedEvent(WorkerMessageReceivedEvent $event): void + { + if ($this->resetBreadcrumbs) { + $this->hub->pushScope(); + } } /** diff --git a/src/Resources/config/schema/sentry-1.0.xsd b/src/Resources/config/schema/sentry-1.0.xsd index d8c81294..34fa7cc3 100644 --- a/src/Resources/config/schema/sentry-1.0.xsd +++ b/src/Resources/config/schema/sentry-1.0.xsd @@ -101,6 +101,7 @@ + diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml index e4b916df..dae3e7df 100644 --- a/src/Resources/config/services.xml +++ b/src/Resources/config/services.xml @@ -76,6 +76,7 @@ + diff --git a/tests/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php index c993f6f2..6d3a501e 100644 --- a/tests/DependencyInjection/ConfigurationTest.php +++ b/tests/DependencyInjection/ConfigurationTest.php @@ -42,6 +42,7 @@ public function testProcessConfigurationWithDefaultConfiguration(): void 'messenger' => [ 'enabled' => interface_exists(MessageBusInterface::class), 'capture_soft_fails' => true, + 'reset_breadcrumbs' => false, ], 'tracing' => [ 'enabled' => true, diff --git a/tests/DependencyInjection/Fixtures/php/full.php b/tests/DependencyInjection/Fixtures/php/full.php index 973b125c..31ae0060 100644 --- a/tests/DependencyInjection/Fixtures/php/full.php +++ b/tests/DependencyInjection/Fixtures/php/full.php @@ -60,6 +60,7 @@ 'messenger' => [ 'enabled' => true, 'capture_soft_fails' => false, + 'reset_breadcrumbs' => true, ], 'tracing' => [ 'dbal' => [ diff --git a/tests/DependencyInjection/Fixtures/xml/full.xml b/tests/DependencyInjection/Fixtures/xml/full.xml index ddf466a2..744f0a92 100644 --- a/tests/DependencyInjection/Fixtures/xml/full.xml +++ b/tests/DependencyInjection/Fixtures/xml/full.xml @@ -57,7 +57,7 @@ Symfony\Component\HttpKernel\Exception\BadRequestHttpException GET tracing_ignored_transaction - + default diff --git a/tests/DependencyInjection/Fixtures/yml/full.yml b/tests/DependencyInjection/Fixtures/yml/full.yml index e9097964..8f45b2dd 100644 --- a/tests/DependencyInjection/Fixtures/yml/full.yml +++ b/tests/DependencyInjection/Fixtures/yml/full.yml @@ -59,6 +59,7 @@ sentry: messenger: enabled: true capture_soft_fails: false + reset_breadcrumbs: true tracing: dbal: enabled: false diff --git a/tests/DependencyInjection/SentryExtensionTest.php b/tests/DependencyInjection/SentryExtensionTest.php index ce7e4234..0d64d77b 100644 --- a/tests/DependencyInjection/SentryExtensionTest.php +++ b/tests/DependencyInjection/SentryExtensionTest.php @@ -41,6 +41,7 @@ use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent; use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent; +use Symfony\Component\Messenger\Event\WorkerMessageReceivedEvent; use Symfony\Component\Messenger\MessageBusInterface; abstract class SentryExtensionTest extends TestCase @@ -138,10 +139,16 @@ public function testMessengerListener(): void 'method' => 'handleWorkerMessageHandledEvent', 'priority' => 50, ], + [ + 'event' => WorkerMessageReceivedEvent::class, + 'method' => 'handleWorkerMessageReceivedEvent', + 'priority' => 50, + ], ], ], $definition->getTags()); $this->assertFalse($definition->getArgument(1)); + $this->assertTrue($definition->getArgument(2)); } public function testMessengerListenerIsRemovedWhenDisabled(): void diff --git a/tests/End2End/App/Controller/MessengerController.php b/tests/End2End/App/Controller/MessengerController.php index 46afffec..9c7f5626 100644 --- a/tests/End2End/App/Controller/MessengerController.php +++ b/tests/End2End/App/Controller/MessengerController.php @@ -4,6 +4,7 @@ namespace Sentry\SentryBundle\Tests\End2End\App\Controller; +use Psr\Log\LoggerInterface; use Sentry\SentryBundle\Tests\End2End\App\Messenger\FooMessage; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Messenger\MessageBusInterface; @@ -15,9 +16,15 @@ class MessengerController */ private $messenger; - public function __construct(MessageBusInterface $messenger) + /** + * @var LoggerInterface + */ + private $logger; + + public function __construct(MessageBusInterface $messenger, LoggerInterface $logger) { $this->messenger = $messenger; + $this->logger = $logger; } public function dispatchMessage(): Response @@ -29,6 +36,7 @@ public function dispatchMessage(): Response public function dispatchUnrecoverableMessage(): Response { + $this->logger->warning('Dispatch FooMessage'); $this->messenger->dispatch(new FooMessage(false)); return new Response('Success'); diff --git a/tests/End2End/App/Messenger/FooMessageHandler.php b/tests/End2End/App/Messenger/FooMessageHandler.php index 28518d96..2e0db467 100644 --- a/tests/End2End/App/Messenger/FooMessageHandler.php +++ b/tests/End2End/App/Messenger/FooMessageHandler.php @@ -4,14 +4,27 @@ namespace Sentry\SentryBundle\Tests\End2End\App\Messenger; +use Psr\Log\LoggerInterface; use Symfony\Component\Messenger\Exception\UnrecoverableExceptionInterface; class FooMessageHandler { + /** + * @var LoggerInterface + */ + private $logger; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + public function __invoke(FooMessage $message): void { + $this->logger->warning('Handle FooMessage'); if (!$message->shouldRetry()) { - throw new class extends \Exception implements UnrecoverableExceptionInterface { }; + throw new class extends \Exception implements UnrecoverableExceptionInterface { + }; } throw new \Exception('This is an intentional failure while handling a message of class ' . \get_class($message)); diff --git a/tests/End2End/App/messenger.yml b/tests/End2End/App/messenger.yml index c729467d..aeffdeaa 100644 --- a/tests/End2End/App/messenger.yml +++ b/tests/End2End/App/messenger.yml @@ -6,6 +6,7 @@ services: class: \Sentry\SentryBundle\Tests\End2End\App\Messenger\StaticInMemoryTransportFactory Sentry\SentryBundle\Tests\End2End\App\Messenger\FooMessageHandler: + autowire: true class: \Sentry\SentryBundle\Tests\End2End\App\Messenger\FooMessageHandler tags: - { name: messenger.message_handler } @@ -15,6 +16,18 @@ services: tags: - controller.service_arguments + Sentry\Monolog\BreadcrumbHandler: + arguments: + - '@Sentry\State\HubInterface' + - !php/const Monolog\Logger::WARNING + +monolog: + handlers: + sentry_breadcrumbs: + type: service + name: sentry_breadcrumbs + id: Sentry\Monolog\BreadcrumbHandler + framework: messenger: transports: @@ -28,3 +41,4 @@ framework: sentry: messenger: capture_soft_fails: false + reset_breadcrumbs: true diff --git a/tests/End2End/End2EndTest.php b/tests/End2End/End2EndTest.php index 19ddfe06..04b511e7 100644 --- a/tests/End2End/End2EndTest.php +++ b/tests/End2End/End2EndTest.php @@ -38,6 +38,8 @@ protected function setUp(): void { parent::setUp(); + StubTransport::$events = []; + file_put_contents(self::SENT_EVENTS_LOG, ''); } @@ -250,6 +252,30 @@ public function testMessengerCaptureSoftFailCanBeDisabled(): void $this->assertLastEventIdIsNull($client); } + public function testResetBreadcrumbs() + { + $this->skipIfMessengerIsMissing(); + + $client = static::createClient(); + + // Create two messages + $client->request('GET', '/dispatch-unrecoverable-message'); + $this->assertSame(200, $client->getResponse()->getStatusCode()); + + $client->request('GET', '/dispatch-unrecoverable-message'); + $this->assertSame(200, $client->getResponse()->getStatusCode()); + + $this->consumeOneMessage($client->getKernel()); + $this->consumeOneMessage($client->getKernel()); + + $events = StubTransport::$events; + $this->assertCount(2, $events); + + // Asser that both have the same number of breadcrumbs meaning that each event has isolated breadcrumbs + $this->assertCount(4, $events[0]->getBreadcrumbs()); + $this->assertCount(4, $events[1]->getBreadcrumbs()); + } + private function consumeOneMessage(KernelInterface $kernel): void { $application = new Application($kernel); diff --git a/tests/EventListener/MessengerListenerTest.php b/tests/EventListener/MessengerListenerTest.php index 8e850aaf..6beb4f43 100644 --- a/tests/EventListener/MessengerListenerTest.php +++ b/tests/EventListener/MessengerListenerTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent; use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent; +use Symfony\Component\Messenger\Event\WorkerMessageReceivedEvent; use Symfony\Component\Messenger\Exception\DelayedMessageHandlingException; use Symfony\Component\Messenger\Exception\HandlerFailedException; use Symfony\Component\Messenger\MessageBusInterface; @@ -116,7 +117,7 @@ public function handleWorkerMessageFailedEventDataProvider(): \Generator yield 'envelope.throwable INSTANCEOF DelayedMessageHandlingException' => [ $exceptions, - $this->getMessageFailedEvent($envelope, 'receiver', new DelayedMessageHandlingException($exceptions, $envelope), false), + $this->getMessageFailedEvent($envelope, 'receiver', new DelayedMessageHandlingException($exceptions), false), [ 'messenger.receiver_name' => 'receiver', 'messenger.message_class' => \get_class($envelope->getMessage()), @@ -238,6 +239,94 @@ public function testHandleWorkerMessageHandledEvent(): void $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent(Envelope::wrap((object) []), 'receiver')); } + public function testResetBreadcrumbsPushAndPopScopeWhenEnabled(): void + { + if (!$this->supportsMessenger()) { + $this->markTestSkipped('Messenger not supported in this environment.'); + } + + // Received event should push + $this->hub->expects($this->once()) + ->method('pushScope'); + + // Handled event should pop + $this->hub->expects($this->once()) + ->method('popScope'); + + $listener = new MessengerListener($this->hub, true, true); + + $envelope = Envelope::wrap((object) []); + $listener->handleWorkerMessageReceivedEvent($this->createWorkerMessageReceivedEvent($envelope, 'receiver')); + $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent($envelope, 'receiver')); + } + + public function testResetBreadcrumbsDoesNotPushOrPopWhenDisabled(): void + { + if (!$this->supportsMessenger()) { + $this->markTestSkipped('Messenger not supported in this environment.'); + } + + $this->hub->expects($this->never()) + ->method('pushScope'); + + $this->hub->expects($this->never()) + ->method('popScope'); + + $listener = new MessengerListener($this->hub, true, false); + + $envelope = Envelope::wrap((object) []); + $listener->handleWorkerMessageReceivedEvent($this->createWorkerMessageReceivedEvent($envelope, 'receiver')); + $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent($envelope, 'receiver')); + } + + public function testResetBreadcrumbsPopsAfterFailureWhenEnabled(): void + { + if (!$this->supportsMessenger()) { + $this->markTestSkipped('Messenger not supported in this environment.'); + } + + // Received event should push, failure should pop + $this->hub->expects($this->once()) + ->method('pushScope'); + + // The failure handler pops in finally + $this->hub->expects($this->once()) + ->method('popScope'); + + // Also expect captureEvent to be called once + $this->hub->expects($this->once()) + ->method('withScope') + ->willReturnCallback(function (callable $callback): void { + $callback(new Scope()); + }); + + $this->hub->expects($this->any()) + ->method('getClient') + ->willReturn($this->client); + + $listener = new MessengerListener($this->hub, true, true); + $envelope = Envelope::wrap((object) []); + + $listener->handleWorkerMessageReceivedEvent($this->createWorkerMessageReceivedEvent($envelope, 'receiver')); + + $event = $this->getMessageFailedEvent($envelope, 'receiver', new \Exception('boom'), false); + $listener->handleWorkerMessageFailedEvent($event); + } + + private function createWorkerMessageReceivedEvent(Envelope $envelope, string $receiverName) + { + $x = new WorkerMessageReceivedEvent($envelope, $receiverName); + $class = WorkerMessageReceivedEvent::class; + $refClass = new \ReflectionClass($class); + $ctor = $refClass->getConstructor(); + + if (null !== $ctor && $ctor->getNumberOfParameters() >= 2) { + return $refClass->newInstanceArgs([$envelope, $receiverName]); + } + + return $refClass->newInstanceArgs([$envelope]); + } + private function getMessageFailedEvent(Envelope $envelope, string $receiverName, \Throwable $error, bool $retry): WorkerMessageFailedEvent { $event = new WorkerMessageFailedEvent($envelope, $receiverName, $error); From 1a07f9458a7b7a4ea246a2bec1d404b0ce09a9c1 Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Mon, 8 Sep 2025 09:25:01 +0200 Subject: [PATCH 2/6] cleanup --- tests/EventListener/MessengerListenerTest.php | 39 ++++++------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/tests/EventListener/MessengerListenerTest.php b/tests/EventListener/MessengerListenerTest.php index 6beb4f43..288b3c6b 100644 --- a/tests/EventListener/MessengerListenerTest.php +++ b/tests/EventListener/MessengerListenerTest.php @@ -42,7 +42,7 @@ protected function setUp(): void /** * @dataProvider handleWorkerMessageFailedEventDataProvider * - * @param \Throwable[] $exceptions + * @param \Throwable[] $exceptions * @param array $expectedTags */ public function testHandleWorkerMessageFailedEvent(array $exceptions, WorkerMessageFailedEvent $event, array $expectedTags, bool $expectedIsHandled): void @@ -99,7 +99,7 @@ public function handleWorkerMessageFailedEventDataProvider(): \Generator return; } - $envelope = Envelope::wrap((object) []); + $envelope = Envelope::wrap((object)[]); $exceptions = [ new \Exception(), new \Exception(), @@ -155,7 +155,7 @@ public function handleWorkerMessageFailedEventDataProvider(): \Generator true, ]; - $envelope = new Envelope((object) [], [new BusNameStamp('bus.foo')]); + $envelope = new Envelope((object)[], [new BusNameStamp('bus.foo')]); yield 'envelope.stamps CONTAINS BusNameStamp' => [ [$exceptions[0]], @@ -178,7 +178,7 @@ public function testHandleWorkerMessageFailedEventWithCaptureSoftFailsFlag(bool $this->markTestSkipped('Messenger not supported in this environment.'); } - $envelope = Envelope::wrap((object) []); + $envelope = Envelope::wrap((object)[]); $event = $this->getMessageFailedEvent($envelope, 'receiver', new \Exception(), $retry); $this->hub->expects($this->any()) @@ -236,7 +236,7 @@ public function testHandleWorkerMessageHandledEvent(): void ->method('flush'); $listener = new MessengerListener($this->hub); - $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent(Envelope::wrap((object) []), 'receiver')); + $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent(Envelope::wrap((object)[]), 'receiver')); } public function testResetBreadcrumbsPushAndPopScopeWhenEnabled(): void @@ -255,8 +255,8 @@ public function testResetBreadcrumbsPushAndPopScopeWhenEnabled(): void $listener = new MessengerListener($this->hub, true, true); - $envelope = Envelope::wrap((object) []); - $listener->handleWorkerMessageReceivedEvent($this->createWorkerMessageReceivedEvent($envelope, 'receiver')); + $envelope = Envelope::wrap((object)[]); + $listener->handleWorkerMessageReceivedEvent(new WorkerMessageReceivedEvent($envelope, 'receiver')); $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent($envelope, 'receiver')); } @@ -274,8 +274,8 @@ public function testResetBreadcrumbsDoesNotPushOrPopWhenDisabled(): void $listener = new MessengerListener($this->hub, true, false); - $envelope = Envelope::wrap((object) []); - $listener->handleWorkerMessageReceivedEvent($this->createWorkerMessageReceivedEvent($envelope, 'receiver')); + $envelope = Envelope::wrap((object)[]); + $listener->handleWorkerMessageReceivedEvent(new WorkerMessageReceivedEvent($envelope, 'receiver')); $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent($envelope, 'receiver')); } @@ -285,15 +285,12 @@ public function testResetBreadcrumbsPopsAfterFailureWhenEnabled(): void $this->markTestSkipped('Messenger not supported in this environment.'); } - // Received event should push, failure should pop $this->hub->expects($this->once()) ->method('pushScope'); - // The failure handler pops in finally $this->hub->expects($this->once()) ->method('popScope'); - // Also expect captureEvent to be called once $this->hub->expects($this->once()) ->method('withScope') ->willReturnCallback(function (callable $callback): void { @@ -305,28 +302,14 @@ public function testResetBreadcrumbsPopsAfterFailureWhenEnabled(): void ->willReturn($this->client); $listener = new MessengerListener($this->hub, true, true); - $envelope = Envelope::wrap((object) []); + $envelope = Envelope::wrap((object)[]); - $listener->handleWorkerMessageReceivedEvent($this->createWorkerMessageReceivedEvent($envelope, 'receiver')); + $listener->handleWorkerMessageReceivedEvent(new WorkerMessageReceivedEvent($envelope, 'receiver')); $event = $this->getMessageFailedEvent($envelope, 'receiver', new \Exception('boom'), false); $listener->handleWorkerMessageFailedEvent($event); } - private function createWorkerMessageReceivedEvent(Envelope $envelope, string $receiverName) - { - $x = new WorkerMessageReceivedEvent($envelope, $receiverName); - $class = WorkerMessageReceivedEvent::class; - $refClass = new \ReflectionClass($class); - $ctor = $refClass->getConstructor(); - - if (null !== $ctor && $ctor->getNumberOfParameters() >= 2) { - return $refClass->newInstanceArgs([$envelope, $receiverName]); - } - - return $refClass->newInstanceArgs([$envelope]); - } - private function getMessageFailedEvent(Envelope $envelope, string $receiverName, \Throwable $error, bool $retry): WorkerMessageFailedEvent { $event = new WorkerMessageFailedEvent($envelope, $receiverName, $error); From 3c1b88732faa2b205a4c85f0fd78dbe255b78732 Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Mon, 8 Sep 2025 13:09:50 +0200 Subject: [PATCH 3/6] lints --- tests/End2End/End2EndTest.php | 2 +- tests/EventListener/MessengerListenerTest.php | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/End2End/End2EndTest.php b/tests/End2End/End2EndTest.php index 04b511e7..21942215 100644 --- a/tests/End2End/End2EndTest.php +++ b/tests/End2End/End2EndTest.php @@ -252,7 +252,7 @@ public function testMessengerCaptureSoftFailCanBeDisabled(): void $this->assertLastEventIdIsNull($client); } - public function testResetBreadcrumbs() + public function testResetBreadcrumbs(): void { $this->skipIfMessengerIsMissing(); diff --git a/tests/EventListener/MessengerListenerTest.php b/tests/EventListener/MessengerListenerTest.php index 288b3c6b..d561caaa 100644 --- a/tests/EventListener/MessengerListenerTest.php +++ b/tests/EventListener/MessengerListenerTest.php @@ -42,7 +42,7 @@ protected function setUp(): void /** * @dataProvider handleWorkerMessageFailedEventDataProvider * - * @param \Throwable[] $exceptions + * @param \Throwable[] $exceptions * @param array $expectedTags */ public function testHandleWorkerMessageFailedEvent(array $exceptions, WorkerMessageFailedEvent $event, array $expectedTags, bool $expectedIsHandled): void @@ -99,7 +99,7 @@ public function handleWorkerMessageFailedEventDataProvider(): \Generator return; } - $envelope = Envelope::wrap((object)[]); + $envelope = Envelope::wrap((object) []); $exceptions = [ new \Exception(), new \Exception(), @@ -155,7 +155,7 @@ public function handleWorkerMessageFailedEventDataProvider(): \Generator true, ]; - $envelope = new Envelope((object)[], [new BusNameStamp('bus.foo')]); + $envelope = new Envelope((object) [], [new BusNameStamp('bus.foo')]); yield 'envelope.stamps CONTAINS BusNameStamp' => [ [$exceptions[0]], @@ -178,7 +178,7 @@ public function testHandleWorkerMessageFailedEventWithCaptureSoftFailsFlag(bool $this->markTestSkipped('Messenger not supported in this environment.'); } - $envelope = Envelope::wrap((object)[]); + $envelope = Envelope::wrap((object) []); $event = $this->getMessageFailedEvent($envelope, 'receiver', new \Exception(), $retry); $this->hub->expects($this->any()) @@ -236,7 +236,7 @@ public function testHandleWorkerMessageHandledEvent(): void ->method('flush'); $listener = new MessengerListener($this->hub); - $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent(Envelope::wrap((object)[]), 'receiver')); + $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent(Envelope::wrap((object) []), 'receiver')); } public function testResetBreadcrumbsPushAndPopScopeWhenEnabled(): void @@ -255,7 +255,7 @@ public function testResetBreadcrumbsPushAndPopScopeWhenEnabled(): void $listener = new MessengerListener($this->hub, true, true); - $envelope = Envelope::wrap((object)[]); + $envelope = Envelope::wrap((object) []); $listener->handleWorkerMessageReceivedEvent(new WorkerMessageReceivedEvent($envelope, 'receiver')); $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent($envelope, 'receiver')); } @@ -274,7 +274,7 @@ public function testResetBreadcrumbsDoesNotPushOrPopWhenDisabled(): void $listener = new MessengerListener($this->hub, true, false); - $envelope = Envelope::wrap((object)[]); + $envelope = Envelope::wrap((object) []); $listener->handleWorkerMessageReceivedEvent(new WorkerMessageReceivedEvent($envelope, 'receiver')); $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent($envelope, 'receiver')); } @@ -302,7 +302,7 @@ public function testResetBreadcrumbsPopsAfterFailureWhenEnabled(): void ->willReturn($this->client); $listener = new MessengerListener($this->hub, true, true); - $envelope = Envelope::wrap((object)[]); + $envelope = Envelope::wrap((object) []); $listener->handleWorkerMessageReceivedEvent(new WorkerMessageReceivedEvent($envelope, 'receiver')); From 6daeef30bfbeb9aa916ddcaee0ccd10c82177b7c Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Thu, 11 Sep 2025 13:59:00 +0200 Subject: [PATCH 4/6] rename config --- src/DependencyInjection/Configuration.php | 2 +- src/DependencyInjection/SentryExtension.php | 2 +- tests/DependencyInjection/ConfigurationTest.php | 2 +- tests/DependencyInjection/Fixtures/php/full.php | 2 +- tests/DependencyInjection/Fixtures/yml/full.yml | 2 +- tests/End2End/App/messenger.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 8defe37c..fb146cce 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -187,7 +187,7 @@ private function addMessengerSection(ArrayNodeDefinition $rootNode): void ->{interface_exists(MessageBusInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}() ->children() ->booleanNode('capture_soft_fails')->defaultTrue()->end() - ->booleanNode('reset_breadcrumbs')->defaultFalse()->end() + ->booleanNode('isolate_breadcrumbs_by_message')->defaultFalse()->end() ->end() ->end() ->end(); diff --git a/src/DependencyInjection/SentryExtension.php b/src/DependencyInjection/SentryExtension.php index c7a52481..44218b56 100644 --- a/src/DependencyInjection/SentryExtension.php +++ b/src/DependencyInjection/SentryExtension.php @@ -200,7 +200,7 @@ private function registerMessengerListenerConfiguration(ContainerBuilder $contai return; } - $container->getDefinition(MessengerListener::class)->setArgument(1, $config['capture_soft_fails'])->setArgument(2, $config['reset_breadcrumbs']); + $container->getDefinition(MessengerListener::class)->setArgument(1, $config['capture_soft_fails'])->setArgument(2, $config['isolate_breadcrumbs_by_message']); } /** diff --git a/tests/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php index 6d3a501e..e9aed67e 100644 --- a/tests/DependencyInjection/ConfigurationTest.php +++ b/tests/DependencyInjection/ConfigurationTest.php @@ -42,7 +42,7 @@ public function testProcessConfigurationWithDefaultConfiguration(): void 'messenger' => [ 'enabled' => interface_exists(MessageBusInterface::class), 'capture_soft_fails' => true, - 'reset_breadcrumbs' => false, + 'isolate_breadcrumbs_by_message' => false, ], 'tracing' => [ 'enabled' => true, diff --git a/tests/DependencyInjection/Fixtures/php/full.php b/tests/DependencyInjection/Fixtures/php/full.php index 31ae0060..6cd2c1aa 100644 --- a/tests/DependencyInjection/Fixtures/php/full.php +++ b/tests/DependencyInjection/Fixtures/php/full.php @@ -60,7 +60,7 @@ 'messenger' => [ 'enabled' => true, 'capture_soft_fails' => false, - 'reset_breadcrumbs' => true, + 'isolate_breadcrumbs_by_message' => true, ], 'tracing' => [ 'dbal' => [ diff --git a/tests/DependencyInjection/Fixtures/yml/full.yml b/tests/DependencyInjection/Fixtures/yml/full.yml index 8f45b2dd..760b398e 100644 --- a/tests/DependencyInjection/Fixtures/yml/full.yml +++ b/tests/DependencyInjection/Fixtures/yml/full.yml @@ -59,7 +59,7 @@ sentry: messenger: enabled: true capture_soft_fails: false - reset_breadcrumbs: true + isolate_breadcrumbs_by_message: true tracing: dbal: enabled: false diff --git a/tests/End2End/App/messenger.yml b/tests/End2End/App/messenger.yml index aeffdeaa..a4b91825 100644 --- a/tests/End2End/App/messenger.yml +++ b/tests/End2End/App/messenger.yml @@ -41,4 +41,4 @@ framework: sentry: messenger: capture_soft_fails: false - reset_breadcrumbs: true + isolate_breadcrumbs_by_message: true From 7ab8c8b12f254901afad3424174651ea0cfb74ca Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Thu, 11 Sep 2025 14:04:44 +0200 Subject: [PATCH 5/6] naming in xml --- src/EventListener/MessengerListener.php | 22 +++++++++---------- src/Resources/config/schema/sentry-1.0.xsd | 2 +- .../DependencyInjection/Fixtures/xml/full.xml | 2 +- tests/End2End/End2EndTest.php | 2 +- tests/EventListener/MessengerListenerTest.php | 6 ++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/EventListener/MessengerListener.php b/src/EventListener/MessengerListener.php index 6a36c1f9..df6e223b 100644 --- a/src/EventListener/MessengerListener.php +++ b/src/EventListener/MessengerListener.php @@ -36,20 +36,20 @@ final class MessengerListener * This allows us to have breadcrumbs only for one message and no breadcrumb is leaked into * future messages. */ - private $resetBreadcrumbs; + private $isolateBreadcrumbsByMessage; /** - * @param HubInterface $hub The current hub - * @param bool $captureSoftFails Whether to capture errors thrown - * while processing a message that - * will be retried - * @param bool $resetBreadcrumbs Whether to reset all breadcrumbs after a message + * @param HubInterface $hub The current hub + * @param bool $captureSoftFails Whether to capture errors thrown + * while processing a message that + * will be retried + * @param bool $isolateBreadcrumbsByMessage Whether to reset all breadcrumbs after a message */ - public function __construct(HubInterface $hub, bool $captureSoftFails = true, bool $resetBreadcrumbs = false) + public function __construct(HubInterface $hub, bool $captureSoftFails = true, bool $isolateBreadcrumbsByMessage = false) { $this->hub = $hub; $this->captureSoftFails = $captureSoftFails; - $this->resetBreadcrumbs = $resetBreadcrumbs; + $this->isolateBreadcrumbsByMessage = $isolateBreadcrumbsByMessage; } /** @@ -85,7 +85,7 @@ public function handleWorkerMessageFailedEvent(WorkerMessageFailedEvent $event): } finally { // We always want to pop the scope at the end of this method to add the breadcrumbs // to any potential event that is produced. - if ($this->resetBreadcrumbs) { + if ($this->isolateBreadcrumbsByMessage) { $this->hub->popScope(); } } @@ -101,7 +101,7 @@ public function handleWorkerMessageHandledEvent(WorkerMessageHandledEvent $event // Flush normally happens at shutdown... which only happens in the worker if it is run with a lifecycle limit // such as --time=X or --limit=Y. Flush immediately in a background worker. $this->flushClient(); - if ($this->resetBreadcrumbs) { + if ($this->isolateBreadcrumbsByMessage) { $this->hub->popScope(); } } @@ -116,7 +116,7 @@ public function handleWorkerMessageHandledEvent(WorkerMessageHandledEvent $event */ public function handleWorkerMessageReceivedEvent(WorkerMessageReceivedEvent $event): void { - if ($this->resetBreadcrumbs) { + if ($this->isolateBreadcrumbsByMessage) { $this->hub->pushScope(); } } diff --git a/src/Resources/config/schema/sentry-1.0.xsd b/src/Resources/config/schema/sentry-1.0.xsd index 34fa7cc3..5d03008b 100644 --- a/src/Resources/config/schema/sentry-1.0.xsd +++ b/src/Resources/config/schema/sentry-1.0.xsd @@ -101,7 +101,7 @@ - + diff --git a/tests/DependencyInjection/Fixtures/xml/full.xml b/tests/DependencyInjection/Fixtures/xml/full.xml index 744f0a92..9615eb2e 100644 --- a/tests/DependencyInjection/Fixtures/xml/full.xml +++ b/tests/DependencyInjection/Fixtures/xml/full.xml @@ -57,7 +57,7 @@ Symfony\Component\HttpKernel\Exception\BadRequestHttpException GET tracing_ignored_transaction - + default diff --git a/tests/End2End/End2EndTest.php b/tests/End2End/End2EndTest.php index 21942215..d346dcca 100644 --- a/tests/End2End/End2EndTest.php +++ b/tests/End2End/End2EndTest.php @@ -252,7 +252,7 @@ public function testMessengerCaptureSoftFailCanBeDisabled(): void $this->assertLastEventIdIsNull($client); } - public function testResetBreadcrumbs(): void + public function testIsolateBreadcrumbsByMessage(): void { $this->skipIfMessengerIsMissing(); diff --git a/tests/EventListener/MessengerListenerTest.php b/tests/EventListener/MessengerListenerTest.php index d561caaa..8a38e75c 100644 --- a/tests/EventListener/MessengerListenerTest.php +++ b/tests/EventListener/MessengerListenerTest.php @@ -239,7 +239,7 @@ public function testHandleWorkerMessageHandledEvent(): void $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent(Envelope::wrap((object) []), 'receiver')); } - public function testResetBreadcrumbsPushAndPopScopeWhenEnabled(): void + public function testIsolateBreadcrumbsByMessagePushAndPopScopeWhenEnabled(): void { if (!$this->supportsMessenger()) { $this->markTestSkipped('Messenger not supported in this environment.'); @@ -260,7 +260,7 @@ public function testResetBreadcrumbsPushAndPopScopeWhenEnabled(): void $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent($envelope, 'receiver')); } - public function testResetBreadcrumbsDoesNotPushOrPopWhenDisabled(): void + public function testIsolateBreadcrumbsByMessageDoesNotPushOrPopWhenDisabled(): void { if (!$this->supportsMessenger()) { $this->markTestSkipped('Messenger not supported in this environment.'); @@ -279,7 +279,7 @@ public function testResetBreadcrumbsDoesNotPushOrPopWhenDisabled(): void $listener->handleWorkerMessageHandledEvent(new WorkerMessageHandledEvent($envelope, 'receiver')); } - public function testResetBreadcrumbsPopsAfterFailureWhenEnabled(): void + public function testIsolateBreadcrumbsByMessagePopsAfterFailureWhenEnabled(): void { if (!$this->supportsMessenger()) { $this->markTestSkipped('Messenger not supported in this environment.'); From defd1eff2de99b280743b6be85ef5f3f3b198e22 Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Thu, 11 Sep 2025 14:06:31 +0200 Subject: [PATCH 6/6] comment --- src/EventListener/MessengerListener.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EventListener/MessengerListener.php b/src/EventListener/MessengerListener.php index df6e223b..0e42ac01 100644 --- a/src/EventListener/MessengerListener.php +++ b/src/EventListener/MessengerListener.php @@ -43,7 +43,7 @@ final class MessengerListener * @param bool $captureSoftFails Whether to capture errors thrown * while processing a message that * will be retried - * @param bool $isolateBreadcrumbsByMessage Whether to reset all breadcrumbs after a message + * @param bool $isolateBreadcrumbsByMessage Whether messages should have isolated breadcrumbs */ public function __construct(HubInterface $hub, bool $captureSoftFails = true, bool $isolateBreadcrumbsByMessage = false) {