Skip to content

Commit eeb9cd4

Browse files
rjd22Jean85
authored andcommitted
Implementation of symfony events sentry tracing with tests
1 parent de86b84 commit eeb9cd4

File tree

6 files changed

+606
-0
lines changed

6 files changed

+606
-0
lines changed

phpstan-baseline.neon

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,16 @@ parameters:
105105
count: 2
106106
path: src/aliases.php
107107

108+
-
109+
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterResponseEvent not found\\.$#"
110+
count: 1
111+
path: src/aliases.php
112+
113+
-
114+
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\PostResponseEvent not found\\.$#"
115+
count: 1
116+
path: src/aliases.php
117+
108118
-
109119
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterControllerEvent not found\\.$#"
110120
count: 1

phpstan.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ parameters:
88
- tests
99
excludes_analyse:
1010
- tests/End2End/App
11+
- tests/EventListener/Tracing/RequestListenerTest.php
1112
dynamicConstantNames:
1213
- Symfony\Component\HttpKernel\Kernel::VERSION
1314
- Doctrine\DBAL\Version::VERSION
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\SentryBundle\EventListener\Tracing;
6+
7+
use Sentry\SentryBundle\EventListener\RequestListenerControllerEvent;
8+
use Sentry\SentryBundle\EventListener\RequestListenerRequestEvent;
9+
use Sentry\SentryBundle\EventListener\RequestListenerResponseEvent;
10+
use Sentry\SentryBundle\EventListener\RequestListenerTerminateEvent;
11+
use Sentry\State\HubInterface;
12+
use Sentry\Tracing\Span;
13+
use Sentry\Tracing\SpanContext;
14+
use Sentry\Tracing\Transaction;
15+
use Sentry\Tracing\TransactionContext;
16+
use Symfony\Component\HttpFoundation\Request;
17+
18+
final class RequestListener
19+
{
20+
/**
21+
* @var HubInterface The current hub
22+
*/
23+
private $hub;
24+
25+
/**
26+
* @var Transaction|null
27+
*/
28+
private $transaction = null;
29+
30+
/**
31+
* @var Span|null
32+
*/
33+
private $requestSpan = null;
34+
35+
/**
36+
* @var Span|null
37+
*/
38+
private $controllerSpan = null;
39+
40+
/**
41+
* @var Span|null
42+
*/
43+
private $responseSpan = null;
44+
45+
/**
46+
* Constructor.
47+
*
48+
* @param HubInterface $hub The current hub
49+
*/
50+
public function __construct(HubInterface $hub)
51+
{
52+
$this->hub = $hub;
53+
}
54+
55+
/**
56+
* @param RequestListenerRequestEvent $event The event
57+
*/
58+
public function handleKernelRequestEvent(RequestListenerRequestEvent $event): void
59+
{
60+
if (!$event->isMasterRequest()) {
61+
return;
62+
}
63+
64+
/** @var Request $request */
65+
$request = $event->getRequest();
66+
$requestStartTime = $request->server->get('REQUEST_TIME_FLOAT', microtime(true));
67+
68+
$context = new TransactionContext();
69+
$context->setOp('http.server');
70+
$context->setName($request->getUri());
71+
$context->setData([
72+
'url' => $request->getUri(),
73+
'method' => strtoupper($request->getMethod()),
74+
]);
75+
$context->setStartTimestamp($requestStartTime);
76+
77+
$transaction = $this->hub->startTransaction($context);
78+
79+
// Setting the Transaction on the Hub
80+
$this->hub->setSpan($transaction);
81+
82+
$spanContext = new SpanContext();
83+
$spanContext->setOp('kernel.request');
84+
$spanContext->setStartTimestamp($requestStartTime);
85+
86+
$this->requestSpan = $transaction->startChild($spanContext);
87+
$this->transaction = $transaction;
88+
}
89+
90+
/**
91+
* @param RequestListenerControllerEvent $event The event
92+
*/
93+
public function handleKernelControllerEvent(RequestListenerControllerEvent $event): void
94+
{
95+
if (!$event->isMasterRequest() || !$this->transaction instanceof Transaction) {
96+
return;
97+
}
98+
99+
if ($this->requestSpan instanceof Span) {
100+
$this->requestSpan->finish();
101+
}
102+
103+
$spanContext = new SpanContext();
104+
$spanContext->setOp('controller');
105+
106+
$this->controllerSpan = $this->transaction->startChild($spanContext);
107+
}
108+
109+
/**
110+
* @param RequestListenerResponseEvent $event The event
111+
*/
112+
public function handleKernelResponseEvent(RequestListenerResponseEvent $event): void
113+
{
114+
if (!$event->isMasterRequest() || !$this->transaction instanceof Transaction) {
115+
return;
116+
}
117+
118+
$this->transaction->setHttpStatus($event->getResponse()->getStatusCode());
119+
120+
if ($this->controllerSpan instanceof Span) {
121+
$this->controllerSpan->finish();
122+
}
123+
124+
$spanContext = new SpanContext();
125+
$spanContext->setOp('kernel.response');
126+
127+
$this->responseSpan = $this->transaction->startChild($spanContext);
128+
}
129+
130+
/**
131+
* @param RequestListenerTerminateEvent $event The event
132+
*/
133+
public function handleKernelTerminateEvent(RequestListenerTerminateEvent $event): void
134+
{
135+
if (!$this->transaction instanceof Transaction) {
136+
return;
137+
}
138+
139+
if ($this->responseSpan instanceof Span) {
140+
$this->responseSpan->finish();
141+
}
142+
143+
$this->transaction->finish();
144+
}
145+
}

