Skip to content

Commit 7922374

Browse files
committed
Initial implementation of sentry tracing with tests
1 parent ed64c02 commit 7922374

File tree

4 files changed

+594
-0
lines changed

4 files changed

+594
-0
lines changed
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
27+
*/
28+
private $transaction;
29+
30+
/**
31+
* @var Span
32+
*/
33+
private $requestSpan;
34+
35+
/**
36+
* @var Span
37+
*/
38+
private $controllerSpan;
39+
40+
/**
41+
* @var Span
42+
*/
43+
private $responseSpan;
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) {
96+
return;
97+
}
98+
99+
if ($this->requestSpan) {
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) {
115+
return;
116+
}
117+
118+
$this->transaction->setHttpStatus($event->getResponse()->getStatusCode());
119+
120+
if ($this->controllerSpan) {
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) {
136+
return;
137+
}
138+
139+
if ($this->responseSpan) {
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: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,18 @@
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;
1417
use Symfony\Component\HttpKernel\Event\RequestEvent;
18+
use Symfony\Component\HttpKernel\Event\ResponseEvent;
19+
use Symfony\Component\HttpKernel\Event\TerminateEvent;
1520
use Symfony\Component\HttpKernel\Kernel;
1621

1722
if (version_compare(Kernel::VERSION, '4.3.0', '>=')) {
@@ -30,6 +35,16 @@ class_alias(RequestEvent::class, RequestListenerRequestEvent::class);
3035
class_alias(ControllerEvent::class, RequestListenerControllerEvent::class);
3136
}
3237

38+
if (!class_exists(RequestListenerResponseEvent::class, false)) {
39+
/** @psalm-suppress UndefinedClass */
40+
class_alias(ResponseEvent::class, RequestListenerResponseEvent::class);
41+
}
42+
43+
if (!class_exists(RequestListenerTerminateEvent::class, false)) {
44+
/** @psalm-suppress UndefinedClass */
45+
class_alias(TerminateEvent::class, RequestListenerTerminateEvent::class);
46+
}
47+
3348
if (!class_exists(SubRequestListenerRequestEvent::class, false)) {
3449
/** @psalm-suppress UndefinedClass */
3550
class_alias(RequestEvent::class, SubRequestListenerRequestEvent::class);
@@ -50,6 +65,16 @@ class_alias(GetResponseEvent::class, RequestListenerRequestEvent::class);
5065
class_alias(FilterControllerEvent::class, RequestListenerControllerEvent::class);
5166
}
5267

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

0 commit comments

Comments
 (0)