Skip to content

Commit

Permalink
Implementation of symfony events sentry tracing with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rjd22 authored and Jean85 committed Feb 23, 2021
1 parent 07007cc commit 99baa06
Show file tree
Hide file tree
Showing 6 changed files with 606 additions and 0 deletions.
10 changes: 10 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ parameters:
count: 2
path: src/aliases.php

-
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterResponseEvent not found\\.$#"
count: 1
path: src/aliases.php

-
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\PostResponseEvent not found\\.$#"
count: 1
path: src/aliases.php

-
message: "#^Class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\FilterControllerEvent not found\\.$#"
count: 1
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ parameters:
- tests
excludes_analyse:
- tests/End2End/App
- tests/EventListener/Tracing/RequestListenerTest.php
dynamicConstantNames:
- Symfony\Component\HttpKernel\Kernel::VERSION
145 changes: 145 additions & 0 deletions src/EventListener/Tracing/RequestListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?php

declare(strict_types=1);

namespace Sentry\SentryBundle\EventListener\Tracing;

use Sentry\SentryBundle\EventListener\RequestListenerControllerEvent;
use Sentry\SentryBundle\EventListener\RequestListenerRequestEvent;
use Sentry\SentryBundle\EventListener\RequestListenerResponseEvent;
use Sentry\SentryBundle\EventListener\RequestListenerTerminateEvent;
use Sentry\State\HubInterface;
use Sentry\Tracing\Span;
use Sentry\Tracing\SpanContext;
use Sentry\Tracing\Transaction;
use Sentry\Tracing\TransactionContext;
use Symfony\Component\HttpFoundation\Request;

final class RequestListener
{
/**
* @var HubInterface The current hub
*/
private $hub;

/**
* @var Transaction|null
*/
private $transaction = null;

/**
* @var Span|null
*/
private $requestSpan = null;

/**
* @var Span|null
*/
private $controllerSpan = null;

/**
* @var Span|null
*/
private $responseSpan = null;

/**
* Constructor.
*
* @param HubInterface $hub The current hub
*/
public function __construct(HubInterface $hub)
{
$this->hub = $hub;
}

/**
* @param RequestListenerRequestEvent $event The event
*/
public function handleKernelRequestEvent(RequestListenerRequestEvent $event): void
{
if (!$event->isMasterRequest()) {
return;
}

/** @var Request $request */
$request = $event->getRequest();
$requestStartTime = $request->server->get('REQUEST_TIME_FLOAT', microtime(true));

$context = new TransactionContext();
$context->setOp('http.server');
$context->setName($request->getUri());
$context->setData([
'url' => $request->getUri(),
'method' => strtoupper($request->getMethod()),
]);
$context->setStartTimestamp($requestStartTime);

$transaction = $this->hub->startTransaction($context);

// Setting the Transaction on the Hub
$this->hub->setSpan($transaction);

$spanContext = new SpanContext();
$spanContext->setOp('kernel.request');
$spanContext->setStartTimestamp($requestStartTime);

$this->requestSpan = $transaction->startChild($spanContext);
$this->transaction = $transaction;
}

/**
* @param RequestListenerControllerEvent $event The event
*/
public function handleKernelControllerEvent(RequestListenerControllerEvent $event): void
{
if (!$event->isMasterRequest() || !$this->transaction instanceof Transaction) {
return;
}

if ($this->requestSpan instanceof Span) {
$this->requestSpan->finish();
}

$spanContext = new SpanContext();
$spanContext->setOp('controller');

$this->controllerSpan = $this->transaction->startChild($spanContext);
}

/**
* @param RequestListenerResponseEvent $event The event
*/
public function handleKernelResponseEvent(RequestListenerResponseEvent $event): void
{
if (!$event->isMasterRequest() || !$this->transaction instanceof Transaction) {
return;
}

$this->transaction->setHttpStatus($event->getResponse()->getStatusCode());

if ($this->controllerSpan instanceof Span) {
$this->controllerSpan->finish();
}

$spanContext = new SpanContext();
$spanContext->setOp('kernel.response');

$this->responseSpan = $this->transaction->startChild($spanContext);
}

/**
* @param RequestListenerTerminateEvent $event The event
*/
public function handleKernelTerminateEvent(RequestListenerTerminateEvent $event): void
{
if (!$this->transaction instanceof Transaction) {
return;
}

if ($this->responseSpan instanceof Span) {
$this->responseSpan->finish();
}

$this->transaction->finish();
}
}
9 changes: 9 additions & 0 deletions src/Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@
<tag name="kernel.event_listener" event="Symfony\Component\Messenger\Event\WorkerMessageHandledEvent" method="handleWorkerMessageHandledEvent" priority="50" />
</service>