src/Resources/config/services.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@
6161
<tag name="kernel.event_listener" event="Symfony\Component\Messenger\Event\WorkerMessageHandledEvent" method="handleWorkerMessageHandledEvent" priority="50" />
6262
</service>
6363

64+
<service id="Sentry\SentryBundle\EventListener\Tracing\RequestListener" class="Sentry\SentryBundle\EventListener\Tracing\RequestListener">
65+
<argument type="service" id="Sentry\State\HubInterface" />
66+
67+
<tag name="kernel.event_listener" event="kernel.request" method="handleKernelRequestEvent" priority="4" />
68+
<tag name="kernel.event_listener" event="kernel.controller" method="handleKernelControllerEvent" priority="11" />
69+
<tag name="kernel.event_listener" event="kernel.response" method="handleKernelResponseEvent" priority="15" />
70+
<tag name="kernel.event_listener" event="kernel.terminate" method="handleKernelTerminateEvent" priority="20" />
71+
</service>
72+
6473
<service id="Sentry\SentryBundle\Command\SentryTestCommand" class="Sentry\SentryBundle\Command\SentryTestCommand">
6574
<tag name="console.command" />
6675
</service>

src/aliases.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,22 @@
1414
use Sentry\SentryBundle\EventListener\ErrorListenerExceptionEvent;
1515
use Sentry\SentryBundle\EventListener\RequestListenerControllerEvent;
1616
use Sentry\SentryBundle\EventListener\RequestListenerRequestEvent;
17+
use Sentry\SentryBundle\EventListener\RequestListenerResponseEvent;
18+
use Sentry\SentryBundle\EventListener\RequestListenerTerminateEvent;
1719
use Sentry\SentryBundle\EventListener\SubRequestListenerRequestEvent;
1820
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\Compatibility\DriverInterface;
1921
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\Compatibility\ExceptionConverterDriverInterface;
2022
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\Compatibility\MiddlewareInterface;
2123
use Symfony\Component\HttpKernel\Event\ControllerEvent;
2224
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
2325
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
26+
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
2427
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
2528
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
29+
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
2630
use Symfony\Component\HttpKernel\Event\RequestEvent;
31+
use Symfony\Component\HttpKernel\Event\ResponseEvent;
32+
use Symfony\Component\HttpKernel\Event\TerminateEvent;
2733
use Symfony\Component\HttpKernel\Kernel;
2834

2935
if (version_compare(Kernel::VERSION, '4.3.0', '>=')) {
@@ -42,6 +48,16 @@ class_alias(RequestEvent::class, RequestListenerRequestEvent::class);
4248
class_alias(ControllerEvent::class, RequestListenerControllerEvent::class);
4349
}
4450

51+
if (!class_exists(RequestListenerResponseEvent::class, false)) {
52+
/** @psalm-suppress UndefinedClass */
53+
class_alias(ResponseEvent::class, RequestListenerResponseEvent::class);
54+
}
55+
56+
if (!class_exists(RequestListenerTerminateEvent::class, false)) {
57+
/** @psalm-suppress UndefinedClass */
58+
class_alias(TerminateEvent::class, RequestListenerTerminateEvent::class);
59+
}
60+
4561
if (!class_exists(SubRequestListenerRequestEvent::class, false)) {
4662
/** @psalm-suppress UndefinedClass */
4763
class_alias(RequestEvent::class, SubRequestListenerRequestEvent::class);
@@ -62,6 +78,16 @@ class_alias(GetResponseEvent::class, RequestListenerRequestEvent::class);
6278
class_alias(FilterControllerEvent::class, RequestListenerControllerEvent::class);
6379
}
6480

81+
if (!class_exists(RequestListenerResponseEvent::class, false)) {
82+
/** @psalm-suppress UndefinedClass */
83+
class_alias(FilterResponseEvent::class, RequestListenerResponseEvent::class);
84+
}
85+
86+
if (!class_exists(RequestListenerTerminateEvent::class, false)) {
87+
/** @psalm-suppress UndefinedClass */
88+
class_alias(PostResponseEvent::class, RequestListenerTerminateEvent::class);
89+
}
90+
6591
if (!class_exists(SubRequestListenerRequestEvent::class, false)) {
6692
/** @psalm-suppress UndefinedClass */
6793
class_alias(GetResponseEvent::class, SubRequestListenerRequestEvent::class);

0 commit comments

Comments
 (0)