o11y-8.x-1.x-dev/modules/o11y_metrics/modules/o11y_metrics_requests/src/EventSubscriber/RequestCollector.php
modules/o11y_metrics/modules/o11y_metrics_requests/src/EventSubscriber/RequestCollector.php
<?php
namespace Drupal\o11y_metrics_requests\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Drupal\Component\Utility\Timer;
use Drupal\Core\Config\ConfigFactoryInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\TerminateEvent;
use Drupal\o11y_metrics\Bridge\PrometheusBridgeInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\o11y_metrics\BaseMetricsSourceInterface;
/**
* Gathers prometheus http metrics on kernel request+terminate.
*/
class RequestCollector implements EventSubscriberInterface, BaseMetricsSourceInterface {
const TIMER_NAME = 'prometheus_metrics_request_timer';
const CONFIG_NAME = 'o11y_metrics_requests.settings';
/**
* The promphp bridge.
*
* @var \Drupal\o11y_metrics\Bridge\PrometheusBridgeInterface
*/
protected $promBridge;
/**
* The current_route_match service.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* The prometheusio requests config object.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
/**
* {@inheritdoc}
*/
public function __construct(
PrometheusBridgeInterface $promBridge,
RouteMatchInterface $routeMatch,
ConfigFactoryInterface $config
) {
$this->promBridge = $promBridge;
$this->routeMatch = $routeMatch;
$this->config = $config->get(static::CONFIG_NAME);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
return [
KernelEvents::REQUEST => ['startPrometheusMetricsTimer', 1000],
KernelEvents::TERMINATE => ['collectPrometheusMetricsTimer', 0],
];
}
/**
* {@inheritdoc}
*/
public static function getMetricsSourceId(): string {
return 'requests';
}
/**
* Event callback.
*
* Starts the request timer.
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function startPrometheusMetricsTimer(RequestEvent $event) {
Timer::start(self::TIMER_NAME);
}
/**
* Event callback.
*
* Stops the request timer and collect events.
*/
public function collectPrometheusMetricsTimer(TerminateEvent $event) {
if ($this->routeMatch->getRouteName() == 'o11y_metrics.metrics') {
return;
}
$routeName = str_replace('.', '_', $this->routeMatch->getRouteName());
$routeObject = $this->routeMatch->getRouteObject();
if (empty($routeName) || !$routeObject) {
return;
}
$isAdminRoute = $routeObject->getOptions()['_admin_route'] ?? FALSE;
if ($isAdminRoute && ($this->config->get('exclude_admin_paths'))) {
return;
}
Timer::stop(self::TIMER_NAME);
$timeInMs = Timer::read(self::TIMER_NAME);
$timeInS = $timeInMs / 1000;
$request = $event->getRequest();
$method = $request->getMethod();
$this->promBridge->getCounter(
'drupal',
'http_requests_total',
'Total number of requests.',
[],
$this
)->inc();
$httpCode = (((string) $event->getResponse()->getStatusCode())[0] ?? 'x') . 'xx';
$buckets = $this->config->get('buckets');
$this->promBridge->getHistogram(
'drupal',
'http_requests',
'Timing metrics for requests.',
[
'method',
'route',
'status',
],
empty($buckets) ? NULL : $buckets,
$this
)
->observe($timeInS, [$method, $routeName, $httpCode]);
}
}
