diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 8 | ||||
-rw-r--r-- | src/metrics.rs | 65 |
2 files changed, 73 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs index 6a62dcc..77a6c11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ mod database; mod frontend; mod indieauth; mod micropub; +mod metrics; use crate::indieauth::IndieAuthMiddleware; use crate::micropub::CORSMiddleware; @@ -42,6 +43,7 @@ where .build()) }); app.at("/") + .with(CORSMiddleware {}) .with(frontend::ErrorHandlerMiddleware {}) .get(frontend::mainpage) .post(frontend::onboarding_receiver); @@ -54,7 +56,13 @@ where app.at("/coffee") .with(frontend::ErrorHandlerMiddleware {}) .get(frontend::coffee); + // TODO make sure the health check actually checks the backend or something + // otherwise it'll get false-negatives for application faults like resource + // exhaustion app.at("/health").get(|_| async { Ok("OK") }); + app.at("/metrics").get(metrics::gather); + + app.with(metrics::InstrumentationMiddleware {}); app } diff --git a/src/metrics.rs b/src/metrics.rs new file mode 100644 index 0000000..0537b9d --- /dev/null +++ b/src/metrics.rs @@ -0,0 +1,65 @@ +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<S> tide::Middleware<S> for InstrumentationMiddleware +where + S: Send + Sync + Clone + 'static, +{ + async fn handle( + &self, + req: Request<S>, + 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<S>(_: Request<S>) -> Result +where + S: Send + Sync + Clone +{ + let mut buffer: Vec<u8> = vec![]; + let encoder = TextEncoder::new(); + let metric_families = prometheus::gather(); + encoder.encode(&metric_families, &mut buffer).unwrap(); + + Ok(Response::builder(200).body(buffer).build()) +} |