use async_trait::async_trait; use lazy_static::lazy_static; use prometheus::{ self, register_histogram_vec, register_int_counter_vec, Encoder, HistogramVec, IntCounterVec, TextEncoder, }; use std::time::{Duration, Instant}; use tide::{Next, Request, Response, Result}; // Copied from https://docs.rs/prometheus/0.12.0/src/prometheus/histogram.rs.html#885-889 #[inline] fn duration_to_seconds(d: Duration) -> f64 { let nanos = f64::from(d.subsec_nanos()) / 1e9; d.as_secs() as f64 + nanos } lazy_static! { static ref HTTP_CONNS_COUNTER: IntCounterVec = register_int_counter_vec!( "http_requests_total", "Number of processed HTTP requests", &["code", "method", "url"] ) .unwrap(); static ref HTTP_REQUEST_DURATION_HISTOGRAM: HistogramVec = register_histogram_vec!( "http_request_duration_seconds", "Duration of HTTP requests", &["code", "method", "url"] ) .unwrap(); } pub struct InstrumentationMiddleware {} #[async_trait] impl tide::Middleware for InstrumentationMiddleware where S: Send + Sync + Clone + 'static, { async fn handle(&self, req: Request, next: Next<'_, S>) -> Result { let url = req.url().to_string(); let method = req.method().to_string(); // Execute the request let instant = Instant::now(); let res = next.run(req).await; let elapsed = duration_to_seconds(instant.elapsed()); // Get the code from the response let code = res.status().to_string(); HTTP_CONNS_COUNTER .with_label_values(&[&code, &method, &url]) .inc(); HTTP_REQUEST_DURATION_HISTOGRAM .with_label_values(&[&code, &method, &url]) .observe(elapsed); Ok(res) } } pub async fn gather(_: Request) -> Result where S: Send + Sync + Clone, { let mut buffer: Vec = vec![]; let encoder = TextEncoder::new(); let metric_families = prometheus::gather(); encoder.encode(&metric_families, &mut buffer).unwrap(); Ok(Response::builder(200).body(buffer).build()) }