diff --git a/src/main.rs b/src/main.rs index adeff75..0b309e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,7 +33,7 @@ use self::{ data::{ActorCache, MediaCache, State}, db::Db, jobs::create_workers, - middleware::{DebugPayload, RelayResolver}, + middleware::{DebugPayload, RelayResolver, Timings}, routes::{actor, inbox, index, nodeinfo, nodeinfo_meta, statics}, }; @@ -182,6 +182,7 @@ async fn main() -> Result<(), anyhow::Error> { }; app.wrap(TracingLogger::default()) + .wrap(Timings) .service(web::resource("/").route(web::get().to(index))) .service(web::resource("/media/{path}").route(web::get().to(routes::media))) .service( diff --git a/src/middleware.rs b/src/middleware.rs index e11344d..93d6a68 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -1,7 +1,9 @@ mod payload; +mod timings; mod verifier; mod webfinger; pub(crate) use payload::DebugPayload; +pub(crate) use timings::Timings; pub(crate) use verifier::MyVerify; pub(crate) use webfinger::RelayResolver; diff --git a/src/middleware/timings.rs b/src/middleware/timings.rs new file mode 100644 index 0000000..4a0f2d5 --- /dev/null +++ b/src/middleware/timings.rs @@ -0,0 +1,72 @@ +use actix_web::dev::{Service, ServiceRequest, Transform}; +use futures_util::future::LocalBoxFuture; +use std::{ + future::{ready, Ready}, + time::Instant, +}; + +pub(crate) struct Timings; +pub(crate) struct TimingsMiddleware(S); + +struct LogOnDrop { + begin: Instant, + path: String, + method: String, +} + +impl Drop for LogOnDrop { + fn drop(&mut self) { + let duration = self.begin.elapsed(); + metrics::histogram!("relay.request.complete", duration, "path" => self.path.clone(), "method" => self.method.clone()); + } +} + +impl Transform for Timings +where + S: Service, + S::Future: 'static, +{ + type Response = S::Response; + type Error = S::Error; + type InitError = (); + type Transform = TimingsMiddleware; + type Future = Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + ready(Ok(TimingsMiddleware(service))) + } +} + +impl Service for TimingsMiddleware +where + S: Service, + S::Future: 'static, +{ + type Response = S::Response; + type Error = S::Error; + type Future = LocalBoxFuture<'static, Result>; + + fn poll_ready( + &self, + ctx: &mut core::task::Context<'_>, + ) -> std::task::Poll> { + self.0.poll_ready(ctx) + } + + fn call(&self, req: ServiceRequest) -> Self::Future { + let logger = LogOnDrop { + begin: Instant::now(), + path: req.path().to_string(), + method: req.method().to_string(), + }; + let fut = self.0.call(req); + + Box::pin(async move { + let res = fut.await; + + drop(logger); + + res + }) + } +}