Duibonduil commited on
Commit
606df4c
·
verified ·
1 Parent(s): b05bb9c

Upload opentelemetry_adapter.py

Browse files
aworld/metrics/opentelemetry/opentelemetry_adapter.py ADDED
@@ -0,0 +1,356 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import os
3
+ from urllib.parse import urljoin
4
+ from typing import Optional, Sequence
5
+ from typing_extensions import LiteralString
6
+ from uuid import uuid4
7
+ from opentelemetry import metrics
8
+ from opentelemetry.sdk.resources import Resource
9
+ from opentelemetry.semconv.resource import ResourceAttributes
10
+ from opentelemetry.sdk.metrics import MeterProvider
11
+ from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader, ConsoleMetricExporter
12
+ from aworld.metrics.metric import (
13
+ Gauge,
14
+ Histogram,
15
+ MetricProvider,
16
+ Counter,
17
+ MetricExporter,
18
+ UpDownCounter,
19
+ get_metric_provider,
20
+ set_metric_provider
21
+ )
22
+
23
+ MEMORY_FIELDS: list[LiteralString] = 'available used free active inactive buffers cached shared wired slab'.split()
24
+ """
25
+ The fields of the memory information returned by psutil.virtual_memory().
26
+ """
27
+
28
+
29
+ class OpentelemetryMetricProvider(MetricProvider):
30
+ """
31
+ MetricProvider is a class for providing metrics.
32
+ """
33
+
34
+ def __init__(self, exporter: MetricExporter = None):
35
+ """Initialize the MetricProvider.
36
+ Args:
37
+ exporter: The exporter of the metric.
38
+ """
39
+ super().__init__()
40
+ if not exporter:
41
+ exporter = ConsoleMetricExporter()
42
+ self._exporter = exporter
43
+
44
+ self._otel_provider = MeterProvider(
45
+ metric_readers=[PeriodicExportingMetricReader(
46
+ exporter=self._exporter, export_interval_millis=5000)],
47
+ resource=build_otel_resource()
48
+ )
49
+ metrics.set_meter_provider(self._otel_provider)
50
+ self._meter = self._otel_provider.get_meter("aworld")
51
+
52
+ def create_counter(self,
53
+ name: str,
54
+ description: str,
55
+ unit: str,
56
+ labelnames: Optional[Sequence[str]] = None) -> Counter:
57
+ """
58
+ Create a counter.
59
+ Args:
60
+ name: The name of the counter.
61
+ description: The description of the counter.
62
+ unit: The unit of the counter.
63
+ """
64
+ return OpentelemetryCounter(name, description, unit, self)
65
+
66
+ def create_un_down_counter(self,
67
+ name: str,
68
+ description: str,
69
+ unit: str,
70
+ labelnames: Optional[Sequence[str]] = None) -> UpDownCounter:
71
+ """
72
+ Create a un-down counter.
73
+ Args:
74
+ name: The name of the counter.
75
+ description: The description of the counter.
76
+ unit: The unit of the counter.
77
+ """
78
+ return OpentelemetryUpDownCounter(name, description, unit, self)
79
+
80
+ def create_gauge(self,
81
+ name: str,
82
+ description: str,
83
+ unit: str,
84
+ labelnames: Optional[Sequence[str]] = None) -> Gauge:
85
+ """
86
+ Create a gauge.
87
+ Args:
88
+ name: The name of the gauge.
89
+ description: The description of the gauge.
90
+ unit: The unit of the gauge.
91
+ """
92
+ return OpentelemetryGauge(name, description, unit, self)
93
+
94
+ def create_histogram(self,
95
+ name: str,
96
+ description: str,
97
+ unit: str,
98
+ buckets: Optional[Sequence[float]] = None,
99
+ labelnames: Optional[Sequence[str]] = None) -> Histogram:
100
+ """
101
+ Create a histogram.
102
+ Args:
103
+ name: The name of the histogram.
104
+ description: The description of the histogram.
105
+ unit: The unit of the histogram.
106
+ buckets: The buckets of the histogram.
107
+ """
108
+ return OpentelemetryHistogram(name, description, unit, self, buckets)
109
+
110
+ def shutdown(self):
111
+ """
112
+ Shutdown the metric provider.
113
+ """
114
+ self._exporter.shutdown()
115
+ self._otel_provider.shutdown()
116
+
117
+
118
+ class OpentelemetryCounter(Counter):
119
+ """
120
+ OpentelemetryCounter is a subclass of Counter, representing a counter metric.
121
+ A counter is a cumulative metric that represents a single numerical value that only ever goes up.
122
+ """
123
+
124
+ def __init__(self,
125
+ name: str,
126
+ description: str,
127
+ unit: str,
128
+ provider: OpentelemetryMetricProvider):
129
+ """
130
+ Initialize the Counter.
131
+ Args:
132
+ name: The name of the counter.
133
+ description: The description of the counter.
134
+ unit: The unit of the counter.
135
+ provider: The provider of the counter.
136
+ """
137
+ super().__init__(name, description, unit, provider)
138
+ self._counter = provider._meter.create_counter(
139
+ name=name, description=description, unit=unit)
140
+
141
+ def add(self, value: int, labels: dict = None) -> None:
142
+ """
143
+ Add a value to the counter.
144
+ Args:
145
+ value: The value to add to the counter.
146
+ labels: The labels to associate with the value.
147
+ """
148
+ if labels is None:
149
+ labels = {}
150
+ self._counter.add(value, labels)
151
+
152
+
153
+ class OpentelemetryUpDownCounter(UpDownCounter):
154
+ """
155
+ OpentelemetryUpDownCounter is a subclass of UpDownCounter, representing an un-down counter metric.
156
+ An un-down counter is a cumulative metric that represents a single numerical value that only ever goes up.
157
+ """
158
+
159
+ def __init__(self,
160
+ name: str,
161
+ description: str,
162
+ unit: str,
163
+ provider: OpentelemetryMetricProvider):
164
+ """
165
+ Initialize the UnDownCounter.
166
+ Args:
167
+ name: The name of the counter.
168
+ description: The description of the counter.
169
+ unit: The unit of the counter.
170
+ provider: The provider of the counter.
171
+ """
172
+ super().__init__(name, description, unit, provider)
173
+ self._counter = provider._meter.create_up_down_counter(
174
+ name=name, description=description, unit=unit)
175
+
176
+ def inc(self, value: int, labels: dict = None) -> None:
177
+ """
178
+ Add a value to the counter.
179
+ Args:
180
+ value: The value to add to the counter.
181
+ labels: The labels to associate with the value.
182
+ """
183
+ if labels is None:
184
+ labels = {}
185
+ self._counter.add(value, labels)
186
+
187
+ def dec(self, value: int, labels: dict = None) -> None:
188
+ """
189
+ Subtract a value from the counter.
190
+ Args:
191
+ value: The value to subtract from the counter.
192
+ labels: The labels to associate with the value.
193
+ """
194
+ if labels is None:
195
+ labels = {}
196
+ self._counter.add(-value, labels)
197
+
198
+
199
+ class OpentelemetryGauge(Gauge):
200
+ """
201
+ OpentelemetryGauge is a subclass of Gauge, representing a gauge metric.
202
+ A gauge is a metric that represents a single numerical value that can arbitrarily go up and down.
203
+ """
204
+
205
+ def __init__(self,
206
+ name: str,
207
+ description: str,
208
+ unit: str,
209
+ provider: OpentelemetryMetricProvider):
210
+ """
211
+ Initialize the Gauge.
212
+ Args:
213
+ name: The name of the gauge.
214
+ description: The description of the gauge.
215
+ unit: The unit of the gauge.
216
+ provider: The provider of the gauge.
217
+ """
218
+ super().__init__(name, description, unit, provider)
219
+ self._gauge = provider._meter.create_gauge(
220
+ name=name, description=description, unit=unit)
221
+
222
+ def set(self, value: int, labels: dict = None) -> None:
223
+ """
224
+ Set the value of the gauge.
225
+ Args:
226
+ value: The value to set the gauge to.
227
+ labels: The labels to associate with the value.
228
+ """
229
+ if labels is None:
230
+ labels = {}
231
+ self._gauge.set(value, labels)
232
+
233
+
234
+ class OpentelemetryHistogram(Histogram):
235
+ """
236
+ OpentelemetryHistogram is a subclass of Histogram, representing a histogram metric.
237
+ A histogram is a metric that represents the distribution of a set of values.
238
+ """
239
+
240
+ def __init__(self,
241
+ name: str,
242
+ description: str,
243
+ unit: str,
244
+ provider: OpentelemetryMetricProvider,
245
+ buckets: Sequence[float] = None):
246
+ """
247
+ Initialize the Histogram.
248
+ Args:
249
+ name: The name of the histogram.
250
+ description: The description of the histogram.
251
+ unit: The unit of the histogram.
252
+ provider: The provider of the histogram.
253
+ buckets: The buckets of the histogram.
254
+ """
255
+ super().__init__(name, description, unit, provider, buckets)
256
+ self._histogram = provider._meter.create_histogram(name=name,
257
+ description=description,
258
+ unit=unit,
259
+ explicit_bucket_boundaries_advisory=buckets)
260
+
261
+ def record(self, value: int, labels: dict = None) -> None:
262
+ """
263
+ Record a value in the histogram.
264
+ Args:
265
+ value: The value to record in the histogram.
266
+ labels: The labels to associate with the value.
267
+ """
268
+ if labels is None:
269
+ labels = {}
270
+ self._histogram.record(value, labels)
271
+
272
+
273
+ def configure_otlp_provider(backend: Sequence[str] = None,
274
+ base_url: str = None,
275
+ write_token: str = None,
276
+ **kwargs
277
+ ) -> None:
278
+ """
279
+ Configure the OpenTelemetry provider.
280
+ Args:
281
+ backends: The backends to use.
282
+ base_url: The base URL of the backend.
283
+ write_token: The write token of the backend.
284
+ **kwargs: The keyword arguments to pass to the backend.
285
+ """
286
+ import requests
287
+ from opentelemetry.exporter.otlp.proto.http import Compression
288
+ from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
289
+
290
+ if backend == "console":
291
+ set_metric_provider(OpentelemetryMetricProvider())
292
+ elif backend == "logfire":
293
+ base_url = base_url or "https://logfire-us.pydantic.dev"
294
+ headers = {'User-Agent': f'logfire/3.14.0',
295
+ 'Authorization': write_token}
296
+ session = requests.Session()
297
+ session.headers.update(headers)
298
+ exporter = OTLPMetricExporter(
299
+ endpoint=urljoin(base_url, '/v1/metrics'),
300
+ session=session,
301
+ compression=Compression.Gzip,
302
+ )
303
+ set_metric_provider(OpentelemetryMetricProvider(exporter))
304
+ elif backend == "antmonitor":
305
+ ant_otlp_endpoint = os.getenv("ANT_OTEL_ENDPOINT")
306
+ base_url = base_url or ant_otlp_endpoint
307
+ session = requests.Session()
308
+ session.timeout = 30
309
+ exporter = OTLPMetricExporter(
310
+ endpoint=base_url,
311
+ session=session,
312
+ compression=Compression.Gzip,
313
+ timeout=30
314
+ )
315
+ set_metric_provider(OpentelemetryMetricProvider(exporter))
316
+
317
+ metrics_system_enabled = kwargs.get("metrics_system_enabled") or os.getenv(
318
+ "METRICS_SYSTEM_ENABLED") or "false"
319
+ if metrics_system_enabled.lower() == "true":
320
+ instrument_system_metrics()
321
+
322
+
323
+ def instrument_system_metrics():
324
+ """
325
+ Instrument system metrics.
326
+ """
327
+ try:
328
+ from opentelemetry.instrumentation.system_metrics import (
329
+ _DEFAULT_CONFIG,
330
+ SystemMetricsInstrumentor
331
+ )
332
+ except ImportError:
333
+ raise ImportError(
334
+ "Could not import opentelemetry.instrumentation.system_metrics, please install it with `pip install opentelemetry-instrumentation-system-metrics`"
335
+ )
336
+ config = _DEFAULT_CONFIG.copy()
337
+ config['system.memory.usage'] = MEMORY_FIELDS + ['total']
338
+ config['system.memory.utilization'] = MEMORY_FIELDS
339
+ config['system.swap.utilization'] = ['used']
340
+ instrumentor = SystemMetricsInstrumentor(config=config)
341
+ otel_provider = get_metric_provider()._otel_provider
342
+ instrumentor.instrument(meter_provider=otel_provider)
343
+
344
+
345
+ def build_otel_resource():
346
+ """
347
+ Build the OpenTelemetry resource.
348
+ """
349
+ service_name = os.getenv("MONITOR_SERVICE_NAME") or "aworld"
350
+ return Resource(
351
+ attributes={
352
+ ResourceAttributes.SERVICE_NAME: service_name,
353
+ ResourceAttributes.SERVICE_NAMESPACE: "aworld",
354
+ ResourceAttributes.SERVICE_INSTANCE_ID: uuid4().hex
355
+ }
356
+ )