use std::sync::Arc;
use axum::extract::State;
use axum::http::{header, StatusCode};
use axum::response::{IntoResponse, Response};
use axum::Json;
use prometheus::{Encoder, Histogram, HistogramOpts, IntCounter, Registry, TextEncoder};
use serde_json::{json, Value};
use crate::runtime::ReadinessProbe;
pub async fn healthz() -> Json<Value> {
Json(json!({
"status": "ok",
"version": env!("CARGO_PKG_VERSION"),
}))
}
pub async fn readyz(State(probe): State<Arc<ReadinessProbe>>) -> Response {
match probe.check().await {
Ok(()) => (StatusCode::OK, Json(json!({"status": "ready"}))).into_response(),
Err(detail) => (
StatusCode::SERVICE_UNAVAILABLE,
Json(json!({
"status": "not_ready",
"detail": detail,
})),
)
.into_response(),
}
}
pub async fn metrics(State(registry): State<Arc<MetricsRegistry>>) -> Response {
let metric_families = registry.inner.gather();
let mut buffer = Vec::new();
let encoder = TextEncoder::new();
if let Err(e) = encoder.encode(&metric_families, &mut buffer) {
return (
StatusCode::INTERNAL_SERVER_ERROR,
format!("metrics encode failure: {e}"),
)
.into_response();
}
(
StatusCode::OK,
[(header::CONTENT_TYPE, encoder.format_type())],
buffer,
)
.into_response()
}
pub struct MetricsRegistry {
inner: Registry,
pub grpc_requests: IntCounter,
pub flight_queries: IntCounter,
pub eval_invocations: IntCounter,
pub search_latency: Histogram,
}
impl MetricsRegistry {
pub fn new() -> Result<Self, prometheus::Error> {
let inner = Registry::new();
let grpc_requests = IntCounter::new(
"jammi_grpc_requests_total",
"Total number of gRPC requests served (SessionService + TriggerService).",
)?;
inner.register(Box::new(grpc_requests.clone()))?;
let flight_queries = IntCounter::new(
"jammi_flight_queries_total",
"Total number of Flight SQL queries executed.",
)?;
inner.register(Box::new(flight_queries.clone()))?;
let eval_invocations = IntCounter::new(
"jammi_eval_invocations_total",
"Total number of eval RPCs invoked.",
)?;
inner.register(Box::new(eval_invocations.clone()))?;
let search_latency = Histogram::with_opts(
HistogramOpts::new(
"jammi_search_latency_seconds",
"Vector-search request latency, in seconds.",
)
.buckets(vec![
0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0,
]),
)?;
inner.register(Box::new(search_latency.clone()))?;
Ok(Self {
inner,
grpc_requests,
flight_queries,
eval_invocations,
search_latency,
})
}
pub fn inner(&self) -> &Registry {
&self.inner
}
}