tide_server_timing/
middleware.rs

1use tide::{Next, Request};
2use tracing_futures::Instrument;
3
4use http_types::trace::{Metric, ServerTiming};
5
6use crate::span_ext::SpanExt;
7
8/// Middleware that captures encodes all traces in a handler as `Server-Timing` headers.
9#[derive(Debug)]
10pub struct TimingMiddleware {
11    _priv: (),
12}
13
14impl TimingMiddleware {
15    /// Create a new instance of `Timing`.
16    pub fn new() -> Self {
17        Self { _priv: () }
18    }
19}
20
21#[tide::utils::async_trait]
22impl<State: Clone + Send + Sync + 'static> tide::Middleware<State> for TimingMiddleware {
23    async fn handle(&self, req: Request<State>, next: Next<'_, State>) -> tide::Result {
24        // Create a fake span to guarantee we're always operating in a unique span.
25        // TODO: We may not need this.
26        let res = async move {
27            // Mark the root span.
28            let span = tracing::Span::current();
29            span.insert_ext(crate::SpanRootTiming);
30
31            // Run the current future to completion.
32            let mut res = async move { next.run(req).await }
33                .instrument(tracing::info_span!("tide endpoint handler"))
34                .await;
35
36            // Now access the trace from the store.
37            let span = tracing::span::Span::current();
38            span.take_ext(|timings: crate::SpanTiming| {
39                let raw_timings = timings.flatten();
40                let mut timings = ServerTiming::new();
41
42                for timing in raw_timings {
43                    let dur = match timing.end_time {
44                        Some(end_time) => end_time.duration_since(timing.start_time),
45                        None => continue, // This would be the active span, which we ignore.
46                    };
47
48                    let name = timing.id.into_u64().to_string();
49                    let desc = format!("{} ({})", timing.span_name, timing.target);
50
51                    let metric = Metric::new(name, Some(dur), Some(desc))
52                        .expect("Invalid metric formatting");
53                    timings.push(metric);
54                }
55                timings.apply(&mut res);
56            });
57            res
58        }
59        .instrument(tracing::info_span!("tide-server-wrapper"))
60        .await;
61        Ok(res)
62    }
63}