<?php
namespace App\EventListener;
use App\Service\DataDogService;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class DataDogRequestListener
{
private $dataDogService;
private $logger;
private $spans = [];
public function __construct(DataDogService $dataDogService, LoggerInterface $logger)
{
$this->dataDogService = $dataDogService;
$this->logger = $logger;
}
public function onKernelRequest(RequestEvent $event): void
{
if ($event->getRequestType() !== HttpKernelInterface::MASTER_REQUEST) {
return;
}
$request = $event->getRequest();
// Start main request span
$span = $this->dataDogService->startSpan('http.request', [
'http.method' => $request->getMethod(),
'http.url' => $request->getRequestUri(),
'http.route' => $request->attributes->get('_route'),
'http.controller' => $request->attributes->get('_controller'),
'http.user_agent' => $request->headers->get('User-Agent'),
'http.referer' => $request->headers->get('Referer'),
'http.ip' => $request->getClientIp()
]);
$this->spans['request'] = $span;
}
public function onKernelResponse(ResponseEvent $event): void
{
if ($event->getRequestType() !== HttpKernelInterface::MASTER_REQUEST) {
return;
}
if (!isset($this->spans['request'])) {
return;
}
$response = $event->getResponse();
$request = $event->getRequest();
// End request span
$span = $this->dataDogService->endSpan($this->spans['request']);
$span['tags']['http.status_code'] = $response->getStatusCode();
$span['tags']['http.response_size'] = strlen($response->getContent());
// Send trace to DataDog
$this->dataDogService->sendTrace([$span]);
// Send performance metric
$duration = $span['duration'] / 1000000; // Convert to seconds
$route = $request->attributes->get('_route');
$route = $route ? $route : 'unknown';
$this->dataDogService->sendMetric('http.request.duration', $duration, [
'method:' . $request->getMethod(),
'status_code:' . $response->getStatusCode(),
'route:' . $route
]);
// Log request completion
$this->logger->info('Request completed', [
'method' => $request->getMethod(),
'url' => $request->getRequestUri(),
'status_code' => $response->getStatusCode(),
'duration_ms' => round($duration * 1000, 2),
'route' => $route
], ['datadog']);
// Clear spans
$this->spans = [];
}
public function onKernelException(ExceptionEvent $event): void
{
if ($event->getRequestType() !== HttpKernelInterface::MASTER_REQUEST) {
return;
}
if (!isset($this->spans['request'])) {
return;
}
$exception = $event->getThrowable();
$request = $event->getRequest();
// End request span with error
$span = $this->dataDogService->endSpan($this->spans['request'], $exception);
$span['tags']['http.status_code'] = 500;
// Send trace to DataDog
$this->dataDogService->sendTrace([$span]);
// Send error metric
$route = $request->attributes->get('_route');
$route = $route ? $route : 'unknown';
$this->dataDogService->sendMetric('http.request.errors', 1, [
'method:' . $request->getMethod(),
'route:' . $route,
'exception:' . get_class($exception)
]);
// Log error
$this->logger->error('Request failed with exception', [
'method' => $request->getMethod(),
'url' => $request->getRequestUri(),
'exception' => get_class($exception),
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'route' => $route
], ['datadog']);
// Clear spans
$this->spans = [];
}
}