diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index 9de88acd..2e6b8d49 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -136,7 +136,9 @@ public function getConfigTreeBuilder()
->children()
->scalarNode('request')->defaultValue(0)->end()
->scalarNode('kernel_exception')->defaultValue(0)->end()
+ ->scalarNode('finish_request')->defaultValue(0)->end()
->scalarNode('console_exception')->defaultValue(0)->end()
+ ->scalarNode('console_terminate')->defaultValue(0)->end()
->end()
->end()
->end()
diff --git a/src/EventListener/ExceptionListener.php b/src/EventListener/ExceptionListener.php
index f8f1fd33..857f664b 100644
--- a/src/EventListener/ExceptionListener.php
+++ b/src/EventListener/ExceptionListener.php
@@ -8,9 +8,11 @@
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleEvent;
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
+use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
@@ -82,6 +84,10 @@ public function setClient(\Raven_Client $client)
*/
public function onKernelRequest(GetResponseEvent $event): void
{
+ if (($route = $event->getRequest()->attributes->get('_route'))) {
+ $this->client->transaction->push($route);
+ }
+
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
@@ -115,6 +121,13 @@ public function onKernelException(GetResponseForExceptionEvent $event): void
$this->client->captureException($exception);
}
+ public function onFinishRequest(FinishRequestEvent $event): void
+ {
+ if (($route = $event->getRequest()->attributes->get('_route'))) {
+ $this->client->transaction->pop($route);
+ }
+ }
+
/**
* This method only ensures that the client and error handlers are registered at the start of the command
* execution cycle, and not only on exceptions
@@ -125,7 +138,9 @@ public function onKernelException(GetResponseForExceptionEvent $event): void
*/
public function onConsoleCommand(ConsoleCommandEvent $event): void
{
- // only triggers loading of client, does not need to do anything.
+ if (($command = $event->getCommand())) {
+ $this->client->transaction->push($command->getName());
+ }
}
public function onConsoleError(ConsoleErrorEvent $event): void
@@ -138,6 +153,13 @@ public function onConsoleException(ConsoleExceptionEvent $event): void
$this->handleConsoleError($event);
}
+ public function onFinishCommand(ConsoleTerminateEvent $event): void
+ {
+ if (($command = $event->getCommand())) {
+ $this->client->transaction->pop($command->getName());
+ }
+ }
+
/**
* @param ConsoleExceptionEvent|ConsoleErrorEvent $event
*/
diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml
index c322f116..7269182f 100644
--- a/src/Resources/config/services.xml
+++ b/src/Resources/config/services.xml
@@ -23,8 +23,10 @@
+
+
diff --git a/test/DependencyInjection/SentryExtensionTest.php b/test/DependencyInjection/SentryExtensionTest.php
index fccd1495..d7216758 100644
--- a/test/DependencyInjection/SentryExtensionTest.php
+++ b/test/DependencyInjection/SentryExtensionTest.php
@@ -287,12 +287,22 @@ public function test_that_it_has_proper_event_listener_tags_for_exception_listen
'method' => 'onKernelException',
'priority' => '%sentry.listener_priorities.kernel_exception%',
],
+ [
+ 'event' => 'kernel.finish_request',
+ 'method' => 'onFinishRequest',
+ 'priority' => '%sentry.listener_priorities.finish_request%',
+ ],
['event' => 'console.command', 'method' => 'onConsoleCommand'],
[
'event' => 'console.error',
'method' => 'onConsoleError',
'priority' => '%sentry.listener_priorities.console_exception%',
],
+ [
+ 'event' => 'console.terminate',
+ 'method' => 'onFinishCommand',
+ 'priority' => '%sentry.listener_priorities.console_terminate%',
+ ],
],
$tags
);
diff --git a/test/EventListener/ExceptionListenerTest.php b/test/EventListener/ExceptionListenerTest.php
index 8aa3423d..ecccd6be 100644
--- a/test/EventListener/ExceptionListenerTest.php
+++ b/test/EventListener/ExceptionListenerTest.php
@@ -9,8 +9,10 @@
use Sentry\SentryBundle\EventListener\SentryExceptionListenerInterface;
use Sentry\SentryBundle\SentrySymfonyEvents;
use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
+use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\Alias;
@@ -18,6 +20,7 @@
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -81,6 +84,10 @@ public function test_that_user_data_is_not_set_on_subrequest()
{
$mockEvent = $this->createMock(GetResponseEvent::class);
+ $mockEvent
+ ->method('getRequest')
+ ->willReturn(new Request());
+
$mockEvent
->expects($this->once())
->method('getRequestType')
@@ -107,6 +114,10 @@ public function test_that_user_data_is_not_set_if_token_storage_not_present()
$mockEvent = $this->createMock(GetResponseEvent::class);
+ $mockEvent
+ ->method('getRequest')
+ ->willReturn(new Request());
+
$mockEvent
->expects($this->once())
->method('getRequestType')
@@ -136,6 +147,10 @@ public function test_that_user_data_is_not_set_if_authorization_checker_not_pres
$mockEvent = $this->createMock(GetResponseEvent::class);
+ $mockEvent
+ ->method('getRequest')
+ ->willReturn(new Request());
+
$mockEvent
->expects($this->once())
->method('getRequestType')
@@ -167,6 +182,10 @@ public function test_that_user_data_is_not_set_if_token_not_present()
$mockEvent = $this->createMock(GetResponseEvent::class);
+ $mockEvent
+ ->method('getRequest')
+ ->willReturn(new Request());
+
$mockEvent
->expects($this->once())
->method('getRequestType')
@@ -209,6 +228,10 @@ public function test_that_user_data_is_not_set_if_not_authorized()
$mockEvent = $this->createMock(GetResponseEvent::class);
+ $mockEvent
+ ->method('getRequest')
+ ->willReturn(new Request());
+
$mockEvent->expects($this->once())
->method('getRequestType')
->willReturn(HttpKernelInterface::MASTER_REQUEST);
@@ -254,6 +277,10 @@ public function test_that_username_is_set_from_user_interface_if_token_present_a
$mockEvent = $this->createMock(GetResponseEvent::class);
+ $mockEvent
+ ->method('getRequest')
+ ->willReturn(new Request());
+
$mockEvent
->expects($this->once())
->method('getRequestType')
@@ -303,6 +330,10 @@ public function test_that_username_is_set_from_user_interface_if_token_present_a
$mockEvent = $this->createMock(GetResponseEvent::class);
+ $mockEvent
+ ->method('getRequest')
+ ->willReturn(new Request());
+
$mockEvent
->expects($this->once())
->method('getRequestType')
@@ -358,6 +389,10 @@ public function test_that_username_is_set_from_user_interface_if_token_present_a
$mockEvent = $this->createMock(GetResponseEvent::class);
+ $mockEvent
+ ->method('getRequest')
+ ->willReturn(new Request());
+
$mockEvent
->expects($this->once())
->method('getRequestType')
@@ -404,6 +439,10 @@ public function test_that_ip_is_set_from_request_stack()
$mockEvent = $this->createMock(GetResponseEvent::class);
+ $mockEvent
+ ->method('getRequest')
+ ->willReturn(new Request());
+
$mockRequest = $this->createMock(Request::class);
$mockRequest
@@ -438,6 +477,59 @@ public function test_that_ip_is_set_from_request_stack()
$listener->onKernelRequest($mockEvent);
}
+ public function test_that_it_sets_transaction_from_route()
+ {
+ $mockEvent = $this->createMock(GetResponseEvent::class);
+
+ $request = new Request();
+ $request->attributes->set('_route', 'my_route');
+ $mockEvent
+ ->expects($this->once())
+ ->method('getRequest')
+ ->willReturn($request);
+
+ $mockEvent
+ ->method('getRequestType')
+ ->willReturn(HttpKernelInterface::SUB_REQUEST);
+
+ $mockTransactionStack = $this->createMock(\Raven_TransactionStack::class);
+ $this->mockSentryClient->transaction = $mockTransactionStack;
+
+ $mockTransactionStack
+ ->expects($this->once())
+ ->method('push')
+ ->with('my_route');
+
+ $this->containerBuilder->compile();
+ $listener = $this->getListener();
+ $listener->onKernelRequest($mockEvent);
+ }
+
+ public function test_that_it_pops_transaction_from_route()
+ {
+ $mockEvent = $this->createMock(FinishRequestEvent::class);
+
+ $request = new Request();
+ $request->attributes->set('_route', 'my_route');
+ $mockEvent
+ ->expects($this->once())
+ ->method('getRequest')
+ ->willReturn($request);
+
+ $mockTransactionStack = $this->createMock(\Raven_TransactionStack::class);
+ $this->mockSentryClient->transaction = $mockTransactionStack;
+
+ $mockTransactionStack
+ ->expects($this->once())
+ ->method('pop')
+ ->with('my_route');
+
+ $this->containerBuilder->compile();
+ /** @var ExceptionListener $listener */
+ $listener = $this->getListener();
+ $listener->onFinishRequest($mockEvent);
+ }
+
public function test_regression_with_unauthenticated_user_token_PR_78()
{
$mockToken = $this->createMock(TokenInterface::class);
@@ -447,6 +539,10 @@ public function test_regression_with_unauthenticated_user_token_PR_78()
$mockEvent = $this->createMock(GetResponseEvent::class);
+ $mockEvent
+ ->method('getRequest')
+ ->willReturn(new Request());
+
$mockEvent
->expects($this->once())
->method('getRequestType')
@@ -465,6 +561,10 @@ public function test_that_it_does_not_report_http_exception_if_included_in_captu
{
$mockEvent = $this->createMock(GetResponseForExceptionEvent::class);
+ $mockEvent
+ ->method('getRequest')
+ ->willReturn(new Request());
+
$mockEvent
->expects($this->once())
->method('getException')
@@ -510,6 +610,30 @@ public function test_that_it_captures_exception()
$listener->onKernelException($mockEvent);
}
+ public function test_that_it_sets_transaction_from_command()
+ {
+ $mockEvent = $this->createMock(ConsoleCommandEvent::class);
+
+ $command = new Command('my_command');
+ $mockEvent
+ ->expects($this->once())
+ ->method('getCommand')
+ ->willReturn($command);
+
+ $mockTransactionStack = $this->createMock(\Raven_TransactionStack::class);
+ $this->mockSentryClient->transaction = $mockTransactionStack;
+
+ $mockTransactionStack
+ ->expects($this->once())
+ ->method('push')
+ ->with('my_command');
+
+ $this->containerBuilder->compile();
+ /** @var ExceptionListener $listener */
+ $listener = $this->getListener();
+ $listener->onConsoleCommand($mockEvent);
+ }
+
/**
* @dataProvider mockCommandProvider
*/
@@ -593,6 +717,30 @@ public function test_that_it_captures_console_error(?Command $mockCommand, strin
$listener->onConsoleError($event);
}
+ public function test_that_it_pops_transaction_from_command()
+ {
+ $mockEvent = $this->createMock(ConsoleTerminateEvent::class);
+
+ $command = new Command('my_command');
+ $mockEvent
+ ->expects($this->once())
+ ->method('getCommand')
+ ->willReturn($command);
+
+ $mockTransactionStack = $this->createMock(\Raven_TransactionStack::class);
+ $this->mockSentryClient->transaction = $mockTransactionStack;
+
+ $mockTransactionStack
+ ->expects($this->once())
+ ->method('pop')
+ ->with('my_command');
+
+ $this->containerBuilder->compile();
+ /** @var ExceptionListener $listener */
+ $listener = $this->getListener();
+ $listener->onFinishCommand($mockEvent);
+ }
+
public function mockCommandProvider()
{
$mockCommand = $this->createMock(Command::class);