use std::sync::Arc;
use std::time::Instant;
use axum::{
extract::{MatchedPath, Request, State},
http::{HeaderValue, StatusCode, header},
middleware,
response::{IntoResponse, Response},
};
pub use ff_observability::Metrics;
#[cfg_attr(not(feature = "observability"), allow(dead_code))]
pub async fn metrics_handler(State(metrics): State<Arc<Metrics>>) -> Response {
let body = metrics.render();
(
StatusCode::OK,
[(
header::CONTENT_TYPE,
HeaderValue::from_static("text/plain; version=0.0.4; charset=utf-8"),
)],
body,
)
.into_response()
}
#[cfg_attr(not(feature = "observability"), allow(dead_code))]
pub async fn http_middleware(
State(metrics): State<Arc<Metrics>>,
req: Request,
next: middleware::Next,
) -> Response {
let method: &'static str = method_as_static(req.method());
let matched = req.extensions().get::<MatchedPath>().cloned();
let start = Instant::now();
let resp = next.run(req).await;
let elapsed = start.elapsed();
let status = resp.status().as_u16();
let path: &str = matched.as_ref().map(|m| m.as_str()).unwrap_or("unknown");
metrics.record_http_request(method, path, status, elapsed);
resp
}
fn method_as_static(m: &axum::http::Method) -> &'static str {
match *m {
axum::http::Method::GET => "GET",
axum::http::Method::POST => "POST",
axum::http::Method::PUT => "PUT",
axum::http::Method::DELETE => "DELETE",
axum::http::Method::HEAD => "HEAD",
axum::http::Method::OPTIONS => "OPTIONS",
axum::http::Method::PATCH => "PATCH",
axum::http::Method::CONNECT => "CONNECT",
axum::http::Method::TRACE => "TRACE",
_ => "OTHER",
}
}