timvw_rust_runtime/
trace.rs1use std::fmt;
25use std::task::{Context, Poll};
26#[cfg(not(target_family = "wasm"))]
27use std::time::Instant;
28
29use tower::{Layer, Service};
30use tracing::Instrument;
31#[cfg(target_family = "wasm")]
32use web_time::Instant;
33
34use crate::maybe::BoxFuture;
35use crate::MaybeSend;
36
37#[derive(Clone, Copy, Debug, Default)]
39pub struct TraceLayer {
40 _private: (),
41}
42
43impl TraceLayer {
44 pub fn new() -> Self {
46 Self::default()
47 }
48}
49
50impl<S> Layer<S> for TraceLayer {
51 type Service = TraceService<S>;
52
53 fn layer(&self, inner: S) -> Self::Service {
54 TraceService { inner }
55 }
56}
57
58#[derive(Clone, Debug)]
60pub struct TraceService<S> {
61 inner: S,
62}
63
64impl<S, ReqBody, ResBody> Service<http::Request<ReqBody>> for TraceService<S>
65where
66 S: Service<http::Request<ReqBody>, Response = http::Response<ResBody>>,
67 S::Error: fmt::Display,
68 S::Future: MaybeSend + 'static,
69{
70 type Response = S::Response;
71 type Error = S::Error;
72 type Future = BoxFuture<'static, Result<S::Response, S::Error>>;
73
74 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
75 self.inner.poll_ready(cx)
76 }
77
78 fn call(&mut self, request: http::Request<ReqBody>) -> Self::Future {
79 let uri = request.uri();
80 let url = format!(
81 "{}://{}{}",
82 uri.scheme_str().unwrap_or("http"),
83 uri.authority().map(|a| a.as_str()).unwrap_or(""),
84 uri.path()
85 );
86 let span = tracing::debug_span!(
87 "http.request",
88 http.request.method = %request.method(),
89 url.full = %url,
90 http.response.status_code = tracing::field::Empty,
91 );
92 let start = Instant::now();
93 let future = {
94 let _entered = span.enter();
95 self.inner.call(request)
96 };
97 let instrumented = span.clone();
98 Box::pin(
99 async move {
100 let result = future.await;
101 let elapsed_ms = start.elapsed().as_millis() as u64;
102 match &result {
103 Ok(response) => {
104 let status = response.status();
105 span.record("http.response.status_code", status.as_u16());
106 if status.is_server_error() {
107 tracing::warn!(status = status.as_u16(), elapsed_ms, "response");
108 } else {
109 tracing::debug!(status = status.as_u16(), elapsed_ms, "response");
110 }
111 }
112 Err(error) => tracing::warn!(%error, elapsed_ms, "request failed"),
113 }
114 result
115 }
116 .instrument(instrumented),
117 )
118 }
119}