use tide::{Request, Response, Result, Next}; use prometheus::{self, IntCounterVec, HistogramVec, TextEncoder, Encoder, register_int_counter_vec, register_histogram_vec}; use lazy_static::lazy_static; use async_trait::async_trait; use std::time::{Instant, Duration}; // 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()) }