1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace ArangoClient\Prometheus; |
6
|
|
|
|
7
|
|
|
use GuzzleHttp\Psr7\StreamWrapper; |
8
|
|
|
use Psr\Http\Message\ResponseInterface; |
9
|
|
|
|
10
|
|
|
class Prometheus |
11
|
|
|
{ |
12
|
|
|
protected Metrics $output; |
13
|
|
|
|
14
|
4 |
|
public function __construct() |
15
|
|
|
{ |
16
|
4 |
|
$this->output = new Metrics(); |
17
|
|
|
} |
18
|
|
|
|
19
|
|
|
|
20
|
1 |
|
public function parseStream(ResponseInterface $from): Metrics |
21
|
|
|
{ |
22
|
1 |
|
$stream = $from->getBody(); |
23
|
1 |
|
$resource = StreamWrapper::getResource($stream); |
24
|
|
|
|
25
|
1 |
|
while ($line = stream_get_line($resource, 1000000, "\n")) { |
26
|
1 |
|
$this->handleLine($line); |
27
|
|
|
} |
28
|
1 |
|
return $this->output; |
29
|
|
|
} |
30
|
|
|
|
31
|
3 |
|
public function parseText(string $content): Metrics |
32
|
|
|
{ |
33
|
3 |
|
$fileObject = new \SplFileObject('php://memory', 'r+'); |
34
|
3 |
|
$fileObject->fwrite($content); |
35
|
3 |
|
$fileObject->rewind(); |
36
|
|
|
|
37
|
3 |
|
while ($fileObject->valid()) { |
38
|
3 |
|
$line = $fileObject->current(); |
39
|
3 |
|
if (is_string($line)) { |
40
|
3 |
|
$this->handleLine($line); |
41
|
|
|
} |
42
|
3 |
|
$fileObject->next(); |
43
|
|
|
} |
44
|
|
|
|
45
|
3 |
|
return $this->output; |
46
|
|
|
} |
47
|
|
|
|
48
|
4 |
|
protected function handleLine(string $line): void |
49
|
|
|
{ |
50
|
4 |
|
match (true) { |
51
|
4 |
|
str_starts_with($line, "# TYPE ") => $this->setType($line), |
|
|
|
|
52
|
4 |
|
str_starts_with($line, "# HELP ") => $this->setHelpText($line), |
|
|
|
|
53
|
4 |
|
default => $this->setMetric($line), |
|
|
|
|
54
|
4 |
|
}; |
55
|
|
|
} |
56
|
|
|
|
57
|
4 |
|
protected function createMetricIfNew(string $name): void |
58
|
|
|
{ |
59
|
4 |
|
if (!property_exists($this->output, $name)) { |
60
|
4 |
|
$this->output->$name = new Metric($name); |
61
|
|
|
} |
62
|
|
|
} |
63
|
|
|
|
64
|
4 |
|
protected function setType(string $line): void |
65
|
|
|
{ |
66
|
4 |
|
$matches = []; |
67
|
|
|
if ( |
68
|
4 |
|
preg_match( |
69
|
4 |
|
"/^# TYPE (?'metricName'[a-zA-Z_:][a-zA-Z0-9_:]*) (?'type'counter|gauge|histogram|summary)$/", |
70
|
4 |
|
$line, |
71
|
4 |
|
$matches, |
72
|
4 |
|
) |
73
|
|
|
) { |
74
|
4 |
|
$metricName = $matches['metricName']; |
75
|
4 |
|
$this->createMetricIfNew($metricName); |
76
|
|
|
|
77
|
4 |
|
$this->output->$metricName->type = $matches['type']; |
78
|
|
|
} |
79
|
|
|
} |
80
|
|
|
|
81
|
4 |
|
protected function setHelpText(string $line): void |
82
|
|
|
{ |
83
|
4 |
|
$matches = []; |
84
|
|
|
if ( |
85
|
4 |
|
preg_match( |
86
|
4 |
|
"/^# HELP (?'metricName'[a-zA-Z_:][a-zA-Z0-9_:]*) (?'helpText'[\W\w]*)/", |
87
|
4 |
|
$line, |
88
|
4 |
|
$matches, |
89
|
4 |
|
) |
90
|
|
|
) { |
91
|
4 |
|
$metricName = $matches['metricName']; |
92
|
|
|
|
93
|
4 |
|
$this->createMetricIfNew($metricName); |
94
|
|
|
|
95
|
4 |
|
$this->output->$metricName->help = $matches['helpText']; |
96
|
|
|
} |
97
|
|
|
} |
98
|
|
|
|
99
|
4 |
|
protected function setMetric(string $line): void |
100
|
|
|
{ |
101
|
4 |
|
$matches = []; |
102
|
|
|
if ( |
103
|
4 |
|
preg_match( |
104
|
4 |
|
"/^(?'rawMetricName'[a-zA-Z_:][a-zA-Z0-9_:]*)(?'rawLabels'{[\W\w]*})? (?'value'[-+.,eE\d]+) ?(?'timestamp'[0-9]+)?$/", |
105
|
4 |
|
$line, |
106
|
4 |
|
$matches, |
107
|
4 |
|
) |
108
|
|
|
) { |
109
|
|
|
// The first group contains the metricName and the suffix |
110
|
4 |
|
[$metricName, $suffix] = $this->extractMetricNameAndSuffix($matches['rawMetricName']); |
111
|
|
|
|
112
|
4 |
|
$this->createMetricIfNew($metricName); |
113
|
|
|
|
114
|
|
|
// Feed the remaining matches to the suffix dependent parser |
115
|
4 |
|
match ($suffix) { |
116
|
4 |
|
"bucket" => $this->setBucket($metricName, $matches), |
|
|
|
|
117
|
4 |
|
default => $this->setMetricValues($metricName, $matches, $suffix), |
|
|
|
|
118
|
4 |
|
}; |
119
|
|
|
} |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* @return string[] |
124
|
|
|
*/ |
125
|
4 |
|
protected function extractMetricNameAndSuffix(string $value): array |
126
|
|
|
{ |
127
|
4 |
|
$nameParticles = explode("_", $value); |
128
|
4 |
|
$suffix = array_pop($nameParticles); |
129
|
|
|
|
130
|
4 |
|
if (in_array($suffix, ["bucket", "count", "sum"])) { |
131
|
3 |
|
$baseName = implode("_", $nameParticles); |
132
|
3 |
|
return [$baseName, $suffix]; |
133
|
|
|
} |
134
|
|
|
|
135
|
3 |
|
return [$value, 'value']; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* @param array<string|int|float> $matches |
140
|
|
|
*/ |
141
|
2 |
|
protected function setBucket(mixed $metricName, array $matches): void |
142
|
|
|
{ |
143
|
2 |
|
$labels = null; |
144
|
2 |
|
$timestamp = null; |
145
|
|
|
|
146
|
2 |
|
$value = is_numeric($matches['value']) ? +$matches['value'] : $matches['value']; |
147
|
|
|
|
148
|
2 |
|
if (array_key_exists("rawLabels", $matches)) { |
149
|
2 |
|
$labels = $this->extractLabels((string) $matches['rawLabels']); |
150
|
|
|
} |
151
|
|
|
|
152
|
2 |
|
if (array_key_exists("timestamp", $matches)) { |
153
|
1 |
|
$timestamp = is_numeric($matches['timestamp']) ? +$matches['timestamp'] : $matches['timestamp']; |
154
|
|
|
} |
155
|
|
|
|
156
|
2 |
|
$bucket = new Bucket( |
157
|
2 |
|
$value, |
158
|
2 |
|
$labels, |
159
|
2 |
|
$timestamp, |
160
|
2 |
|
); |
161
|
|
|
|
162
|
2 |
|
$this->output->$metricName->buckets[] = $bucket; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* @param array<string, mixed> $matches |
167
|
|
|
*/ |
168
|
4 |
|
protected function setMetricValues(string $metricName, array $matches, string $suffix): void |
169
|
|
|
{ |
170
|
4 |
|
$this->output->$metricName->$suffix = is_numeric($matches['value']) ? +$matches['value'] : $matches['value']; |
171
|
|
|
|
172
|
4 |
|
$this->setLabels($metricName, $matches); |
173
|
|
|
|
174
|
4 |
|
$this->setTimestamp($metricName, $matches); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* @param array<string, mixed> $matches |
179
|
|
|
*/ |
180
|
4 |
|
protected function setLabels(string $metricName, array $matches): void |
181
|
|
|
{ |
182
|
4 |
|
if (array_key_exists("rawLabels", $matches)) { |
183
|
4 |
|
$this->output->$metricName->labels = $this->extractLabels($matches['rawLabels']); |
184
|
|
|
} |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* @param array<string, mixed> $matches |
189
|
|
|
*/ |
190
|
4 |
|
protected function setTimestamp(string $metricName, array $matches): void |
191
|
|
|
{ |
192
|
4 |
|
if (array_key_exists("timestamp", $matches)) { |
193
|
1 |
|
$this->output->$metricName->timestamp = is_numeric($matches['timestamp']) ? +$matches['timestamp'] : $matches['timestamp']; |
194
|
|
|
} |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* @return array<string, float|int|string> |
200
|
|
|
*/ |
201
|
4 |
|
protected function extractLabels(string $rawLabels): array |
202
|
|
|
{ |
203
|
4 |
|
$labels = []; |
204
|
4 |
|
$matches = []; |
205
|
|
|
if ( |
206
|
4 |
|
preg_match_all( |
207
|
4 |
|
"/(?'label'[a-zA-Z0-9]*)=\"(?'value'[^\"]*)\"/", |
208
|
4 |
|
$rawLabels, |
209
|
4 |
|
$matches, |
210
|
4 |
|
) |
211
|
|
|
) { |
212
|
4 |
|
foreach ($matches['label'] as $key => $label) { |
213
|
4 |
|
$value = $matches['value'][$key]; |
214
|
|
|
|
215
|
4 |
|
$labels[(string) $label] = is_numeric($value) ? +$value : $value; |
216
|
|
|
} |
217
|
|
|
} |
218
|
|
|
|
219
|
4 |
|
return $labels; |
220
|
|
|
} |
221
|
|
|
} |
222
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.