arize-phoenix 3.16.1__py3-none-any.whl → 3.16.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of arize-phoenix might be problematic. Click here for more details.
- {arize_phoenix-3.16.1.dist-info → arize_phoenix-3.16.2.dist-info}/METADATA +4 -2
- {arize_phoenix-3.16.1.dist-info → arize_phoenix-3.16.2.dist-info}/RECORD +12 -11
- phoenix/core/project.py +24 -11
- phoenix/server/app.py +8 -0
- phoenix/server/main.py +6 -0
- phoenix/server/prometheus.py +75 -0
- phoenix/server/static/index.js +54 -54
- phoenix/trace/dsl/helpers.py +22 -3
- phoenix/version.py +1 -1
- {arize_phoenix-3.16.1.dist-info → arize_phoenix-3.16.2.dist-info}/WHEEL +0 -0
- {arize_phoenix-3.16.1.dist-info → arize_phoenix-3.16.2.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-3.16.1.dist-info → arize_phoenix-3.16.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: arize-phoenix
|
|
3
|
-
Version: 3.16.
|
|
3
|
+
Version: 3.16.2
|
|
4
4
|
Summary: AI Observability and Evaluation
|
|
5
5
|
Project-URL: Documentation, https://docs.arize.com/phoenix/
|
|
6
6
|
Project-URL: Issues, https://github.com/Arize-ai/phoenix/issues
|
|
@@ -17,7 +17,6 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Requires-Python: <3.13,>=3.8
|
|
20
|
-
Requires-Dist: ddsketch
|
|
21
20
|
Requires-Dist: hdbscan>=0.8.33
|
|
22
21
|
Requires-Dist: jinja2
|
|
23
22
|
Requires-Dist: numpy
|
|
@@ -44,6 +43,8 @@ Requires-Dist: typing-extensions>=4.6; python_version >= '3.12'
|
|
|
44
43
|
Requires-Dist: umap-learn
|
|
45
44
|
Requires-Dist: uvicorn
|
|
46
45
|
Requires-Dist: wrapt
|
|
46
|
+
Provides-Extra: container
|
|
47
|
+
Requires-Dist: prometheus-client; extra == 'container'
|
|
47
48
|
Provides-Extra: dev
|
|
48
49
|
Requires-Dist: anthropic; extra == 'dev'
|
|
49
50
|
Requires-Dist: arize[autoembeddings,llm-evaluation]; extra == 'dev'
|
|
@@ -57,6 +58,7 @@ Requires-Dist: llama-index>=0.10.3; extra == 'dev'
|
|
|
57
58
|
Requires-Dist: nbqa; extra == 'dev'
|
|
58
59
|
Requires-Dist: pandas-stubs<=2.0.2.230605; extra == 'dev'
|
|
59
60
|
Requires-Dist: pre-commit; extra == 'dev'
|
|
61
|
+
Requires-Dist: prometheus-client; extra == 'dev'
|
|
60
62
|
Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
61
63
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
62
64
|
Requires-Dist: pytest-lazy-fixture; extra == 'dev'
|
|
@@ -4,13 +4,13 @@ phoenix/datetime_utils.py,sha256=D955QLrkgrrSdUM6NyqbCeAu2SMsjhR5rHVQEsVUdng,277
|
|
|
4
4
|
phoenix/exceptions.py,sha256=X5k9ipUDfwSCwZB-H5zFJLas86Gf9tAx0W4l5TZxp5k,108
|
|
5
5
|
phoenix/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
6
6
|
phoenix/services.py,sha256=f6AeyKTuOpy9RCcTCjVH3gx5nYZhbTMFOuv1WSUOB5o,4992
|
|
7
|
-
phoenix/version.py,sha256=
|
|
7
|
+
phoenix/version.py,sha256=6An59m8khxMeeR51SwvjJubBK8eqW_S7vJrujMumRKc,23
|
|
8
8
|
phoenix/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
phoenix/core/embedding_dimension.py,sha256=zKGbcvwOXgLf-yrJBpQyKtd-LEOPRKHnUToyAU8Owis,87
|
|
10
10
|
phoenix/core/model.py,sha256=C-kDATyJEgP-oqYVKOiQM76Ljs66F6VZdT93_b8kTGk,4725
|
|
11
11
|
phoenix/core/model_schema.py,sha256=lQaTvKS34yurHOJ53YD020uURLfgG3dqKC1NLQftOjA,50222
|
|
12
12
|
phoenix/core/model_schema_adapter.py,sha256=3GkyzqUST4fYi-Bgs8qAam5hwMCdQRZTDLjZ9Bnzdm4,8268
|
|
13
|
-
phoenix/core/project.py,sha256=
|
|
13
|
+
phoenix/core/project.py,sha256=wtpfifivrQUOb3Tj91wBzk6q2_aKFA1FGjz9tRIVptY,25805
|
|
14
14
|
phoenix/core/traces.py,sha256=qAlsDmQJhS9Pkl1IFm-jne-8xS7MEqtw5Q1Ohv9TT2w,3432
|
|
15
15
|
phoenix/datasets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
phoenix/datasets/dataset.py,sha256=scKVZ7zc6Dpc_ntt-pWhzY-KWqOJEwKePuyNnKSVTGE,30515
|
|
@@ -55,8 +55,9 @@ phoenix/pointcloud/pointcloud.py,sha256=4zAIkKs2xOUbchpj4XDAV-iPMXrfAJ15TG6rlIYG
|
|
|
55
55
|
phoenix/pointcloud/projectors.py,sha256=zO_RrtDYSv2rqVOfIP2_9Cv11Dc8EmcZR94xhFcBYPU,1057
|
|
56
56
|
phoenix/pointcloud/umap_parameters.py,sha256=lJsEOrbSuSiqI7g4Yt6xj7kgYxEqoep4ZHWLr6VWBqw,1760
|
|
57
57
|
phoenix/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
58
|
-
phoenix/server/app.py,sha256=
|
|
59
|
-
phoenix/server/main.py,sha256=
|
|
58
|
+
phoenix/server/app.py,sha256=kika5W-3Uy17iqNdRVB-6KdtBEiIrCB_pQRC4LobroI,7170
|
|
59
|
+
phoenix/server/main.py,sha256=zhSu11gEAADD86T2QimmRrIyXlMKrOR2UAkVT_0BlUU,9718
|
|
60
|
+
phoenix/server/prometheus.py,sha256=YQXwXZt3kxXN5JqGKDMH_jI9r4IL23xn6c8Bdrvg15A,2368
|
|
60
61
|
phoenix/server/thread_server.py,sha256=dP6cm6Cf08jNhDA1TRlVZpziu1YgtPDmaeIJMm725eI,2154
|
|
61
62
|
phoenix/server/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
63
|
phoenix/server/api/context.py,sha256=wjCzq4QlszKG1iN-xgu5rRLYPqdvTFqX02aFYPipNoQ,512
|
|
@@ -127,7 +128,7 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
|
|
|
127
128
|
phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
|
|
128
129
|
phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
|
|
129
130
|
phoenix/server/static/index.css,sha256=KKGpx4iwF91VGRm0YN-4cn8oC-oIqC6HecoPf0x3ZM8,1885
|
|
130
|
-
phoenix/server/static/index.js,sha256=
|
|
131
|
+
phoenix/server/static/index.js,sha256=Bcnr7c5XdUC2kyU57-MIsPpNcDfMCNQP1DIYBROQAa0,3182685
|
|
131
132
|
phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
|
|
132
133
|
phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
133
134
|
phoenix/server/templates/index.html,sha256=lO2wGA5XsftPg03rw_VcyaYf_4vegtlWbIT5ms4fA_c,1982
|
|
@@ -154,7 +155,7 @@ phoenix/trace/trace_dataset.py,sha256=RpHIfZLbMmULOIb-fKXJkQLhIdC0sJlAOTjlyJppMY
|
|
|
154
155
|
phoenix/trace/utils.py,sha256=7LurVGXn245cjj4MJsc7v6jq4DSJkpK6YGBfIaSywuw,1307
|
|
155
156
|
phoenix/trace/dsl/__init__.py,sha256=WIQIjJg362XD3s50OsPJJ0xbDsGp41bSv7vDllLrPuA,144
|
|
156
157
|
phoenix/trace/dsl/filter.py,sha256=paLpcSMnHdgCfcvcroaqOoCe2retAZ5ocp_5cNTnv9s,14167
|
|
157
|
-
phoenix/trace/dsl/helpers.py,sha256=
|
|
158
|
+
phoenix/trace/dsl/helpers.py,sha256=urPNmk_D2ZpoKxYWS5DQsQt0nb38O0CoG3wNjOit-yM,2610
|
|
158
159
|
phoenix/trace/dsl/missing.py,sha256=BWPOHr2_tBkPDgVeq8GVXXVbNbJiBelu4NtwHBg6mTE,1435
|
|
159
160
|
phoenix/trace/dsl/query.py,sha256=k0guhWBEo6L7ZJH5FJs2-iGSnWXdUUqu09gd-8M4CGg,14783
|
|
160
161
|
phoenix/trace/langchain/__init__.py,sha256=F37GfD1pd5Kuw7R7iRUM1zXXpO8xEcycNZh5dwqBXNk,109
|
|
@@ -171,8 +172,8 @@ phoenix/utilities/error_handling.py,sha256=7b5rpGFj9EWZ8yrZK1IHvxB89suWk3lggDayU
|
|
|
171
172
|
phoenix/utilities/logging.py,sha256=lDXd6EGaamBNcQxL4vP1au9-i_SXe0OraUDiJOcszSw,222
|
|
172
173
|
phoenix/utilities/project.py,sha256=qWsvKnG1oKhOFUowXf9qiOL2ia7jaFe_ijFFHEt8GJo,431
|
|
173
174
|
phoenix/utilities/span_store.py,sha256=13UK0rE4wQd70yl___WsDRnH0ru-xErng9_Ml7zfEwE,978
|
|
174
|
-
arize_phoenix-3.16.
|
|
175
|
-
arize_phoenix-3.16.
|
|
176
|
-
arize_phoenix-3.16.
|
|
177
|
-
arize_phoenix-3.16.
|
|
178
|
-
arize_phoenix-3.16.
|
|
175
|
+
arize_phoenix-3.16.2.dist-info/METADATA,sha256=woqWRuz22Oi3v7sZupUCnEuztlm4ahUYgjEwJADYOwM,29287
|
|
176
|
+
arize_phoenix-3.16.2.dist-info/WHEEL,sha256=bq9SyP5NxIRA9EpQgMCd-9RmPHWvbH-4lTDGwxgIR64,87
|
|
177
|
+
arize_phoenix-3.16.2.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
|
|
178
|
+
arize_phoenix-3.16.2.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
|
|
179
|
+
arize_phoenix-3.16.2.dist-info/RECORD,,
|
phoenix/core/project.py
CHANGED
|
@@ -20,7 +20,6 @@ from typing import (
|
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
import numpy as np
|
|
23
|
-
from ddsketch import DDSketch
|
|
24
23
|
from google.protobuf.json_format import MessageToDict
|
|
25
24
|
from openinference.semconv.trace import SpanAttributes
|
|
26
25
|
from pandas import DataFrame, Index, MultiIndex
|
|
@@ -216,10 +215,15 @@ class _Spans:
|
|
|
216
215
|
self._start_time_sorted_root_spans: SortedKeyList[WrappedSpan] = SortedKeyList(
|
|
217
216
|
key=lambda span: span.start_time,
|
|
218
217
|
)
|
|
218
|
+
"""
|
|
219
|
+
A root span is defined to be a span whose parent span is not in our collection.
|
|
220
|
+
This includes spans whose parent is None and spans whose parent has not arrived
|
|
221
|
+
(or will not arrive). For spans whose parent is not None, the root span status
|
|
222
|
+
is temporary and will be revoked when its parent span arrives.
|
|
223
|
+
"""
|
|
219
224
|
self._latency_sorted_root_spans: SortedKeyList[WrappedSpan] = SortedKeyList(
|
|
220
225
|
key=lambda span: span[ComputedAttributes.LATENCY_MS],
|
|
221
226
|
)
|
|
222
|
-
self._root_span_latency_ms_sketch = DDSketch()
|
|
223
227
|
self._token_count_total: int = 0
|
|
224
228
|
self._last_updated_at: Optional[datetime] = None
|
|
225
229
|
|
|
@@ -285,7 +289,15 @@ class _Spans:
|
|
|
285
289
|
def root_span_latency_ms_quantiles(self, probability: float) -> Optional[float]:
|
|
286
290
|
"""Root span latency quantiles in milliseconds"""
|
|
287
291
|
with self._lock:
|
|
288
|
-
|
|
292
|
+
spans = self._latency_sorted_root_spans
|
|
293
|
+
if not (n := len(spans)):
|
|
294
|
+
return None
|
|
295
|
+
if probability >= 1:
|
|
296
|
+
return cast(float, spans[-1][ComputedAttributes.LATENCY_MS])
|
|
297
|
+
if probability <= 0:
|
|
298
|
+
return cast(float, spans[0][ComputedAttributes.LATENCY_MS])
|
|
299
|
+
k = max(0, round(n * probability) - 1)
|
|
300
|
+
return cast(float, spans[k][ComputedAttributes.LATENCY_MS])
|
|
289
301
|
|
|
290
302
|
def get_descendant_spans(self, span_id: SpanID) -> Iterator[WrappedSpan]:
|
|
291
303
|
for span in self._get_descendant_spans(span_id):
|
|
@@ -353,26 +365,27 @@ class _Spans:
|
|
|
353
365
|
return
|
|
354
366
|
|
|
355
367
|
parent_span_id = span.parent_id
|
|
356
|
-
|
|
357
|
-
if not is_root_span:
|
|
368
|
+
if parent_span_id is not None:
|
|
358
369
|
self._child_spans[parent_span_id].add(span)
|
|
359
370
|
self._parent_span_ids[span_id] = parent_span_id
|
|
360
371
|
|
|
372
|
+
for child_span in self._child_spans.get(span_id, ()):
|
|
373
|
+
# A root span is a span whose parent span is not in our collection.
|
|
374
|
+
# Now that their parent span has arrived, they are no longer root spans.
|
|
375
|
+
self._start_time_sorted_root_spans.remove(child_span)
|
|
376
|
+
self._latency_sorted_root_spans.remove(child_span)
|
|
377
|
+
|
|
361
378
|
# Add computed attributes to span
|
|
362
379
|
start_time = span.start_time
|
|
363
380
|
end_time = span.end_time
|
|
364
|
-
span[ComputedAttributes.LATENCY_MS] =
|
|
365
|
-
end_time - start_time
|
|
366
|
-
).total_seconds() * 1000
|
|
367
|
-
if is_root_span:
|
|
368
|
-
self._root_span_latency_ms_sketch.add(latency)
|
|
381
|
+
span[ComputedAttributes.LATENCY_MS] = (end_time - start_time).total_seconds() * 1000
|
|
369
382
|
span[ComputedAttributes.ERROR_COUNT] = int(span.status_code is SpanStatusCode.ERROR)
|
|
370
383
|
|
|
371
384
|
# Store the new span (after adding computed attributes)
|
|
372
385
|
self._spans[span_id] = span
|
|
373
386
|
self._traces[span.context.trace_id].add(span)
|
|
374
387
|
self._start_time_sorted_spans.add(span)
|
|
375
|
-
if
|
|
388
|
+
if parent_span_id is None or parent_span_id not in self._spans:
|
|
376
389
|
self._start_time_sorted_root_spans.add(span)
|
|
377
390
|
self._latency_sorted_root_spans.add(span)
|
|
378
391
|
self._propagate_cumulative_values(span)
|
phoenix/server/app.py
CHANGED
|
@@ -151,6 +151,7 @@ def create_app(
|
|
|
151
151
|
span_store: Optional[SpanStore] = None,
|
|
152
152
|
debug: bool = False,
|
|
153
153
|
read_only: bool = False,
|
|
154
|
+
enable_prometheus: bool = False,
|
|
154
155
|
) -> Starlette:
|
|
155
156
|
graphql = GraphQLWithContext(
|
|
156
157
|
schema=schema,
|
|
@@ -160,9 +161,16 @@ def create_app(
|
|
|
160
161
|
export_path=export_path,
|
|
161
162
|
graphiql=True,
|
|
162
163
|
)
|
|
164
|
+
if enable_prometheus:
|
|
165
|
+
from phoenix.server.prometheus import PrometheusMiddleware
|
|
166
|
+
|
|
167
|
+
prometheus_middlewares = [Middleware(PrometheusMiddleware)]
|
|
168
|
+
else:
|
|
169
|
+
prometheus_middlewares = []
|
|
163
170
|
return Starlette(
|
|
164
171
|
middleware=[
|
|
165
172
|
Middleware(HeadersMiddleware),
|
|
173
|
+
*prometheus_middlewares,
|
|
166
174
|
],
|
|
167
175
|
debug=debug,
|
|
168
176
|
routes=(
|
phoenix/server/main.py
CHANGED
|
@@ -129,6 +129,7 @@ if __name__ == "__main__":
|
|
|
129
129
|
parser.add_argument("--no-internet", action="store_true")
|
|
130
130
|
parser.add_argument("--umap_params", type=str, required=False, default=DEFAULT_UMAP_PARAMS_STR)
|
|
131
131
|
parser.add_argument("--debug", action="store_false")
|
|
132
|
+
parser.add_argument("--enable-prometheus", type=bool, default=False)
|
|
132
133
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
133
134
|
serve_parser = subparsers.add_parser("serve")
|
|
134
135
|
datasets_parser = subparsers.add_parser("datasets")
|
|
@@ -223,6 +224,10 @@ if __name__ == "__main__":
|
|
|
223
224
|
)
|
|
224
225
|
read_only = args.read_only
|
|
225
226
|
logger.info(f"Server umap params: {umap_params}")
|
|
227
|
+
if enable_prometheus := args.enable_prometheus:
|
|
228
|
+
from phoenix.server.prometheus import start_prometheus
|
|
229
|
+
|
|
230
|
+
start_prometheus()
|
|
226
231
|
app = create_app(
|
|
227
232
|
export_path=export_path,
|
|
228
233
|
model=model,
|
|
@@ -232,6 +237,7 @@ if __name__ == "__main__":
|
|
|
232
237
|
debug=args.debug,
|
|
233
238
|
read_only=read_only,
|
|
234
239
|
span_store=span_store,
|
|
240
|
+
enable_prometheus=enable_prometheus,
|
|
235
241
|
)
|
|
236
242
|
host = args.host or get_env_host()
|
|
237
243
|
port = args.port or get_env_port()
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from threading import Thread
|
|
3
|
+
|
|
4
|
+
import psutil
|
|
5
|
+
from prometheus_client import (
|
|
6
|
+
Counter,
|
|
7
|
+
Gauge,
|
|
8
|
+
Histogram,
|
|
9
|
+
start_http_server,
|
|
10
|
+
)
|
|
11
|
+
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
|
|
12
|
+
from starlette.requests import Request
|
|
13
|
+
from starlette.responses import Response
|
|
14
|
+
from starlette.routing import Match
|
|
15
|
+
|
|
16
|
+
REQUESTS_PROCESSING_TIME = Histogram(
|
|
17
|
+
name="starlette_requests_processing_time_seconds",
|
|
18
|
+
documentation="Histogram of requests processing time by method and path (in seconds)",
|
|
19
|
+
labelnames=["method", "path"],
|
|
20
|
+
)
|
|
21
|
+
EXCEPTIONS = Counter(
|
|
22
|
+
name="starlette_exceptions_total",
|
|
23
|
+
documentation="Total count of exceptions raised by method, path and exception type",
|
|
24
|
+
labelnames=["method", "path", "exception_type"],
|
|
25
|
+
)
|
|
26
|
+
RAM_METRIC = Gauge(
|
|
27
|
+
name="memory_usage_bytes",
|
|
28
|
+
documentation="Memory usage in bytes",
|
|
29
|
+
labelnames=["type"],
|
|
30
|
+
)
|
|
31
|
+
CPU_METRIC = Gauge(
|
|
32
|
+
name="cpu_usage_percent",
|
|
33
|
+
documentation="CPU usage percent",
|
|
34
|
+
labelnames=["core"],
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class PrometheusMiddleware(BaseHTTPMiddleware):
|
|
39
|
+
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
|
|
40
|
+
for route in request.app.routes:
|
|
41
|
+
match, _ = route.matches(request.scope)
|
|
42
|
+
if match is Match.FULL:
|
|
43
|
+
path = route.path
|
|
44
|
+
break
|
|
45
|
+
else:
|
|
46
|
+
return await call_next(request)
|
|
47
|
+
method = request.method
|
|
48
|
+
start_time = time.perf_counter()
|
|
49
|
+
try:
|
|
50
|
+
response = await call_next(request)
|
|
51
|
+
except BaseException as e:
|
|
52
|
+
EXCEPTIONS.labels(method=method, path=path, exception_type=type(e).__name__).inc()
|
|
53
|
+
raise
|
|
54
|
+
stop_time = time.perf_counter()
|
|
55
|
+
REQUESTS_PROCESSING_TIME.labels(method=method, path=path).observe(stop_time - start_time)
|
|
56
|
+
return response
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def start_prometheus() -> None:
|
|
60
|
+
Thread(target=gather_system_data, daemon=True).start()
|
|
61
|
+
start_http_server(9090)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def gather_system_data() -> None:
|
|
65
|
+
while True:
|
|
66
|
+
time.sleep(1)
|
|
67
|
+
|
|
68
|
+
ram = psutil.virtual_memory()
|
|
69
|
+
swap = psutil.swap_memory()
|
|
70
|
+
|
|
71
|
+
RAM_METRIC.labels(type="virtual").set(ram.used)
|
|
72
|
+
RAM_METRIC.labels(type="swap").set(swap.used)
|
|
73
|
+
|
|
74
|
+
for core, percent in enumerate(psutil.cpu_percent(interval=1, percpu=True)):
|
|
75
|
+
CPU_METRIC.labels(core=core).set(percent)
|