Duibonduil commited on
Commit
c532cde
·
verified ·
1 Parent(s): cabd0ca

Upload prometheus_adapter.py

Browse files
aworld/metrics/prometheus/prometheus_adapter.py ADDED
@@ -0,0 +1,386 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ import threading
3
+ from typing import Sequence, Optional, Dict, List
4
+ from prometheus_client import Counter as PCounter, Gauge as PGauge, Histogram as PHistogram, CollectorRegistry
5
+ from prometheus_client import start_http_server, REGISTRY
6
+ from aworld.metrics.metric import(
7
+ MetricProvider,
8
+ Counter,
9
+ UpDownCounter,
10
+ MetricExporter,
11
+ Gauge,
12
+ Histogram,
13
+ set_metric_provider
14
+ )
15
+
16
+
17
+ class PrometheusMetricProvider(MetricProvider):
18
+ """
19
+ PrometheusMetricProvider is a subclass of MetricProvider, representing a metric provider for Prometheus.
20
+ """
21
+
22
+ def __init__(self, exporter: MetricExporter):
23
+ """
24
+ Initialize the PrometheusMetricProvider.
25
+ Args:
26
+ port: The port to use for the Prometheus server.
27
+ """
28
+ super().__init__()
29
+ self.exporter = exporter
30
+
31
+ def shutdown(self) -> None:
32
+ """
33
+ Shutdown the PrometheusMetricProvider.
34
+ """
35
+ self.exporter.shutdown()
36
+
37
+ def create_counter(self, name: str, description: str, unit: str,
38
+ labelnames: Optional[Sequence[str]] = None) -> Counter:
39
+ """
40
+ Create a counter metric.
41
+ Args:
42
+ name: The name of the metric.
43
+ description: The description of the metric.
44
+ unit: The unit of the metric.
45
+ Returns:
46
+ The counter metric.
47
+ """
48
+ return PrometheusCounter(name, description, unit, self, labelnames)
49
+
50
+ def create_un_down_counter(self, name: str, description: str, unit: str,
51
+ labelnames: Optional[Sequence[str]] = None) -> UpDownCounter:
52
+ """
53
+ Create an up-down counter metric.
54
+ Args:
55
+ name: The name of the metric.
56
+ description: The description of the metric.
57
+ unit: The unit of the metric.
58
+ Returns:
59
+ The up-down counter metric.
60
+ """
61
+ return PrometheusUpDownCounter(name, description, unit, self, labelnames)
62
+
63
+ def create_gauge(self, name: str, description: str, unit: str, labelnames: Optional[Sequence[str]] = None) -> Gauge:
64
+ """
65
+ Create a gauge metric.
66
+ Args:
67
+ name: The name of the metric.
68
+ description: The description of the metric.
69
+ unit: The unit of the metric.
70
+ Returns:
71
+ The gauge metric.
72
+ """
73
+ return PrometheusGauge(name, description, unit, self, labelnames)
74
+
75
+ def create_histogram(self,
76
+ name: str,
77
+ description: str,
78
+ unit: str,
79
+ buckets: Optional[Sequence[float]] = None,
80
+ labelnames: Optional[Sequence[str]] = None) -> Histogram:
81
+ """
82
+ Create a histogram metric.
83
+ Args:
84
+ name: The name of the metric.
85
+ description: The description of the metric.
86
+ unit: The unit of the metric.
87
+ buckets: The buckets of the histogram.
88
+ Returns:
89
+ The histogram metric.
90
+ """
91
+ return PrometheusHistogram(name, description, unit, self, buckets, labelnames)
92
+
93
+
94
+ class PrometheusCounter(Counter):
95
+ """
96
+ PrometheusCounter is a subclass of Counter, representing a counter metric for Prometheus.
97
+ """
98
+
99
+ def __init__(self,
100
+ name: str,
101
+ description: str,
102
+ unit: str,
103
+ provider: MetricProvider,
104
+ labelnames: Optional[Sequence[str]] = None):
105
+ """
106
+ Initialize the PrometheusCounter.
107
+ Args:
108
+ name: The name of the metric.
109
+ description: The description of the metric.
110
+ unit: The unit of the metric.
111
+ provider: The provider of the metric.
112
+ """
113
+ labelnames = labelnames or []
114
+ super().__init__(name, description, unit, provider, labelnames)
115
+ self._counter = PCounter(name=name, documentation=description, labelnames=labelnames, unit=unit)
116
+
117
+ def add(self, value: int, labels: dict = None) -> None:
118
+ """
119
+ Add a value to the counter.
120
+ Args:
121
+ value: The value to add to the counter.
122
+ labels: The labels to associate with the value.
123
+ """
124
+ if labels:
125
+ self._counter.labels(**labels).inc(value)
126
+ else:
127
+ self._counter.inc(value)
128
+
129
+
130
+ class PrometheusUpDownCounter(UpDownCounter):
131
+ """
132
+ PrometheusUpDownCounter is a subclass of UpDownCounter, representing an up-down counter metric for Prometheus.
133
+ """
134
+
135
+ def __init__(self,
136
+ name: str,
137
+ description: str,
138
+ unit: str,
139
+ provider: MetricProvider,
140
+ labelnames: Optional[Sequence[str]] = None):
141
+ """
142
+ Initialize the PrometheusUpDownCounter.
143
+ Args:
144
+ name: The name of the metric.
145
+ description: The description of the metric.
146
+ unit: The unit of the metric.
147
+ provider: The provider of the metric.
148
+ """
149
+ labelnames = labelnames or []
150
+ super().__init__(name, description, unit, provider, labelnames)
151
+ self._gauge = PGauge(name=name, documentation=description, labelnames=labelnames, unit=unit)
152
+
153
+ def inc(self, value: int, labels: dict = None) -> None:
154
+ """
155
+ Add a value to the counter.
156
+ Args:
157
+ value: The value to add to the counter.
158
+ labels: The labels to associate with the value.
159
+ """
160
+ if labels:
161
+ self._gauge.labels(**labels).inc(value)
162
+ else:
163
+ self._gauge.inc(value)
164
+
165
+ def dec(self, value: int, labels: dict = None) -> None:
166
+ """
167
+ Subtract a value from the counter.
168
+ Args:
169
+ value: The value to subtract from the counter.
170
+ labels: The labels to associate with the value.
171
+ """
172
+ if labels:
173
+ self._gauge.labels(**labels).dec(value)
174
+ else:
175
+ self._gauge.dec(value)
176
+
177
+
178
+ class PrometheusGauge(Gauge):
179
+ """
180
+ PrometheusGauge is a subclass of Gauge, representing a gauge metric for Prometheus.
181
+ """
182
+
183
+ def __init__(self,
184
+ name: str,
185
+ description: str,
186
+ unit: str,
187
+ provider: MetricProvider,
188
+ labelnames: Optional[Sequence[str]] = None):
189
+ """
190
+ Initialize the PrometheusGauge.
191
+ Args:
192
+ name: The name of the metric.
193
+ description: The description of the metric.
194
+ unit: The unit of the metric.
195
+ provider: The provider of the metric.
196
+ """
197
+ labelnames = labelnames or []
198
+ super().__init__(name, description, unit, provider, labelnames)
199
+ self._gauge = PGauge(name=name, documentation=description, labelnames=labelnames, unit=unit)
200
+
201
+ def set(self, value: int, labels: dict = None) -> None:
202
+ """
203
+ Set the value of the gauge.
204
+ Args:
205
+ value: The value to set the gauge to.
206
+ labels: The labels to associate with the value.
207
+ """
208
+ if labels:
209
+ self._gauge.labels(**labels).set(value)
210
+ else:
211
+ self._gauge.set(value)
212
+
213
+ def inc(self, value: int, labels: dict = None) -> None:
214
+ """
215
+ Add a value to the gauge.
216
+ Args:
217
+ value: The value to add to the gauge.
218
+ labels: The labels to associate with the value.
219
+ """
220
+ if labels:
221
+ self._gauge.labels(**labels).inc(value)
222
+ else:
223
+ self._gauge.inc(value)
224
+
225
+ def dec(self, value: int, labels: dict = None) -> None:
226
+ """
227
+ Subtract a value from the gauge.
228
+ Args:
229
+ value: The value to subtract from the gauge.
230
+ labels: The labels to associate with the value.
231
+ """
232
+ if labels:
233
+ self._gauge.labels(**labels).dec(value)
234
+ else:
235
+ self._gauge.dec(value)
236
+
237
+
238
+ class PrometheusHistogram(Histogram):
239
+ """
240
+ PrometheusHistogram is a subclass of Histogram, representing a histogram metric for Prometheus.
241
+ """
242
+
243
+ def __init__(self,
244
+ name: str,
245
+ description: str,
246
+ unit: str,
247
+ provider: MetricProvider,
248
+ buckets: Sequence[float] = None,
249
+ labelnames: Optional[Sequence[str]] = None):
250
+ """
251
+ Initialize the PrometheusHistogram.
252
+ Args:
253
+ name: The name of the metric.
254
+ description: The description of the metric.
255
+ unit: The unit of the metric.
256
+ provider: The provider of the metric.
257
+ """
258
+ labelnames = labelnames or []
259
+ super().__init__(name, description, unit, provider, buckets, labelnames)
260
+ if buckets:
261
+ self._histogram = PHistogram(name=name, documentation=description, labelnames=labelnames, unit=unit,
262
+ buckets=buckets)
263
+ else:
264
+ self._histogram = PHistogram(name=name, documentation=description, labelnames=labelnames, unit=unit)
265
+
266
+ def record(self, value: int, labels: dict = None) -> None:
267
+ """
268
+ Record a value in the histogram.
269
+ Args:
270
+ value: The value to record in the histogram.
271
+ labels: The labels to associate with the value.
272
+ """
273
+ if labels:
274
+ self._histogram.labels(**labels).observe(value)
275
+ else:
276
+ self._histogram.observe(value)
277
+
278
+
279
+ class PrometheusMetricExporter(MetricExporter):
280
+ """
281
+ PrometheusMetricExporter is a class for exporting metrics to Prometheus.
282
+ """
283
+
284
+ def __init__(self, port: int = 8000):
285
+ """
286
+ Initialize the PrometheusMetricExporter.
287
+ Args:
288
+ port: The port to use for the Prometheus server.
289
+ """
290
+ self.port = port
291
+ server, server_thread = start_http_server(self.port)
292
+ self.server = server
293
+ self.server_thread = server_thread
294
+
295
+ def shutdown(self) -> None:
296
+ """
297
+ Shutdown the PrometheusMetricExporter.
298
+ """
299
+ self.server.shutdown()
300
+ self.server_thread.join()
301
+
302
+
303
+ class PrometheusConsoleMetricExporter(MetricExporter):
304
+ """Implementation of :class:`MetricExporter` that prints metrics to the
305
+ console.
306
+
307
+ This class can be used for diagnostic purposes. It prints the exported
308
+ metrics to the console STDOUT.
309
+ """
310
+
311
+ def __init__(self, out_interval_secs: float = 1.0):
312
+ """Initialize the console exporter."""
313
+ self._should_shutdown = False
314
+ self.out_interval_secs = out_interval_secs
315
+ self.metrics_thread = threading.Thread(target=self._output_metrics_to_console)
316
+ self.metrics_thread.daemon = True
317
+ self.metrics_thread.start()
318
+
319
+ def generate_latest(self, registry: CollectorRegistry = REGISTRY) -> bytes:
320
+ """Returns the metrics from the registry in latest text format as a string."""
321
+
322
+ def sample_line(line):
323
+ if line.labels:
324
+ labelstr = '{{{0}}}'.format(','.join(
325
+ ['{}="{}"'.format(
326
+ k, v.replace('\\', r'\\').replace('\n', r'\n').replace('"', r'\"'))
327
+ for k, v in sorted(line.labels.items())]))
328
+ else:
329
+ labelstr = ''
330
+ timestamp = ''
331
+ if line.timestamp is not None:
332
+ # Convert to milliseconds.
333
+ timestamp = f' {int(float(line.timestamp) * 1000):d}'
334
+ return f'{line.name}{labelstr} {line.value}{timestamp}\n'
335
+
336
+ output = []
337
+ for metric in registry.collect():
338
+ try:
339
+ om_samples: Dict[str, List[str]] = {}
340
+ for s in metric.samples:
341
+ for suffix in ['_gsum', '_gcount']:
342
+ if s.name == metric.name + suffix:
343
+ # OpenMetrics specific sample, put in a gauge at the end.
344
+ om_samples.setdefault(suffix, []).append(sample_line(s))
345
+ break
346
+ else:
347
+ output.append(sample_line(s))
348
+ except Exception as exception:
349
+ exception.args = (exception.args or ('',)) + (metric,)
350
+ raise
351
+
352
+ for suffix, lines in sorted(om_samples.items()):
353
+ output.extend(lines)
354
+ return ''.join(output).encode('utf-8')
355
+
356
+ def _output_metrics_to_console(self):
357
+ while not self._should_shutdown:
358
+ metrics_text = self.generate_latest(REGISTRY)
359
+ print(metrics_text.decode('utf-8'))
360
+ time.sleep(self.out_interval_secs)
361
+
362
+ def shutdown(self) -> None:
363
+ """
364
+ Shutdown the PrometheusConsoleMetricExporter.
365
+ """
366
+ self._should_shutdown = True
367
+
368
+ def configure_prometheus_provider(backend: str,
369
+ base_url: str = None,
370
+ write_token: str = None,
371
+ **kwargs
372
+ ):
373
+
374
+ """
375
+ Initialize the prometheus metric provider.
376
+ Args:
377
+ backend: The backend of the metric provider.
378
+ base_url: The base url of the metric provider.
379
+ write_token: The write token of the metric provider.
380
+ """
381
+ if backend == "console":
382
+ exporter = PrometheusConsoleMetricExporter(out_interval_secs=2)
383
+ set_metric_provider(PrometheusMetricProvider(exporter))
384
+ elif backend == "prometheus":
385
+ exporter = PrometheusMetricExporter()
386
+ set_metric_provider(PrometheusMetricProvider(exporter))