<service id="Sentry\SentryBundle\EventListener\Tracing\RequestListener" class="Sentry\SentryBundle\EventListener\Tracing\RequestListener">
<argument type="service" id="Sentry\State\HubInterface" />

<tag name="kernel.event_listener" event="kernel.request" method="handleKernelRequestEvent" priority="4" />
<tag name="kernel.event_listener" event="kernel.controller" method="handleKernelControllerEvent" priority="11" />
<tag name="kernel.event_listener" event="kernel.response" method="handleKernelResponseEvent" priority="15" />
<tag name="kernel.event_listener" event="kernel.terminate" method="handleKernelTerminateEvent" priority="20" />
</service>

<service id="Sentry\SentryBundle\Command\SentryTestCommand" class="Sentry\SentryBundle\Command\SentryTestCommand">
<tag name="console.command" />
</service>
Expand Down
26 changes: 26 additions & 0 deletions src/aliases.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@
use Sentry\SentryBundle\EventListener\ErrorListenerExceptionEvent;
use Sentry\SentryBundle\EventListener\RequestListenerControllerEvent;
use Sentry\SentryBundle\EventListener\RequestListenerRequestEvent;
use Sentry\SentryBundle\EventListener\RequestListenerResponseEvent;
use Sentry\SentryBundle\EventListener\RequestListenerTerminateEvent;
use Sentry\SentryBundle\EventListener\SubRequestListenerRequestEvent;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\Event\TerminateEvent;
use Symfony\Component\HttpKernel\Kernel;

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

if (!class_exists(RequestListenerResponseEvent::class, false)) {
/** @psalm-suppress UndefinedClass */
class_alias(ResponseEvent::class, RequestListenerResponseEvent::class);
}

if (!class_exists(RequestListenerTerminateEvent::class, false)) {
/** @psalm-suppress UndefinedClass */
class_alias(TerminateEvent::class, RequestListenerTerminateEvent::class);
}

if (!class_exists(SubRequestListenerRequestEvent::class, false)) {
/** @psalm-suppress UndefinedClass */
class_alias(RequestEvent::class, SubRequestListenerRequestEvent::class);
Expand All @@ -50,6 +66,16 @@ class_alias(GetResponseEvent::class, RequestListenerRequestEvent::class);
class_alias(FilterControllerEvent::class, RequestListenerControllerEvent::class);
}

if (!class_exists(RequestListenerResponseEvent::class, false)) {
/** @psalm-suppress UndefinedClass */
class_alias(FilterResponseEvent::class, RequestListenerResponseEvent::class);
}

if (!class_exists(RequestListenerTerminateEvent::class, false)) {
/** @psalm-suppress UndefinedClass */
class_alias(PostResponseEvent::class, RequestListenerTerminateEvent::class);
}

if (!class_exists(SubRequestListenerRequestEvent::class, false)) {
/** @psalm-suppress UndefinedClass */
class_alias(GetResponseEvent::class, SubRequestListenerRequestEvent::class);
Expand Down

0 comments on commit 99baa06

Please sign in to comment.