AppMetrics::setInstance()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 10
c 1
b 0
f 0
dl 0
loc 15
ccs 10
cts 10
cp 1
rs 9.9332
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Artprima\PrometheusMetricsBundle\Metrics;
6
7
use Prometheus\CollectorRegistry;
8
use Prometheus\Exception\MetricNotFoundException;
9
use Symfony\Component\HttpKernel\Event\RequestEvent;
10
use Symfony\Component\HttpKernel\Event\TerminateEvent;
11
use Symfony\Component\Stopwatch\Stopwatch;
12
13
/**
14
 * Class AppMetrics is an implementation of basic metrics collector that is turned on by default.
15
 *
16
 * Collected metrics:
17
 * - requests (per method and route)
18
 * - responses (per method, route and response type)
19
 * - request duration histogram (per method and route)
20
 */
21
class AppMetrics implements MetricsGeneratorInterface
22
{
23
    private const STOPWATCH_CLASS = '\Symfony\Component\Stopwatch\Stopwatch';
24
25
    /**
26
     * @var Stopwatch
27
     */
28
    private $stopwatch;
29
30
    /**
31
     * @var string
32
     */
33
    private $namespace;
34
35
    /**
36
     * @var CollectorRegistry
37
     */
38
    private $collectionRegistry;
39
40 33
    public function init(string $namespace, CollectorRegistry $collectionRegistry): void
41
    {
42 33
        $this->namespace = $namespace;
43 33
        $this->collectionRegistry = $collectionRegistry;
44 33
    }
45
46 15
    public function collectRequest(RequestEvent $event): void
47
    {
48 15
        $request = $event->getRequest();
49 15
        $requestMethod = $request->getMethod();
50 15
        $requestRoute = $request->attributes->get('_route');
51
52
        // do not track "OPTIONS" requests
53 15
        if ('OPTIONS' === $requestMethod) {
54 3
            return;
55
        }
56
57 12
        $this->setInstance($request->server->get('HOSTNAME') ?? 'dev');
58 12
        $this->incRequestsTotal($requestMethod, $requestRoute);
59 12
    }
60
61 21
    public function collectResponse(TerminateEvent $event): void
62
    {
63 21
        $response = $event->getResponse();
64 21
        $request = $event->getRequest();
65
66 21
        $requestMethod = $request->getMethod();
67 21
        $requestRoute = $request->attributes->get('_route');
68
69 21
        if ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300) {
70 12
            $this->incResponsesTotal('2xx', $requestMethod, $requestRoute);
71 9
        } elseif ($response->getStatusCode() >= 300 && $response->getStatusCode() < 400) {
72 3
            $this->incResponsesTotal('3xx', $requestMethod, $requestRoute);
73 6
        } elseif ($response->getStatusCode() >= 400 && $response->getStatusCode() < 500) {
74 3
            $this->incResponsesTotal('4xx', $requestMethod, $requestRoute);
75 3
        } elseif ($response->getStatusCode() >= 500) {
76 3
            $this->incResponsesTotal('5xx', $requestMethod, $requestRoute);
77
        }
78
79 21
        if ($this->stopwatch && $this->stopwatch->isStarted('execution_time')) {
80 9
            $evt = $this->stopwatch->stop('execution_time');
81 9
            if (null !== $evt) {
82 9
                $this->setRequestDuration($evt->getDuration() / 1000, $requestMethod, $requestRoute);
83
            }
84
        }
85 21
    }
86
87 9
    public function collectStart(RequestEvent $event): void
88
    {
89
        // do not track "OPTIONS" requests
90 9
        if ($event->getRequest()->isMethod('OPTIONS')) {
91
            return;
92
        }
93
94 9
        if (class_exists(self::STOPWATCH_CLASS)) {
95 9
            $className = self::STOPWATCH_CLASS;
96 9
            $this->stopwatch = new $className();
97 9
            $this->stopwatch->start('execution_time');
98
        }
99 9
    }
100
101 12
    private function setInstance(string $value): void
102
    {
103 12
        $name = 'instance_name';
104
        try {
105
            // the trick with try/catch let's us setting the instance name only once
106 12
            $this->collectionRegistry->getGauge($this->namespace, $name);
107 12
        } catch (MetricNotFoundException $e) {
108
            /** @noinspection PhpUnhandledExceptionInspection */
109 12
            $gauge = $this->collectionRegistry->registerGauge(
110 12
                $this->namespace,
111 4
                $name,
112 12
                'app instance name',
113 12
                ['instance']
114
            );
115 12
            $gauge->set(1, [$value]);
116
        }
117 12
    }
118
119 12
    private function incRequestsTotal(?string $method = null, ?string $route = null): void
120
    {
121 12
        $counter = $this->collectionRegistry->getOrRegisterCounter(
122 12
            $this->namespace,
123 12
            'http_requests_total',
124 12
            'total request count',
125 12
            ['action']
126
        );
127
128 12
        $counter->inc(['all']);
129
130 12
        if (null !== $method && null !== $route) {
131 12
            $counter->inc([sprintf('%s-%s', $method, $route)]);
132
        }
133 12
    }
134
135 21
    private function incResponsesTotal(string $type, ?string $method = null, ?string $route = null): void
136
    {
137 21
        $counter = $this->collectionRegistry->getOrRegisterCounter(
138 21
            $this->namespace,
139 21
            sprintf('http_%s_responses_total', $type),
140 21
            sprintf('total %s response count', $type),
141 21
            ['action']
142
        );
143 21
        $counter->inc(['all']);
144
145 21
        if (null !== $method && null !== $route) {
146 21
            $counter->inc([sprintf('%s-%s', $method, $route)]);
147
        }
148 21
    }
149
150 9
    private function setRequestDuration(float $duration, ?string $method = null, ?string $route = null): void
151
    {
152 9
        $histogram = $this->collectionRegistry->getOrRegisterHistogram(
153 9
            $this->namespace,
154 9
            'request_durations_histogram_seconds',
155 9
            'request durations in seconds',
156 9
            ['action']
157
        );
158 9
        $histogram->observe($duration, ['all']);
159
160 9
        if (null !== $method && null !== $route) {
161 9
            $histogram->observe($duration, [sprintf('%s-%s', $method, $route)]);
162
        }
163 9
    }
164
}
165