PluginProbe
DecaLog / 4.4.0
DecaLog v4.4.0
3.0.2 3.1.0 3.10.0 3.2.0 3.3.0 3.4.0 3.4.1 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.6.3 3.7.0 3.7.1 3.8.0 3.9.0 3.9.1 4.0.0 4.1.0 4.2.0 4.3.0 4.3.1 4.4.0 4.5.0 All 75 releases
decalog / includes / libraries / prometheus / Storage / InMemory.php

InMemory.php in DecaLog 4.4.0, at includes/libraries/prometheus/Storage/InMemory.php

346 lines 10.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Prometheus\Storage;
6
7 use Prometheus\MetricFamilySamples;
8 use RuntimeException;
9
10 class InMemory implements Adapter
11 {
12
13 /**
14 * @var mixed[]
15 */
16 private $counters = [];
17
18 /**
19 * @var mixed[]
20 */
21 private $gauges = [];
22
23 /**
24 * @var mixed[]
25 */
26 private $histograms = [];
27
28 /**
29 * @return MetricFamilySamples[]
30 */
31 public function collect(): array
32 {
33 $metrics = $this->internalCollect($this->counters);
34 $metrics = array_merge($metrics, $this->internalCollect($this->gauges));
35 $metrics = array_merge($metrics, $this->collectHistograms());
36 return $metrics;
37 }
38
39 /**
40 * @deprecated use replacement method wipeStorage from Adapter interface
41 */
42 public function flushMemory(): void
43 {
44 $this->wipeStorage();
45 }
46
47 /**
48 * @inheritDoc
49 */
50 public function wipeStorage(): void
51 {
52 $this->counters = [];
53 $this->gauges = [];
54 $this->histograms = [];
55 }
56
57 /**
58 * @return MetricFamilySamples[]
59 */
60 private function collectHistograms(): array
61 {
62 $histograms = [];
63 foreach ($this->histograms as $histogram) {
64 $metaData = $histogram['meta'];
65 $data = [
66 'name' => $metaData['name'],
67 'help' => $metaData['help'],
68 'type' => $metaData['type'],
69 'labelNames' => $metaData['labelNames'],
70 'buckets' => $metaData['buckets'],
71 ];
72
73 // Add the Inf bucket so we can compute it later on
74 $data['buckets'][] = '+Inf';
75
76 $histogramBuckets = [];
77 foreach ($histogram['samples'] as $key => $value) {
78 $parts = explode(':', $key);
79 $labelValues = $parts[2];
80 $bucket = $parts[3];
81 // Key by labelValues
82 $histogramBuckets[$labelValues][$bucket] = $value;
83 }
84
85 // Compute all buckets
86 $labels = array_keys($histogramBuckets);
87 sort($labels);
88 foreach ($labels as $labelValues) {
89 $acc = 0;
90 $decodedLabelValues = $this->decodeLabelValues($labelValues);
91 foreach ($data['buckets'] as $bucket) {
92 $bucket = (string)$bucket;
93 if (!isset($histogramBuckets[$labelValues][$bucket])) {
94 $data['samples'][] = [
95 'name' => $metaData['name'] . '_bucket',
96 'labelNames' => ['le'],
97 'labelValues' => array_merge($decodedLabelValues, [$bucket]),
98 'value' => $acc,
99 ];
100 } else {
101 $acc += $histogramBuckets[$labelValues][$bucket];
102 $data['samples'][] = [
103 'name' => $metaData['name'] . '_' . 'bucket',
104 'labelNames' => ['le'],
105 'labelValues' => array_merge($decodedLabelValues, [$bucket]),
106 'value' => $acc,
107 ];
108 }
109 }
110
111 // Add the count
112 $data['samples'][] = [
113 'name' => $metaData['name'] . '_count',
114 'labelNames' => [],
115 'labelValues' => $decodedLabelValues,
116 'value' => $acc,
117 ];
118
119 // Add the sum
120 $data['samples'][] = [
121 'name' => $metaData['name'] . '_sum',
122 'labelNames' => [],
123 'labelValues' => $decodedLabelValues,
124 'value' => $histogramBuckets[$labelValues]['sum'],
125 ];
126 }
127 $histograms[] = new MetricFamilySamples($data);
128 }
129 return $histograms;
130 }
131
132 /**
133 * @param mixed[] $metrics
134 * @return MetricFamilySamples[]
135 */
136 private function internalCollect(array $metrics): array
137 {
138 $result = [];
139 foreach ($metrics as $metric) {
140 $metaData = $metric['meta'];
141 $data = [
142 'name' => $metaData['name'],
143 'help' => $metaData['help'],
144 'type' => $metaData['type'],
145 'labelNames' => $metaData['labelNames'],
146 'samples' => [],
147 ];
148 foreach ($metric['samples'] as $key => $value) {
149 $parts = explode(':', $key);
150 $labelValues = $parts[2];
151 $data['samples'][] = [
152 'name' => $metaData['name'],
153 'labelNames' => [],
154 'labelValues' => $this->decodeLabelValues($labelValues),
155 'value' => $value,
156 ];
157 }
158 $this->sortSamples($data['samples']);
159 $result[] = new MetricFamilySamples($data);
160 }
161 return $result;
162 }
163
164 /**
165 * @param mixed[] $data
166 * @return void
167 */
168 public function updateHistogram(array $data): void
169 {
170 // Initialize the sum
171 $metaKey = $this->metaKey($data);
172 if (array_key_exists($metaKey, $this->histograms) === false) {
173 $this->histograms[$metaKey] = [
174 'meta' => $this->metaData($data),
175 'samples' => [],
176 ];
177 }
178 $sumKey = $this->histogramBucketValueKey($data, 'sum');
179 if (array_key_exists($sumKey, $this->histograms[$metaKey]['samples']) === false) {
180 $this->histograms[$metaKey]['samples'][$sumKey] = 0;
181 }
182
183 $this->histograms[$metaKey]['samples'][$sumKey] += $data['value'];
184
185
186 $bucketToIncrease = '+Inf';
187 foreach ($data['buckets'] as $bucket) {
188 if ($data['value'] <= $bucket) {
189 $bucketToIncrease = $bucket;
190 break;
191 }
192 }
193
194 $bucketKey = $this->histogramBucketValueKey($data, $bucketToIncrease);
195 if (array_key_exists($bucketKey, $this->histograms[$metaKey]['samples']) === false) {
196 $this->histograms[$metaKey]['samples'][$bucketKey] = 0;
197 }
198 $this->histograms[$metaKey]['samples'][$bucketKey] += 1;
199 }
200
201 /**
202 * @param mixed[] $data
203 */
204 public function updateGauge(array $data): void
205 {
206 $metaKey = $this->metaKey($data);
207 $valueKey = $this->valueKey($data);
208 if (array_key_exists($metaKey, $this->gauges) === false) {
209 $this->gauges[$metaKey] = [
210 'meta' => $this->metaData($data),
211 'samples' => [],
212 ];
213 }
214 if (array_key_exists($valueKey, $this->gauges[$metaKey]['samples']) === false) {
215 $this->gauges[$metaKey]['samples'][$valueKey] = 0;
216 }
217 if ($data['command'] === Adapter::COMMAND_SET) {
218 $this->gauges[$metaKey]['samples'][$valueKey] = $data['value'];
219 } else {
220 $this->gauges[$metaKey]['samples'][$valueKey] += $data['value'];
221 }
222 }
223
224 /**
225 * @param mixed[] $data
226 */
227 public function updateCounter(array $data): void
228 {
229 $metaKey = $this->metaKey($data);
230 $valueKey = $this->valueKey($data);
231 if (array_key_exists($metaKey, $this->counters) === false) {
232 $this->counters[$metaKey] = [
233 'meta' => $this->metaData($data),
234 'samples' => [],
235 ];
236 }
237 if (array_key_exists($valueKey, $this->counters[$metaKey]['samples']) === false) {
238 $this->counters[$metaKey]['samples'][$valueKey] = 0;
239 }
240 if ($data['command'] === Adapter::COMMAND_SET) {
241 $this->counters[$metaKey]['samples'][$valueKey] = 0;
242 } else {
243 $this->counters[$metaKey]['samples'][$valueKey] += $data['value'];
244 }
245 }
246
247 /**
248 * @param mixed[] $data
249 * @param string|int $bucket
250 *
251 * @return string
252 */
253 private function histogramBucketValueKey(array $data, $bucket): string
254 {
255 return implode(':', [
256 $data['type'],
257 $data['name'],
258 $this->encodeLabelValues($data['labelValues']),
259 $bucket,
260 ]);
261 }
262
263 /**
264 * @param mixed[] $data
265 *
266 * @return string
267 */
268 private function metaKey(array $data): string
269 {
270 return implode(':', [
271 $data['type'],
272 $data['name'],
273 'meta'
274 ]);
275 }
276
277 /**
278 * @param mixed[] $data
279 *
280 * @return string
281 */
282 private function valueKey(array $data): string
283 {
284 return implode(':', [
285 $data['type'],
286 $data['name'],
287 $this->encodeLabelValues($data['labelValues']),
288 'value'
289 ]);
290 }
291
292 /**
293 * @param mixed[] $data
294 *
295 * @return mixed[]
296 */
297 private function metaData(array $data): array
298 {
299 $metricsMetaData = $data;
300 unset($metricsMetaData['value'], $metricsMetaData['command'], $metricsMetaData['labelValues']);
301 return $metricsMetaData;
302 }
303
304 /**
305 * @param mixed[] $samples
306 */
307 private function sortSamples(array &$samples): void
308 {
309 usort($samples, function ($a, $b): int {
310 return strcmp(implode("", $a['labelValues']), implode("", $b['labelValues']));
311 });
312 }
313
314 /**
315 * @param mixed[] $values
316 * @return string
317 * @throws RuntimeException
318 */
319 private function encodeLabelValues(array $values): string
320 {
321 $json = json_encode($values);
322 if (false === $json) {
323 throw new RuntimeException(json_last_error_msg());
324 }
325 return base64_encode($json);
326 }
327
328 /**
329 * @param string $values
330 * @return mixed[]
331 * @throws RuntimeException
332 */
333 private function decodeLabelValues(string $values): array
334 {
335 $json = base64_decode($values, true);
336 if (false === $json) {
337 throw new RuntimeException('Cannot base64 decode label values');
338 }
339 $decodedValues = json_decode($json, true);
340 if (false === $decodedValues) {
341 throw new RuntimeException(json_last_error_msg());
342 }
343 return $decodedValues;
344 }
345 }
346