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