rocket/trace/
traceable.rs

1use std::error::Error as StdError;
2
3use crate::request::ConnectionMeta;
4use crate::sentinel::Sentry;
5use crate::util::Formatter;
6use crate::{route, Catcher, Config, Error, Request, Response, Route};
7use crate::error::ErrorKind;
8
9use figment::Figment;
10use rocket::http::Header;
11use tracing::Level;
12
13pub trait Trace {
14    fn trace(&self, level: Level);
15
16    #[inline(always)] fn trace_info(&self) { self.trace(Level::INFO) }
17    #[inline(always)] fn trace_warn(&self) { self.trace(Level::WARN) }
18    #[inline(always)] fn trace_error(&self) { self.trace(Level::ERROR) }
19    #[inline(always)] fn trace_debug(&self) { self.trace(Level::DEBUG) }
20    #[inline(always)] fn trace_trace(&self) { self.trace(Level::TRACE) }
21}
22
23pub trait TraceAll: Sized {
24    fn trace_all(self, level: Level);
25
26    #[inline(always)] fn trace_all_info(self) { self.trace_all(Level::INFO) }
27    #[inline(always)] fn trace_all_warn(self) { self.trace_all(Level::WARN) }
28    #[inline(always)] fn trace_all_error(self) { self.trace_all(Level::ERROR) }
29    #[inline(always)] fn trace_all_debug(self) { self.trace_all(Level::DEBUG) }
30    #[inline(always)] fn trace_all_trace(self) { self.trace_all(Level::TRACE) }
31}
32
33impl<T: Trace, I: IntoIterator<Item = T>> TraceAll for I {
34    fn trace_all(self, level: Level) {
35        self.into_iter().for_each(|i| i.trace(level))
36    }
37}
38
39impl<T: Trace> Trace for &T {
40    #[inline(always)]
41    fn trace(&self, level: Level) {
42        T::trace(self, level)
43    }
44}
45
46impl Trace for Figment {
47    fn trace(&self, level: Level) {
48        for param in Config::PARAMETERS {
49            if let Some(source) = self.find_metadata(param) {
50                if param.contains("secret") {
51                    continue;
52                }
53
54                event! { level, "figment",
55                    param,
56                    %source.name,
57                    source.source = source.source.as_ref().map(display),
58                    value = self.find_value(param).ok().map(debug),
59                }
60            }
61        }
62
63        // Check for now deprecated config values.
64        for (key, replacement) in Config::DEPRECATED_KEYS {
65            if let Some(source) = self.find_metadata(key) {
66                event! { Level::WARN, "deprecated",
67                    key,
68                    replacement,
69                    %source.name,
70                    source.source = source.source.as_ref().map(display),
71                    "config key `{key}` is deprecated and has no meaning"
72                }
73            }
74        }
75    }
76}
77
78impl Trace for Config {
79    fn trace(&self, level: Level) {
80        event! { level, "config",
81            http2 = cfg!(feature = "http2"),
82            log_level = self.log_level.map(|l| l.as_str()),
83            log_format = ?self.log_format,
84            cli_colors = %self.cli_colors,
85            workers = self.workers,
86            max_blocking = self.max_blocking,
87            ident = %self.ident,
88            ip_header = self.ip_header.as_ref().map(|s| s.as_str()),
89            proxy_proto_header = self.proxy_proto_header.as_ref().map(|s| s.as_str()),
90            limits = %Formatter(|f| f.debug_map()
91                .entries(self.limits.limits.iter().map(|(k, v)| (k.as_str(), display(v))))
92                .finish()),
93            temp_dir = %self.temp_dir.relative().display(),
94            keep_alive = (self.keep_alive != 0).then_some(self.keep_alive),
95            shutdown.ctrlc = self.shutdown.ctrlc,
96            shutdown.signals = %{
97                #[cfg(not(unix))] {
98                    "disabled (not unix)"
99                }
100
101                #[cfg(unix)] {
102                    Formatter(|f| f.debug_set()
103                        .entries(self.shutdown.signals.iter().map(|s| s.as_str()))
104                        .finish())
105                }
106            },
107                shutdown.grace = self.shutdown.grace,
108                shutdown.mercy = self.shutdown.mercy,
109                shutdown.force = self.shutdown.force,
110        }
111
112        #[cfg(feature = "secrets")] {
113            if !self.secret_key.is_provided() {
114                warn! {
115                    name: "volatile_secret_key",
116                    "secrets enabled without configuring a stable `secret_key`\n\
117                    private/signed cookies will become unreadable after restarting\n\
118                    disable the `secrets` feature or configure a `secret_key`\n\
119                    this becomes a hard error in non-debug profiles",
120                }
121            }
122
123            let secret_key_is_known = Config::KNOWN_SECRET_KEYS.iter().any(|&key_str| {
124                let value = figment::value::Value::from(key_str);
125                self.secret_key == value.deserialize().expect("known key is valid")
126            });
127
128            if secret_key_is_known {
129                warn! {
130                    name: "insecure_secret_key",
131                    "The configured `secret_key` is exposed and insecure. \
132                    The configured key is publicly published and thus insecure. \
133                    Try generating a new key with `head -c64 /dev/urandom | base64`."
134                }
135            }
136        }
137    }
138}
139
140impl Trace for Route {
141    fn trace(&self, level: Level) {
142        event! { level, "route",
143            name = self.name.as_ref().map(|n| &**n),
144            rank = self.rank,
145            method = %Formatter(|f| match self.method {
146                Some(method) => write!(f, "{}", method),
147                None => write!(f, "[any]"),
148            }),
149            uri = %self.uri,
150            uri.base = %self.uri.base(),
151            uri.unmounted = %self.uri.unmounted(),
152            format = self.format.as_ref().map(display),
153            location = self.location.as_ref()
154                .map(|(file, line, _)| Formatter(move |f| write!(f, "{file}:{line}")))
155                .map(display),
156        }
157
158        event! { Level::DEBUG, "sentinels",
159            route = self.name.as_ref().map(|n| &**n),
160            sentinels = %Formatter(|f| {
161                f.debug_set()
162                    .entries(self.sentinels.iter().filter(|s| s.specialized).map(|s| s.type_name))
163                    .finish()
164            })
165        }
166    }
167}
168
169impl Trace for Catcher {
170    fn trace(&self, level: Level) {
171        event! { level, "catcher",
172            name = self.name.as_ref().map(|n| &**n),
173            code = %Formatter(|f| match self.code {
174                Some(code) => write!(f, "{}", code),
175                None => write!(f, "default"),
176            }),
177            rank = self.rank,
178            uri.base = %self.base(),
179            location = self.location.as_ref()
180                .map(|(file, line, _)| Formatter(move |f| write!(f, "{file}:{line}")))
181                .map(display),
182        }
183    }
184}
185
186impl Trace for &dyn crate::fairing::Fairing {
187    fn trace(&self, level: Level) {
188        self.info().trace(level)
189    }
190}
191
192impl Trace for crate::fairing::Info {
193    fn trace(&self, level: Level) {
194        event!(level, "fairing", name = self.name, kind = %self.kind)
195    }
196}
197
198impl Trace for figment::error::Kind {
199    fn trace(&self, _: Level) {
200        use figment::error::{OneOf as V, Kind::*};
201
202        match self {
203            Message(message) => error!(message),
204            InvalidType(actual, expected) => error!(%actual, expected, "invalid type"),
205            InvalidValue(actual, expected) => error!(%actual, expected, "invalid value"),
206            InvalidLength(actual, expected) => error!(%actual, expected, "invalid length"),
207            UnknownVariant(actual, v) => error!(actual, expected = %V(v), "unknown variant"),
208            UnknownField(actual, v) => error!(actual, expected = %V(v), "unknown field"),
209            UnsupportedKey(actual, v) => error!(%actual, expected = &**v, "unsupported key"),
210            MissingField(value) => error!(value = &**value, "missing field"),
211            DuplicateField(value) => error!(value, "duplicate field"),
212            ISizeOutOfRange(value) => error!(value, "out of range signed integer"),
213            USizeOutOfRange(value) => error!(value, "out of range unsigned integer"),
214            Unsupported(value) => error!(%value, "unsupported type"),
215        }
216    }
217}
218
219impl Trace for figment::Error {
220    fn trace(&self, _: Level) {
221        for e in self.clone() {
222            span_error!("config",
223                key = (!e.path.is_empty()).then_some(&e.path).and_then(|path| {
224                    let (profile, metadata) = (e.profile.as_ref()?, e.metadata.as_ref()?);
225                    Some(metadata.interpolate(profile, path))
226                }),
227                source.name = e.metadata.as_ref().map(|m| &*m.name),
228                source.source = e.metadata.as_ref().and_then(|m| m.source.as_ref()).map(display)
229                => e.kind.trace_error());
230        }
231    }
232}
233
234impl Trace for Header<'_> {
235    fn trace(&self, level: Level) {
236        event!(level, "header", name = self.name().as_str(), value = self.value());
237    }
238}
239
240impl Trace for route::Outcome<'_> {
241    fn trace(&self, level: Level) {
242        event!(level, "outcome",
243            outcome = match self {
244                Self::Success(..) => "success",
245                Self::Error(..) => "error",
246                Self::Forward(..) => "forward",
247            },
248            status = match self {
249                Self::Success(r) => r.status().code,
250                Self::Error(s) => s.code,
251                Self::Forward((_, s)) => s.code,
252            },
253        )
254    }
255}
256
257impl Trace for Response<'_> {
258    fn trace(&self, level: Level) {
259        event!(level, "response", status = self.status().code);
260    }
261}
262
263impl Trace for Error {
264    fn trace(&self, level: Level) {
265        self.kind.trace(level);
266    }
267}
268
269impl Trace for Sentry {
270    fn trace(&self, level: Level) {
271        let (file, line, col) = self.location;
272        event!(level, "sentry",
273            type_name = self.type_name,
274            location = %Formatter(|f| write!(f, "{file}:{line}:{col}"))
275        );
276    }
277}
278
279impl Trace for Request<'_> {
280    fn trace(&self, level: Level) {
281        event!(level, "request", method = %self.method(), uri = %self.uri())
282    }
283}
284
285impl Trace for ConnectionMeta {
286    fn trace(&self, level: Level) {
287        event!(level, "connection",
288            endpoint = self.peer_endpoint.as_ref().map(display),
289            certs = self.peer_certs.is_some(),
290        )
291    }
292}
293
294impl Trace for ErrorKind {
295    fn trace(&self, level: Level) {
296        use ErrorKind::*;
297
298        fn try_downcast<'a, T>(error: &'a (dyn StdError + 'static)) -> Option<&'a T>
299            where T: StdError + 'static
300        {
301            error.downcast_ref().or_else(|| error.source()?.downcast_ref())
302        }
303
304        match self {
305            Bind(endpoint, error) => {
306                if let Some(e) = try_downcast::<crate::Error>(&**error) {
307                    e.trace(level);
308                } else if let Some(e) = try_downcast::<figment::Error>(&**error) {
309                    e.trace(level);
310                } else {
311                    event!(level, "error::bind",
312                        reason = %error,
313                        endpoint = endpoint.as_ref().map(display),
314                        "binding to network interface failed"
315                    )
316                }
317            }
318            Io(reason) => event!(level, "error::io", %reason, "i/o error"),
319            Config(error) => error.trace(level),
320            Collisions { routes, catchers }=> {
321                span!(level, "collision",
322                    route.pairs = routes.len(),
323                    catcher.pairs = catchers.len(),
324                    "colliding items detected"
325                ).in_scope(|| {
326                    for (a, b) in routes {
327                        span!(level, "colliding route pair").in_scope(|| {
328                            a.trace(level);
329                            b.trace(level);
330                        })
331                    }
332
333                    for (a, b) in catchers {
334                        span!(level, "colliding catcher pair").in_scope(|| {
335                            a.trace(level);
336                            b.trace(level);
337                        })
338                    }
339
340                    span!(Level::INFO, "collisions can usually be resolved by ranking items");
341                });
342            }
343            FailedFairings(fairings) => {
344                let span = span!(level, "failed ignite fairings", count = fairings.len());
345                span.in_scope(|| fairings.iter().trace_all(level));
346            },
347            SentinelAborts(sentries) => {
348                let span = span!(level, "sentries", "sentry launch abort");
349                span.in_scope(|| sentries.iter().trace_all(level));
350            }
351            InsecureSecretKey(profile) => event!(level, "insecure_key", %profile,
352                "secrets enabled in a non-debug profile without a stable `secret_key`\n\
353                disable the `secrets` feature or configure a `secret_key`"
354            ),
355            Liftoff(_, reason) => event!(level, "panic", %reason, "liftoff fairing failed"),
356            Shutdown(_) => event!(level, "shutdown", "shutdown failed"),
357        }
358    }
359}