Completed
Pull Request — develop (#18)
by
unknown
26:13
created

AppMetrics::setRequestDuration()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

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