Skip to content

Commit 1c23f5e

Browse files
committed
Push to the sentry transaction
- At start and end of request, using _route attribute if its set - At start and end of commands, using command name
1 parent 7c76ba5 commit 1c23f5e

File tree

6 files changed

+165
-1
lines changed

6 files changed

+165
-1
lines changed

src/DependencyInjection/Configuration.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ public function getConfigTreeBuilder()
136136
->children()
137137
->scalarNode('request')->defaultValue(0)->end()
138138
->scalarNode('kernel_exception')->defaultValue(0)->end()
139+
->scalarNode('finish_request')->defaultValue(0)->end()
139140
->scalarNode('console_exception')->defaultValue(0)->end()
141+
->scalarNode('console_terminate')->defaultValue(0)->end()
140142
->end()
141143
->end()
142144
->end()

src/EventListener/ExceptionListener.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
use Symfony\Component\Console\Event\ConsoleErrorEvent;
99
use Symfony\Component\Console\Event\ConsoleEvent;
1010
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
11+
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
1112
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
1213
use Symfony\Component\HttpFoundation\Request;
1314
use Symfony\Component\HttpFoundation\RequestStack;
15+
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
1416
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
1517
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
1618
use Symfony\Component\HttpKernel\HttpKernelInterface;
@@ -82,6 +84,10 @@ public function setClient(\Raven_Client $client)
8284
*/
8385
public function onKernelRequest(GetResponseEvent $event): void
8486
{
87+
if (($request = $event->getRequest()) && ($route = $request->attributes->get('_route'))) {
88+
$this->client->transaction->push($route);
89+
}
90+
8591
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
8692
return;
8793
}
@@ -115,6 +121,13 @@ public function onKernelException(GetResponseForExceptionEvent $event): void
115121
$this->client->captureException($exception);
116122
}
117123

124+
public function onFinishRequest(FinishRequestEvent $event): void
125+
{
126+
if (($request = $event->getRequest()) && ($route = $request->attributes->get('_route'))) {
127+
$this->client->transaction->pop($route);
128+
}
129+
}
130+
118131
/**
119132
* This method only ensures that the client and error handlers are registered at the start of the command
120133
* execution cycle, and not only on exceptions
@@ -125,7 +138,9 @@ public function onKernelException(GetResponseForExceptionEvent $event): void
125138
*/
126139
public function onConsoleCommand(ConsoleCommandEvent $event): void
127140
{
128-
// only triggers loading of client, does not need to do anything.
141+
if (($command = $event->getCommand())) {
142+
$this->client->transaction->push($command->getName());
143+
}
129144
}
130145

131146
public function onConsoleError(ConsoleErrorEvent $event): void
@@ -138,6 +153,13 @@ public function onConsoleException(ConsoleExceptionEvent $event): void
138153
$this->handleConsoleError($event);
139154
}
140155

156+
public function onFinishCommand(ConsoleTerminateEvent $event): void
157+
{
158+
if (($command = $event->getCommand())) {
159+
$this->client->transaction->pop($command->getName());
160+
}
161+
}
162+
141163
/**
142164
* @param ConsoleExceptionEvent|ConsoleErrorEvent $event
143165
*/

src/EventListener/SentryExceptionListenerInterface.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
namespace Sentry\SentryBundle\EventListener;
44

5+
use Symfony\Component\Console\Event\ConsoleCommandEvent;
56
use Symfony\Component\Console\Event\ConsoleErrorEvent;
67
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
8+
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
9+
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
710
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
811
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
912

@@ -28,6 +31,23 @@ public function onKernelRequest(GetResponseEvent $event): void;
2831
*/
2932
public function onKernelException(GetResponseForExceptionEvent $event): void;
3033

