Skip to content

Commit a7ad0c8

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 a7ad0c8

File tree

5 files changed

+185
-1
lines changed

5 files changed

+185
-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 (($route = $event->getRequest()->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 (($route = $event->getRequest()->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/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: 148 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;
@@ -81,6 +84,10 @@ public function test_that_user_data_is_not_set_on_subrequest()
8184
{
8285
$mockEvent = $this->createMock(GetResponseEvent::class);
8386

87+
$mockEvent
88+
->method('getRequest')
89+
->willReturn(new Request());
90+
8491
$mockEvent
8592
->expects($this->once())
8693
->method('getRequestType')
@@ -107,6 +114,10 @@ public function test_that_user_data_is_not_set_if_token_storage_not_present()
107114

108115
$mockEvent = $this->createMock(GetResponseEvent::class);
109116

117+
$mockEvent
118+
->method('getRequest')
119+
->willReturn(new Request());
120+
110121
$mockEvent
111122
->expects($this->once())
112123
->method('getRequestType')
@@ -136,6 +147,10 @@ public function test_that_user_data_is_not_set_if_authorization_checker_not_pres
136147

137148
$mockEvent = $this->createMock(GetResponseEvent::class);
138149

150+
$mockEvent
151+
->method('getRequest')
152+
->willReturn(new Request());
153+
139154
$mockEvent
140155
->expects($this->once())
141156
->method('getRequestType')
@@ -167,6 +182,10 @@ public function test_that_user_data_is_not_set_if_token_not_present()
167182

168183
$mockEvent = $this->createMock(GetResponseEvent::class);
169184

185+
$mockEvent
186+
->method('getRequest')
187+
->willReturn(new Request());
188+
170189
$mockEvent
171190
->expects($this->once())
172191
->method('getRequestType')
@@ -209,6 +228,10 @@ public function test_that_user_data_is_not_set_if_not_authorized()
209228

210229
$mockEvent = $this->createMock(GetResponseEvent::class);
211230

231+
$mockEvent
232+
->method('getRequest')
233+
->willReturn(new Request());
234+
212235
$mockEvent->expects($this->once())
213236
->method('getRequestType')
214237
->willReturn(HttpKernelInterface::MASTER_REQUEST);
@@ -254,6 +277,10 @@ public function test_that_username_is_set_from_user_interface_if_token_present_a
254277

255278
$mockEvent = $this->createMock(GetResponseEvent::class);
256279

280+
$mockEvent
281+
->method('getRequest')
282+
->willReturn(new Request());
283+
257284
$mockEvent
258285
->expects($this->once())
259286
->method('getRequestType')
@@ -303,6 +330,10 @@ public function test_that_username_is_set_from_user_interface_if_token_present_a
303330

304331
$mockEvent = $this->createMock(GetResponseEvent::class);
305332

333+
$mockEvent
334+
->method('getRequest')
335+
->willReturn(new Request());
336+
306337
$mockEvent
307338
->expects($this->once())
308339
->method('getRequestType')
@@ -358,6 +389,10 @@ public function test_that_username_is_set_from_user_interface_if_token_present_a
358389

359390
$mockEvent = $this->createMock(GetResponseEvent::class);
360391

392+
$mockEvent
393+
->method('getRequest')
394+
->willReturn(new Request());
395+
361396
$mockEvent
362397
->expects($this->once())
363398
->method('getRequestType')
@@ -404,6 +439,10 @@ public function test_that_ip_is_set_from_request_stack()
404439

405440
$mockEvent = $this->createMock(GetResponseEvent::class);
406441

442+
$mockEvent
443+
->method('getRequest')
444+
->willReturn(new Request());
445+
407446
$mockRequest = $this->createMock(Request::class);
408447

409448
$mockRequest
@@ -438,6 +477,59 @@ public function test_that_ip_is_set_from_request_stack()
438477
$listener->onKernelRequest($mockEvent);
439478
}
440479

480+
public function test_that_it_sets_transaction_from_route()
481+
{
482+
$mockEvent = $this->createMock(GetResponseEvent::class);
483+
484+
$request = new Request();
485+
$request->attributes->set('_route', 'my_route');
486+
$mockEvent
487+
->expects($this->once())
488+
->method('getRequest')
489+
->willReturn($request);
490+
491+
$mockEvent
492+
->method('getRequestType')
493+
->willReturn(HttpKernelInterface::SUB_REQUEST);
494+
495+
$mockTransactionStack = $this->createMock(\Raven_TransactionStack::class);
496+
$this->mockSentryClient->transaction = $mockTransactionStack;
497+
498+
$mockTransactionStack
499+
->expects($this->once())
500+
->method('push')
501+
->with('my_route');
502+
503+
$this->containerBuilder->compile();
504+
$listener = $this->getListener();
505+
$listener->onKernelRequest($mockEvent);
506+
}
507+
508+
public function test_that_it_pops_transaction_from_route()
509+
{
510+
$mockEvent = $this->createMock(FinishRequestEvent::class);
511+
512+
$request = new Request();
513+
$request->attributes->set('_route', 'my_route');
514+
$mockEvent
515+
->expects($this->once())
516+
->method('getRequest')
517+
->willReturn($request);
518+
519+
$mockTransactionStack = $this->createMock(\Raven_TransactionStack::class);
520+
$this->mockSentryClient->transaction = $mockTransactionStack;
521+
522+
$mockTransactionStack
523+
->expects($this->once())
524+
->method('pop')
525+
->with('my_route');
526+
527+
$this->containerBuilder->compile();
528+
/** @var ExceptionListener $listener */
529+
$listener = $this->getListener();
530+
$listener->onFinishRequest($mockEvent);
531+
}
532+
441533
public function test_regression_with_unauthenticated_user_token_PR_78()
442534
{
443535
$mockToken = $this->createMock(TokenInterface::class);
@@ -447,6 +539,10 @@ public function test_regression_with_unauthenticated_user_token_PR_78()
447539

448540
$mockEvent = $this->createMock(GetResponseEvent::class);
449541

542+
$mockEvent
543+
->method('getRequest')
544+
->willReturn(new Request());
545+
450546
$mockEvent
451547
->expects($this->once())
452548
->method('getRequestType')
@@ -465,6 +561,10 @@ public function test_that_it_does_not_report_http_exception_if_included_in_captu
465561
{
466562
$mockEvent = $this->createMock(GetResponseForExceptionEvent::class);
467563

564+
$mockEvent
565+
->method('getRequest')
566+
->willReturn(new Request());
567+
468568
$mockEvent
469569
->expects($this->once())
470570
->method('getException')
@@ -510,6 +610,30 @@ public function test_that_it_captures_exception()
510610
$listener->onKernelException($mockEvent);
511611
}
512612

613+
public function test_that_it_sets_transaction_from_command()
614+
{
615+
$mockEvent = $this->createMock(ConsoleCommandEvent::class);
616+
617+
$command = new Command('my_command');
618+
$mockEvent
619+
->expects($this->once())
620+
->method('getCommand')
621+
->willReturn($command);
622+
623+
$mockTransactionStack = $this->createMock(\Raven_TransactionStack::class);
624+
$this->mockSentryClient->transaction = $mockTransactionStack;
625+
626+
$mockTransactionStack
627+
->expects($this->once())
628+
->method('push')
629+
->with('my_command');
630+
631+
$this->containerBuilder->compile();
632+
/** @var ExceptionListener $listener */
633+
$listener = $this->getListener();
634+
$listener->onConsoleCommand($mockEvent);
635+
}
636+
513637
/**
514638
* @dataProvider mockCommandProvider
515639
*/
@@ -593,6 +717,30 @@ public function test_that_it_captures_console_error(?Command $mockCommand, strin
593717
$listener->onConsoleError($event);
594718
}
595719

720+
public function test_that_it_pops_transaction_from_command()
721+
{
722+
$mockEvent = $this->createMock(ConsoleTerminateEvent::class);
723+
724+
$command = new Command('my_command');
725+
$mockEvent
726+
->expects($this->once())
727+
->method('getCommand')
728+
->willReturn($command);
729+
730+
$mockTransactionStack = $this->createMock(\Raven_TransactionStack::class);
731+
$this->mockSentryClient->transaction = $mockTransactionStack;
732+
733+
$mockTransactionStack
734+
->expects($this->once())
735+
->method('pop')
736+
->with('my_command');
737+
738+
$this->containerBuilder->compile();
739+
/** @var ExceptionListener $listener */
740+
$listener = $this->getListener();
741+
$listener->onFinishCommand($mockEvent);
742+
}
743+
596744
public function mockCommandProvider()
597745
{
598746
$mockCommand = $this->createMock(Command::class);

0 commit comments

Comments
 (0)