ifc_lite_server/routes/
metrics.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! `GET /api/v1/metrics` - Prometheus text exposition of the admission
6//! gauges/counters, resident memory, and the disk-cache size gauges
7//! (`ifc_server_cache_entries`, `ifc_server_cache_bytes`). Hand-rolled text
8//! (no exporter dependency); the route only responds when
9//! `IFC_METRICS_ENABLED` is set, and it sits behind the bearer-token layer
10//! like every compute route.
11//!
12//! The two cache gauges cost a FULL walk of the cacache index on every
13//! scrape (`DiskCache::stats`), so their cost grows with the number of
14//! cached entries rather than being O(1) like the admission counters.
15//! Accepted for now: the index carries the per-entry size already, and the
16//! alternative is running bookkeeping on every cache write.
17
18use axum::extract::State;
19use axum::http::{header, StatusCode};
20use axum::response::{IntoResponse, Response};
21
22use crate::error::ApiError;
23use crate::AppState;
24
25pub async fn metrics(State(state): State<AppState>) -> Response {
26    if !state.config.metrics_enabled {
27        return ApiError::NotFound("metrics disabled".to_owned()).into_response();
28    }
29
30    let mut body = state.admission.metrics_text();
31
32    // Cache size gauges (issue #3636): the index already carries a `size` per
33    // entry, so this is a directory scan, not new bookkeeping. A scan failure
34    // (e.g. an unreadable cache dir) drops just these two lines rather than
35    // failing the whole scrape -- the admission gauges above are still useful
36    // on their own.
37    if let Ok(stats) = state.cache.stats().await {
38        body.push_str(&format!(
39            "# TYPE ifc_server_cache_entries gauge\n\
40             ifc_server_cache_entries {}\n\
41             # TYPE ifc_server_cache_bytes gauge\n\
42             ifc_server_cache_bytes {}\n",
43            stats.entries, stats.bytes,
44        ));
45    }
46
47    (
48        StatusCode::OK,
49        [(header::CONTENT_TYPE, "text/plain; version=0.0.4")],
50        body,
51    )
52        .into_response()
53}
54
55#[cfg(test)]
56#[path = "metrics_tests.rs"]
57mod metrics_tests;