Skip to content

Commit 99baa06

Browse files
rjd22Jean85
authored andcommitted
Implementation of symfony events sentry tracing with tests
1 parent 07007cc commit 99baa06

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
@@ -25,6 +25,16 @@ parameters:
2525
count: 2
2626
path: src/aliases.php
2727

28+
-
29+
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterResponseEvent not found\\.$#"
30+
count: 1
31+
path: src/aliases.php
32+
33+
-
34+
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\PostResponseEvent not found\\.$#"
35+
count: 1
36+
path: src/aliases.php
37+
2838
-
2939
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterControllerEvent not found\\.$#"
3040
count: 1

phpstan.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ parameters:
88
- tests
99
excludes_analyse:
1010
- tests/End2End/App
11+
- tests/EventListener/Tracing/RequestListenerTest.php
1112
dynamicConstantNames:
1213
- Symfony\Component\HttpKernel\Kernel::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
@@ -59,6 +59,15 @@
5959
<tag name="kernel.event_listener" event="Symfony\Component\Messenger\Event\WorkerMessageHandledEvent" method="handleWorkerMessageHandledEvent" priority="50" />
6060
</service>
6161

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

src/aliases.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,19 @@
55
use Sentry\SentryBundle\EventListener\ErrorListenerExceptionEvent;
66
use Sentry\SentryBundle\EventListener\RequestListenerControllerEvent;
77
use Sentry\SentryBundle\EventListener\RequestListenerRequestEvent;
8+
use Sentry\SentryBundle\EventListener\RequestListenerResponseEvent;
9+
use Sentry\SentryBundle\EventListener\RequestListenerTerminateEvent;
810
use Sentry\SentryBundle\EventListener\SubRequestListenerRequestEvent;
911
use Symfony\Component\HttpKernel\Event\ControllerEvent;
1012
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
1113
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
14+
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
1215
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
1316
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
17+
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
1418
use Symfony\Component\HttpKernel\Event\RequestEvent;
19+
use Symfony\Component\HttpKernel\Event\ResponseEvent;
20+
use Symfony\Component\HttpKernel\Event\TerminateEvent;
1521
use Symfony\Component\HttpKernel\Kernel;
1622

1723
if (version_compare(Kernel::VERSION, '4.3.0', '>=')) {
@@ -30,6 +36,16 @@ class_alias(RequestEvent::class, RequestListenerRequestEvent::class);
3036
class_alias(ControllerEvent::class, RequestListenerControllerEvent::class);
3137
}
3238

39+
if (!class_exists(RequestListenerResponseEvent::class, false)) {
40+
/** @psalm-suppress UndefinedClass */
41+
class_alias(ResponseEvent::class, RequestListenerResponseEvent::class);
42+
}
43+
44+
if (!class_exists(RequestListenerTerminateEvent::class, false)) {
45+
/** @psalm-suppress UndefinedClass */
46+
class_alias(TerminateEvent::class, RequestListenerTerminateEvent::class);
47+
}
48+
3349
if (!class_exists(SubRequestListenerRequestEvent::class, false)) {
3450
/** @psalm-suppress UndefinedClass */
3551
class_alias(RequestEvent::class, SubRequestListenerRequestEvent::class);
@@ -50,6 +66,16 @@ class_alias(GetResponseEvent::class, RequestListenerRequestEvent::class);
5066
class_alias(FilterControllerEvent::class, RequestListenerControllerEvent::class);
5167
}
5268

69+
if (!class_exists(RequestListenerResponseEvent::class, false)) {
70+
/** @psalm-suppress UndefinedClass */
71+
class_alias(FilterResponseEvent::class, RequestListenerResponseEvent::class);
72+
}
73+
74+
if (!class_exists(RequestListenerTerminateEvent::class, false)) {
75+
/** @psalm-suppress UndefinedClass */
76+
class_alias(PostResponseEvent::class, RequestListenerTerminateEvent::class);
77+
}
78+
5379
if (!class_exists(SubRequestListenerRequestEvent::class, false)) {
5480
/** @psalm-suppress UndefinedClass */
5581
class_alias(GetResponseEvent::class, SubRequestListenerRequestEvent::class);

0 commit comments

Comments
 (0)