use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Transport {
Http,
Grpc,
}
impl Transport {
pub fn as_label(&self) -> &'static str {
match self {
Transport::Http => "http",
Transport::Grpc => "grpc",
}
}
}
pub trait ApiMetricsBackend: Send + Sync + std::fmt::Debug {
fn record_request(
&self,
_transport: Transport,
_method: &str,
_path: &str,
_status: u16,
_duration_ms: u64,
) {
}
fn record_in_flight_delta(&self, _transport: Transport, _delta: i64) {}
}
#[derive(Debug, Default)]
pub struct NoOpApiMetrics;
impl ApiMetricsBackend for NoOpApiMetrics {}
pub type ApiMetricsHandle = Arc<dyn ApiMetricsBackend>;
pub fn noop_api_metrics() -> ApiMetricsHandle {
Arc::new(NoOpApiMetrics)
}
#[cfg(feature = "http")]
pub async fn http_metrics_middleware(
axum::extract::State(metrics): axum::extract::State<ApiMetricsHandle>,
request: axum::extract::Request,
next: axum::middleware::Next,
) -> axum::response::Response {
let method = request.method().as_str().to_string();
let path = request
.extensions()
.get::<axum::extract::MatchedPath>()
.map(|mp| mp.as_str().to_string())
.unwrap_or_else(|| request.uri().path().to_string());
metrics.record_in_flight_delta(Transport::Http, 1);
let start = std::time::Instant::now();
let response = next.run(request).await;
let duration_ms = start.elapsed().as_millis() as u64;
let status = response.status().as_u16();
metrics.record_request(Transport::Http, &method, &path, status, duration_ms);
metrics.record_in_flight_delta(Transport::Http, -1);
response
}