34+
/**
35+
* When the request finished
36+
*
37+
* @param FinishRequestEvent $event
38+
*/
39+
public function onFinishRequest(FinishRequestEvent $event): void;
40+
41+
/**
42+
* This method only ensures that the client and error handlers are registered at the start of the command
43+
* execution cycle, and not only on exceptions
44+
*
45+
* @param ConsoleCommandEvent $event
46+
*
47+
* @return void
48+
*/
49+
public function onConsoleCommand(ConsoleCommandEvent $event): void;
50+
3151
/**
3252
* When an exception occurs on the command line, this method will be
3353
* triggered for capturing the error.
@@ -44,4 +64,11 @@ public function onConsoleError(ConsoleErrorEvent $event): void;
4464
* @deprecated This method exists for BC with Symfony 3.x
4565
*/
4666
public function onConsoleException(ConsoleExceptionEvent $event): void;
67+
68+
/**
69+
* When a console command finishes
70+
*
71+
* @param ConsoleTerminateEvent $event
72+
*/
73+
public function onFinishCommand(ConsoleTerminateEvent $event): void;
4774
}

src/Resources/config/services.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323

2424
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="%sentry.listener_priorities.request%" />
2525
<tag name="kernel.event_listener" event="kernel.exception" method="onKernelException" priority="%sentry.listener_priorities.kernel_exception%" />
26+
<tag name="kernel.event_listener" event="kernel.finish_request" method="onFinishRequest" priority="%sentry.listener_priorities.finish_request%" />
2627
<tag name="kernel.event_listener" event="console.command" method="onConsoleCommand" />
2728
<tag name="kernel.event_listener" event="console.error" method="onConsoleError" priority="%sentry.listener_priorities.console_exception%" />
29+
<tag name="kernel.event_listener" event="console.terminate" method="onFinishCommand" priority="%sentry.listener_priorities.console_terminate%" />
2830
</service>
2931
</services>
3032
</container>

test/DependencyInjection/SentryExtensionTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,12 +287,22 @@ public function test_that_it_has_proper_event_listener_tags_for_exception_listen
287287
'method' => 'onKernelException',
288288
'priority' => '%sentry.listener_priorities.kernel_exception%',
289289
],
290+
[
291+
'event' => 'kernel.finish_request',
292+
'method' => 'onFinishRequest',
293+
'priority' => '%sentry.listener_priorities.finish_request%',
294+
],
290295
['event' => 'console.command', 'method' => 'onConsoleCommand'],
291296
[
292297
'event' => 'console.error',
293298
'method' => 'onConsoleError',
294299
'priority' => '%sentry.listener_priorities.console_exception%',
295300
],
301+
[
302+
'event' => 'console.terminate',
303+
'method' => 'onFinishCommand',
304+
'priority' => '%sentry.listener_priorities.console_terminate%',
305+
],
296306
],
297307
$tags
298308
);

test/EventListener/ExceptionListenerTest.php

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,18 @@
99
use Sentry\SentryBundle\EventListener\SentryExceptionListenerInterface;
1010
use Sentry\SentryBundle\SentrySymfonyEvents;
1111
use Symfony\Component\Console\Command\Command;
12+
use Symfony\Component\Console\Event\ConsoleCommandEvent;
1213
use Symfony\Component\Console\Event\ConsoleErrorEvent;
1314
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
15+
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
1416
use Symfony\Component\Console\Input\InputInterface;
1517
use Symfony\Component\Console\Output\OutputInterface;
1618
use Symfony\Component\DependencyInjection\Alias;
1719
use Symfony\Component\DependencyInjection\ContainerBuilder;
1820
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
1921
use Symfony\Component\HttpFoundation\Request;
2022
use Symfony\Component\HttpFoundation\RequestStack;
23+
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
2124
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
2225
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
2326
use Symfony\Component\HttpKernel\Exception\HttpException;
@@ -438,6 +441,58 @@ public function test_that_ip_is_set_from_request_stack()
438441
$listener->onKernelRequest($mockEvent);
439442
}
440443

