use crate::http::Request;
use crate::http::Response;
use crate::metrics;
use crate::middleware::{Middleware, Next};
use async_trait::async_trait;
use std::time::Instant;
#[derive(Debug, Clone, Copy, Default)]
pub struct MetricsMiddleware;
impl MetricsMiddleware {
pub fn new() -> Self {
Self
}
}
#[async_trait]
impl Middleware for MetricsMiddleware {
async fn handle(&self, request: Request, next: Next) -> Response {
if !metrics::is_enabled() {
return next(request).await;
}
let path = request.path();
if path.starts_with("/_ferro/") {
return next(request).await;
}
let start = Instant::now();
let method = request.method().to_string();
let route_pattern = request
.route_pattern()
.unwrap_or_else(|| "UNMATCHED".to_string());
let response = next(request).await;
let duration = start.elapsed();
let is_error = match &response {
Ok(resp) => resp.status_code() >= 400,
Err(resp) => resp.status_code() >= 400,
};
metrics::record_request(&route_pattern, &method, duration, is_error);
response
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_metrics_middleware_new() {
let middleware = MetricsMiddleware::new();
assert!(format!("{middleware:?}").contains("MetricsMiddleware"));
}
#[test]
fn test_metrics_middleware_default() {
let middleware = MetricsMiddleware;
assert!(format!("{middleware:?}").contains("MetricsMiddleware"));
}
#[test]
fn test_metrics_middleware_clone() {
let middleware = MetricsMiddleware::new();
let cloned = middleware;
assert!(format!("{cloned:?}").contains("MetricsMiddleware"));
}
#[test]
fn test_metrics_middleware_copy() {
let middleware = MetricsMiddleware::new();
let copied: MetricsMiddleware = middleware; let _original = middleware; assert!(format!("{copied:?}").contains("MetricsMiddleware"));
}
}