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