444+
public function test_that_it_sets_transaction_from_route()
445+
{
446+
$mockEvent = $this->createMock(GetResponseEvent::class);
447+
448+
$request = new Request();
449+
$request->attributes->set('_route', 'my_route');
450+
$mockEvent
451+
->expects($this->once())
452+
->method('getRequest')
453+
->willReturn($request);
454+
455+
$mockEvent
456+
->method('getRequestType')
457+
->willReturn(HttpKernelInterface::SUB_REQUEST);
458+
459+
$mockTransactionStack = $this->createMock(\Raven_TransactionStack::class);
460+
$this->mockSentryClient->transaction = $mockTransactionStack;
461+
462+
$mockTransactionStack
463+
->expects($this->once())
464+
->method('push')
465+
->with('my_route');
466+
467+
$this->containerBuilder->compile();
468+
$listener = $this->getListener();
469+
$listener->onKernelRequest($mockEvent);
470+
}
471+
472+
public function test_that_it_pops_transaction_from_route()
473+
{
474+
$mockEvent = $this->createMock(FinishRequestEvent::class);
475+
476+
$request = new Request();
477+
$request->attributes->set('_route', 'my_route');
478+
$mockEvent
479+
->expects($this->once())
480+
->method('getRequest')
481+
->willReturn($request);
482+
483+
$mockTransactionStack = $this->createMock(\Raven_TransactionStack::class);
484+
$this->mockSentryClient->transaction = $mockTransactionStack;
485+
486+
$mockTransactionStack
487+
->expects($this->once())
488+
->method('pop')
489+
->with('my_route');
490+
491+
$this->containerBuilder->compile();
492+
$listener = $this->getListener();
493+
$listener->onFinishRequest($mockEvent);
494+
}
495+
441496
public function test_regression_with_unauthenticated_user_token_PR_78()
442497
{
443498
$mockToken = $this->createMock(TokenInterface::class);
@@ -510,6 +565,29 @@ public function test_that_it_captures_exception()
510565
$listener->onKernelException($mockEvent);
511566
}
512567

568+
public function test_that_it_sets_transaction_from_command()
569+
{
570+
$mockEvent = $this->createMock(ConsoleCommandEvent::class);
571+
572+
$command = new Command('my_command');
573+
$mockEvent
574+
->expects($this->once())
575+
->method('getCommand')
576+
->willReturn($command);
577+
578+
$mockTransactionStack = $this->createMock(\Raven_TransactionStack::class);
579+
$this->mockSentryClient->transaction = $mockTransactionStack;
580+
581+
$mockTransactionStack
582+
->expects($this->once())
583+
->method('push')
584+
->with('my_command');
585+
586+
$this->containerBuilder->compile();
587+
$listener = $this->getListener();
588+
$listener->onConsoleCommand($mockEvent);
589+
}
590+
513591
/**
514592
* @dataProvider mockCommandProvider
515593
*/
@@ -593,6 +671,29 @@ public function test_that_it_captures_console_error(?Command $mockCommand, strin
593671
$listener->onConsoleError($event);
594672
}
595673

674+
public function test_that_it_pops_transaction_from_command()
675+
{
676+
$mockEvent = $this->createMock(ConsoleTerminateEvent::class);
677+
678+
$command = new Command('my_command');
679+
$mockEvent
680+
->expects($this->once())
681+
->method('getCommand')
682+
->willReturn($command);
683+
684+
$mockTransactionStack = $this->createMock(\Raven_TransactionStack::class);
685+
$this->mockSentryClient->transaction = $mockTransactionStack;
686+
687+
$mockTransactionStack
688+
->expects($this->once())
689+
->method('pop')
690+
->with('my_command');
691+
692+
$this->containerBuilder->compile();
693+
$listener = $this->getListener();
694+
$listener->onFinishCommand($mockEvent);
695+
}
696+
596697
public function mockCommandProvider()
597698
{
598699
$mockCommand = $this->createMock(Command::class);

0 commit comments

Comments
 (0)