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;