Skip to main content

tork_core/middleware/
trace.rs

1//! Request-tracing middleware.
2
3use std::time::Instant;
4
5use crate::error::Result;
6use crate::logging::Logger;
7use crate::middleware::{DuplicatePolicy, Middleware, Next, Request};
8use crate::response::Response;
9use crate::router::BoxFuture;
10
11/// Logs each request's method, path, resulting status, and elapsed time.
12///
13/// This logs before routing (so it covers short-circuited and unmatched requests);
14/// the framework's built-in HTTP log already covers matched routes, so this
15/// middleware is only needed when pre-routing coverage is wanted.
16pub struct Trace;
17
18impl Trace {
19    /// Creates the tracing middleware.
20    pub fn new() -> Self {
21        Self
22    }
23}
24
25impl Default for Trace {
26    fn default() -> Self {
27        Self::new()
28    }
29}
30
31impl Middleware for Trace {
32    fn handle(&self, request: Request, next: Next) -> BoxFuture<'static, Result<Response>> {
33        let method = request.method().clone();
34        let path = request.uri().path().to_owned();
35        let start = Instant::now();
36
37        Box::pin(async move {
38            let result = next.run(request).await;
39            let elapsed = start.elapsed();
40            let status = match &result {
41                Ok(response) => response.status(),
42                Err(error) => error.kind().status(),
43            };
44            Logger::framework("HTTP")
45                .debug(format!("{method} {path} {}", status.as_u16()))
46                .field("method", method.as_str())
47                .field("path", &path)
48                .field("status", status.as_u16())
49                .field("duration_ms", elapsed.as_millis() as u64)
50                .emit();
51            result
52        })
53    }
54
55    fn name(&self) -> &'static str {
56        "Trace"
57    }
58
59    fn duplicate_policy(&self) -> DuplicatePolicy {
60        DuplicatePolicy::Reject
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn builtin_metadata_is_stable() {
70        let middleware = Trace::new();
71        assert_eq!(middleware.name(), "Trace");
72        assert_eq!(middleware.duplicate_policy(), DuplicatePolicy::Reject);
73    }
74}