From f5a8681bdf3edf4ac115957dc49c4290722e03b2 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 21 Jul 2020 15:34:27 -0700 Subject: [PATCH 01/60] WIP Signed-off-by: Eliza Weisman --- Cargo.toml | 4 + core/lib/Cargo.toml | 10 ++ core/lib/src/config/error.rs | 2 +- core/lib/src/fairing/fairings.rs | 2 +- core/lib/src/lib.rs | 1 + core/lib/src/logger.rs | 87 ++++++++------ core/lib/src/response/debug.rs | 11 +- core/lib/src/rocket.rs | 26 +++-- core/lib/src/trace.rs | 188 +++++++++++++++++++++++++++++++ 9 files changed, 278 insertions(+), 53 deletions(-) create mode 100644 core/lib/src/trace.rs diff --git a/Cargo.toml b/Cargo.toml index 3af5843fe4..ae76a49668 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,3 +43,7 @@ members = [ "examples/fairings", "examples/hello_2018", ] + +[patch.crates-io] +tracing-subscriber = { git = "https://github.com/tokio-rs/tracing", rev = "ccdd7a26d406711947802510ffdc6db99a680f8b"} +tracing-core = { git = "https://github.com/tokio-rs/tracing", rev = "ccdd7a26d406711947802510ffdc6db99a680f8b"} \ No newline at end of file diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 5f24b4297d..810f4f008d 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -45,6 +45,16 @@ atomic = "0.4" version = "0.2.9" features = ["fs", "io-std", "io-util", "rt-threaded", "sync", "signal", "macros"] +[dependencies.tracing] +version = "0.1.15" +default-features = false +features = ["log", "std"] + +[dependencies.tracing-subscriber] +version = "0.2" +default-features = false +features = ["fmt", "env-filter", "smallvec"] + [build-dependencies] yansi = "0.5" version_check = "0.9.1" diff --git a/core/lib/src/config/error.rs b/core/lib/src/config/error.rs index 678d3401f1..bb8146045c 100644 --- a/core/lib/src/config/error.rs +++ b/core/lib/src/config/error.rs @@ -76,7 +76,7 @@ impl ConfigError { error!("{} is not a known configuration environment", Paint::default(format!("[{}]", name)).bold()); info_!("in {}", Paint::default(filename.display()).bold()); - info_!("valid environments are: {}", Paint::default(valid_entries).bold()); + info_!("valid environments are: {}", Paint::default(&valid_entries).bold()); } BadEnv(ref name) => { error!("{} is not a valid ROCKET_ENV value", Paint::default(name).bold()); diff --git a/core/lib/src/fairing/fairings.rs b/core/lib/src/fairing/fairings.rs index b09ac96550..68df195163 100644 --- a/core/lib/src/fairing/fairings.rs +++ b/core/lib/src/fairing/fairings.rs @@ -88,7 +88,7 @@ impl Fairings { .collect::>() .join(", "); - info_!("{} {}: {}", Paint::default(num).bold(), kind, Paint::default(names).bold()); + info_!("{} {}: {}", Paint::default(num).bold(), kind, Paint::default(&names).bold()); } } diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index 98d49680f5..6e3a1dbe44 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -106,6 +106,7 @@ pub mod handler; pub mod fairing; pub mod error; pub mod shutdown; +pub mod trace; // Reexport of HTTP everything. pub mod http { diff --git a/core/lib/src/logger.rs b/core/lib/src/logger.rs index edace75283..0776a32cfb 100644 --- a/core/lib/src/logger.rs +++ b/core/lib/src/logger.rs @@ -1,7 +1,7 @@ //! Rocket's logging infrastructure. -use std::{fmt, env}; use std::str::FromStr; +use std::{env, fmt}; use log; use yansi::Paint; @@ -30,7 +30,7 @@ impl LoggingLevel { LoggingLevel::Critical => log::LevelFilter::Warn, LoggingLevel::Normal => log::LevelFilter::Info, LoggingLevel::Debug => log::LevelFilter::Trace, - LoggingLevel::Off => log::LevelFilter::Off + LoggingLevel::Off => log::LevelFilter::Off, } } } @@ -44,7 +44,7 @@ impl FromStr for LoggingLevel { "normal" => LoggingLevel::Normal, "debug" => LoggingLevel::Debug, "off" => LoggingLevel::Off, - _ => return Err("a log level (off, debug, normal, critical)") + _ => return Err("a log level (off, debug, normal, critical)"), }; Ok(level) @@ -57,28 +57,36 @@ impl fmt::Display for LoggingLevel { LoggingLevel::Critical => "critical", LoggingLevel::Normal => "normal", LoggingLevel::Debug => "debug", - LoggingLevel::Off => "off" + LoggingLevel::Off => "off", }; write!(f, "{}", string) } } -#[doc(hidden)] #[macro_export] -macro_rules! log_ { ($name:ident: $($args:tt)*) => { $name!(target: "_", $($args)*) }; } -#[doc(hidden)] #[macro_export] -macro_rules! launch_info { ($($args:tt)*) => { info!(target: "launch", $($args)*) } } -#[doc(hidden)] #[macro_export] -macro_rules! launch_info_ { ($($args:tt)*) => { info!(target: "launch_", $($args)*) } } -#[doc(hidden)] #[macro_export] +#[doc(hidden)] +#[macro_export] +macro_rules! log_ { ($name:ident: $($args:tt)*) => { tracing::$name!(target: "_", $($args)*) }; } +#[doc(hidden)] +#[macro_export] +macro_rules! launch_info { ($($args:tt)*) => { tracing::info!(target: "launch", $($args)*) } } +#[doc(hidden)] +#[macro_export] +macro_rules! launch_info_ { ($($args:tt)*) => { tracing::info!(target: "launch_", $($args)*) } } +#[doc(hidden)] +#[macro_export] macro_rules! error_ { ($($args:expr),+) => { log_!(error: $($args),+); }; } -#[doc(hidden)] #[macro_export] +#[doc(hidden)] +#[macro_export] macro_rules! info_ { ($($args:expr),+) => { log_!(info: $($args),+); }; } -#[doc(hidden)] #[macro_export] +#[doc(hidden)] +#[macro_export] macro_rules! trace_ { ($($args:expr),+) => { log_!(trace: $($args),+); }; } -#[doc(hidden)] #[macro_export] +#[doc(hidden)] +#[macro_export] macro_rules! debug_ { ($($args:expr),+) => { log_!(debug: $($args),+); }; } -#[doc(hidden)] #[macro_export] +#[doc(hidden)] +#[macro_export] macro_rules! warn_ { ($($args:expr),+) => { log_!(warn: $($args),+); }; } impl log::Log for RocketLogger { @@ -86,7 +94,7 @@ impl log::Log for RocketLogger { fn enabled(&self, record: &log::Metadata<'_>) -> bool { match self.0.to_level_filter().to_level() { Some(max) => record.level() <= max || record.target().starts_with("launch"), - None => false + None => false, } } @@ -98,8 +106,12 @@ impl log::Log for RocketLogger { // Don't print Hyper or Rustls messages unless debug is enabled. let configged_level = self.0; - let from_hyper = record.module_path().map_or(false, |m| m.starts_with("hyper::")); - let from_rustls = record.module_path().map_or(false, |m| m.starts_with("rustls::")); + let from_hyper = record + .module_path() + .map_or(false, |m| m.starts_with("hyper::")); + let from_rustls = record + .module_path() + .map_or(false, |m| m.starts_with("rustls::")); if configged_level != LoggingLevel::Debug && (from_hyper || from_rustls) { return; } @@ -115,16 +127,16 @@ impl log::Log for RocketLogger { match record.level() { log::Level::Info => println!("{}", Paint::blue(record.args()).wrap()), log::Level::Trace => println!("{}", Paint::magenta(record.args()).wrap()), - log::Level::Error => { - println!("{} {}", - Paint::red("Error:").bold(), - Paint::red(record.args()).wrap()) - } - log::Level::Warn => { - println!("{} {}", - Paint::yellow("Warning:").bold(), - Paint::yellow(record.args()).wrap()) - } + log::Level::Error => println!( + "{} {}", + Paint::red("Error:").bold(), + Paint::red(record.args()).wrap() + ), + log::Level::Warn => println!( + "{} {}", + Paint::yellow("Warning:").bold(), + Paint::yellow(record.args()).wrap() + ), log::Level::Debug => { print!("\n{} ", Paint::blue("-->").bold()); if let Some(file) = record.file() { @@ -152,7 +164,9 @@ pub(crate) fn try_init(level: LoggingLevel, verbose: bool) -> bool { if !atty::is(atty::Stream::Stdout) || (cfg!(windows) && !Paint::enable_windows_ascii()) - || env::var_os(COLORS_ENV).map(|v| v == "0" || v == "off").unwrap_or(false) + || env::var_os(COLORS_ENV) + .map(|v| v == "0" || v == "off") + .unwrap_or(false) { Paint::disable(); } @@ -170,7 +184,7 @@ pub(crate) fn try_init(level: LoggingLevel, verbose: bool) -> bool { true } -use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; static PUSHED: AtomicBool = AtomicBool::new(false); static LAST_LOG_FILTER: AtomicUsize = AtomicUsize::new(0); @@ -194,7 +208,7 @@ fn usize_to_filter(num: usize) -> log::LevelFilter { 3 => log::LevelFilter::Info, 4 => log::LevelFilter::Debug, 5 => log::LevelFilter::Trace, - _ => unreachable!("max num is 5 in filter_to_usize") + _ => unreachable!("max num is 5 in filter_to_usize"), } } @@ -232,10 +246,13 @@ pub fn init(level: LoggingLevel) -> bool { // Expose logging macros as (hidden) funcions for use by core/contrib codegen. macro_rules! external_log_function { - ($fn_name:ident: $macro_name:ident) => ( - #[doc(hidden)] #[inline(always)] - pub fn $fn_name(msg: &str) { $macro_name!("{}", msg); } - ) + ($fn_name:ident: $macro_name:ident) => { + #[doc(hidden)] + #[inline(always)] + pub fn $fn_name(msg: &str) { + $macro_name!("{}", msg); + } + }; } external_log_function!(error: error); diff --git a/core/lib/src/response/debug.rs b/core/lib/src/response/debug.rs index 354d3e1f4b..cdd6f2a2c4 100644 --- a/core/lib/src/response/debug.rs +++ b/core/lib/src/response/debug.rs @@ -1,6 +1,6 @@ -use crate::request::Request; -use crate::response::{self, Response, Responder}; use crate::http::Status; +use crate::request::Request; +use crate::response::{self, Responder, Response}; use yansi::Paint; @@ -63,8 +63,11 @@ impl From for Debug { impl<'r, E: std::fmt::Debug> Responder<'r, 'static> for Debug { fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { - warn_!("Debug: {:?}", Paint::default(self.0)); - warn_!("Debug always responds with {}.", Status::InternalServerError); + warn_!("Debug: {:?}", Paint::default(&self.0)); + warn_!( + "Debug always responds with {}.", + Status::InternalServerError + ); Response::build().status(Status::InternalServerError).ok() } } diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index d6a597e419..bb6147c621 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -612,18 +612,20 @@ impl Rocket { #[inline] fn configured(config: Config) -> Rocket { - if logger::try_init(config.log_level, false) { - // Temporary weaken log level for launch info. - logger::push_max_level(logger::LoggingLevel::Normal); - } - - launch_info!("{}Configured for {}.", Paint::emoji("🔧 "), config.environment); - launch_info_!("address: {}", Paint::default(&config.address).bold()); - launch_info_!("port: {}", Paint::default(&config.port).bold()); - launch_info_!("log: {}", Paint::default(config.log_level).bold()); - launch_info_!("workers: {}", Paint::default(config.workers).bold()); - launch_info_!("secret key: {}", Paint::default(&config.secret_key).bold()); - launch_info_!("limits: {}", Paint::default(&config.limits).bold()); + crate::trace::try_init(config.log_level); + // if logger::try_init(config.log_level, false) { + // // Temporary weaken log level for launch info. + // logger::push_max_level(logger::LoggingLevel::Normal); + // } + let span = tracing::info_span!("Configured", "for {} {}", config.environment, Paint::emoji("🔧 ")); + let _e = span.enter(); + + tracing::info!(address = %&config.address); + tracing::info!(port = %&config.port); + tracing::info!(log = %&config.log_level); + tracing::info!(workers = %&config.workers); + tracing::info!(secret_key = %&config.secret_key); + tracing::info!(limits = %&config.limits); match config.keep_alive { Some(v) => launch_info_!("keep-alive: {}", Paint::default(format!("{}s", v)).bold()), diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs new file mode 100644 index 0000000000..a7fa59dc7c --- /dev/null +++ b/core/lib/src/trace.rs @@ -0,0 +1,188 @@ +use crate::logger::{LoggingLevel, COLORS_ENV}; +use tracing_subscriber::{ + field, + fmt::{ + format::{self, FormatEvent, FormatFields}, + FmtContext, FormattedFields, + }, + layer::{Context, Layer}, + prelude::*, + registry::LookupSpan, +}; + +use std::env; +use std::fmt::{self, Write}; +use std::str::FromStr; +use std::sync::atomic::{AtomicU64, Ordering::{Acquire, Release}}; + +use yansi::Paint; + +pub(crate) fn try_init(level: LoggingLevel) -> bool { + if level == LoggingLevel::Off { + return false; + } + + if !atty::is(atty::Stream::Stdout) + || (cfg!(windows) && !Paint::enable_windows_ascii()) + || env::var_os(COLORS_ENV) + .map(|v| v == "0" || v == "off") + .unwrap_or(false) + { + Paint::disable(); + } + + tracing::subscriber::set_global_default(tracing_subscriber::registry().with(logging_layer())) + .is_ok() +} + +pub fn logging_layer() -> impl Layer +where + S: tracing::Subscriber, + S: for<'span> LookupSpan<'span>, +{ + let field_format = format::debug_fn(|writer, field, value| { + // We'll format the field name and value separated with a colon. + let name = field.name(); + if name == "message" { + write!(writer, "{:?}", Paint::default(value).bold()) + } else { + write!(writer, "{}: {:?}", Paint::default(field).bold(), value) + } + }) + .delimited(", ") + .display_messages(); + tracing_subscriber::fmt::layer() + .fmt_fields(field_format) + .event_format(EventFormat { last_id: AtomicU64::new(0) }) +} + +struct EventFormat { + last_id: AtomicU64, +} + +impl FormatEvent for EventFormat +where + S: tracing::Subscriber + for<'a> LookupSpan<'a>, + N: for<'a> FormatFields<'a> + 'static, +{ + fn format_event( + &self, + cx: &FmtContext<'_, S, N>, + writer: &mut dyn fmt::Write, + event: &tracing::Event<'_>, + ) -> fmt::Result { + let mut seen = false; + let id = if let Some(span) = cx.lookup_current() { + let id = span.id(); + if id.into_u64() != self.last_id.load(Acquire) { + cx.visit_spans(|span| { + if seen { + write!(writer, " {} ", Paint::default("=>").bold())?; + } + let exts = span.extensions(); + if let Some(fields) = exts.get::>() { + with_meta( + writer, + span.metadata(), + format_args!("{}, {}", Paint::default(span.name()).bold(), fields.fields), + )?; + } else { + with_meta(writer, span.metadata(), Paint::default(span.name()).bold())?; + } + seen = true; + Ok(()) + })?; + } else { + seen = true; + } + Some(id) + } else { + None + }; + + if seen { + write!(writer, " {} ", Paint::default("=>").bold())?; + } + + // xxx(eliza): workaround + let fmt = format::debug_fn(|writer, field, value| { + // We'll format the field name and value separated with a colon. + let name = field.name(); + if name == "message" { + write!(writer, "{:?}", Paint::default(value).bold()) + } else { + write!(writer, "{}: {:?}", Paint::default(field).bold(), value) + } + }) + .delimited(", ") + .display_messages(); + with_meta( + writer, + event.metadata(), + &FmtVisitor { + fmt: &fmt, + records: event, + }, + )?; + if let Some(id) = id { + self.last_id.store(id.into_u64(), Release); + } + Ok(()) + } +} + +fn with_meta( + writer: &mut dyn Write, + meta: &tracing::Metadata<'_>, + f: impl fmt::Display, +) -> fmt::Result { + match *meta.level() { + tracing::Level::INFO => writeln!(writer, "{}", Paint::blue(f).wrap()), + tracing::Level::TRACE => writeln!(writer, "{}", Paint::magenta(f).wrap()), + tracing::Level::ERROR => writeln!( + writer, + "{} {}", + Paint::red("Error:").bold(), + Paint::red(f).wrap() + ), + tracing::Level::WARN => writeln!( + writer, + "{} {}", + Paint::yellow("Warning:").bold(), + Paint::yellow(f).wrap() + ), + tracing::Level::DEBUG => match (meta.file(), meta.line()) { + (Some(file), Some(line)) => writeln!( + writer, + "{}\n {} {}:{}", + f, + Paint::blue("-->").bold(), + Paint::blue(file), + Paint::blue(line) + ), + (Some(file), None) => writeln!( + writer, + "{}\n {} {}", + f, + Paint::blue("-->").bold(), + Paint::blue(file), + ), + _ => writeln!(writer, "{}", f), + }, + } +} + +struct FmtVisitor<'a, F, R> { + fmt: &'a F, + records: R, +} + +impl fmt::Display for FmtVisitor<'_, F, R> +where + F: for<'w> FormatFields<'w>, + R: field::RecordFields, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.fmt.format_fields(f, &self.records) + } +} From debb00882907cf1a901c7d6389fc89c5eeea38e1 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 21 Jul 2020 15:48:29 -0700 Subject: [PATCH 02/60] filtering, nicer formatting Signed-off-by: Eliza Weisman --- core/lib/src/trace.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index a7fa59dc7c..35b92e14c4 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -31,10 +31,27 @@ pub(crate) fn try_init(level: LoggingLevel) -> bool { Paint::disable(); } - tracing::subscriber::set_global_default(tracing_subscriber::registry().with(logging_layer())) + tracing::subscriber::set_global_default(tracing_subscriber::registry() + .with(logging_layer()) + .with(filter_layer(level)) + ) .is_ok() } +pub fn filter_layer(level: LoggingLevel) -> impl Layer +where + S: tracing::Subscriber, +{ + let filter_str = match level { + LoggingLevel::Critical => "warn,rocket::launch=info,hyper=off,rustls=off", + LoggingLevel::Normal => "info,hyper=off,rustls=off" + LoggingLevel::Debug => "trace" + LoggingLevel::Off => "off", + } + tracing_subscriber::env_filter::EnvFilter::try_from(filter_str) + .expect("filter string must parse") +} + pub fn logging_layer() -> impl Layer where S: tracing::Subscriber, @@ -46,7 +63,7 @@ where if name == "message" { write!(writer, "{:?}", Paint::default(value).bold()) } else { - write!(writer, "{}: {:?}", Paint::default(field).bold(), value) + write!(writer, "{}: {:?}", field, Paint::default(value).bold()) } }) .delimited(", ") @@ -84,7 +101,7 @@ where with_meta( writer, span.metadata(), - format_args!("{}, {}", Paint::default(span.name()).bold(), fields.fields), + format_args!("{} {}", span.name(), fields.fields), )?; } else { with_meta(writer, span.metadata(), Paint::default(span.name()).bold())?; @@ -111,7 +128,7 @@ where if name == "message" { write!(writer, "{:?}", Paint::default(value).bold()) } else { - write!(writer, "{}: {:?}", Paint::default(field).bold(), value) + write!(writer, "{}: {:?}", field, Paint::default(value).bold()) } }) .delimited(", ") From e0a0bfda9a81cc40dd63c5fad233182db724aa8f Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 21 Jul 2020 15:48:43 -0700 Subject: [PATCH 03/60] more trace Signed-off-by: Eliza Weisman --- core/lib/src/rocket.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index bb6147c621..319cf7adc3 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -628,18 +628,18 @@ impl Rocket { tracing::info!(limits = %&config.limits); match config.keep_alive { - Some(v) => launch_info_!("keep-alive: {}", Paint::default(format!("{}s", v)).bold()), - None => launch_info_!("keep-alive: {}", Paint::default("disabled").bold()), + Some(v) => tracing::info!(keep_alive = %Paint::default(format!("{}s", v)).bold()), + None => tracing::info!(keep_alive = %Paint::default("disabled").bold()), } let tls_configured = config.tls.is_some(); if tls_configured && cfg!(feature = "tls") { - launch_info_!("tls: {}", Paint::default("enabled").bold()); + tracing::info!(tls = %Paint::default("enabled").bold()); } else if tls_configured { - error_!("tls: {}", Paint::default("disabled").bold()); - error_!("tls is configured, but the tls feature is disabled"); + tracing::error!(tls = %Paint::default("disabled").bold()); + tracing::error!("tls is configured, but the tls feature is disabled"); } else { - launch_info_!("tls: {}", Paint::default("disabled").bold()); + tracing::info!(tls = %Paint::default("disabled").bold()); } if config.secret_key.is_generated() && config.environment.is_prod() { From 6e0e902f3a1fedfe20b7781e23cf305869cfdc2c Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 21 Jul 2020 15:57:56 -0700 Subject: [PATCH 04/60] fix filtering Signed-off-by: Eliza Weisman --- core/lib/src/trace.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 35b92e14c4..2c0262e358 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -44,11 +44,12 @@ where { let filter_str = match level { LoggingLevel::Critical => "warn,rocket::launch=info,hyper=off,rustls=off", - LoggingLevel::Normal => "info,hyper=off,rustls=off" - LoggingLevel::Debug => "trace" + LoggingLevel::Normal => "info,hyper=off,rustls=off", + LoggingLevel::Debug => "trace", LoggingLevel::Off => "off", - } - tracing_subscriber::env_filter::EnvFilter::try_from(filter_str) + }; + + tracing_subscriber::filter::EnvFilter::try_new(filter_str) .expect("filter string must parse") } From 47540a97b67067c5204052fbe13788f784b4318d Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 21 Jul 2020 15:58:06 -0700 Subject: [PATCH 05/60] mounting works Signed-off-by: Eliza Weisman --- core/lib/src/rocket.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 319cf7adc3..db3188f6ed 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -67,20 +67,17 @@ pub(crate) struct Token; impl Rocket { #[inline] fn _mount(&mut self, base: Origin<'static>, routes: Vec) { - info!("{}{} {}{}", - Paint::emoji("🛰 "), - Paint::magenta("Mounting"), - Paint::blue(&base), - Paint::magenta(":")); + let span = tracing::info_span!("Mounting", "{}: {}", Paint::blue(&base), Paint::emoji("🛰")); + let _e = span.enter(); for mut route in routes { let path = route.uri.clone(); if let Err(e) = route.set_uri(base.clone(), path) { - error_!("{}", e); + error!("{}", e); panic!("Invalid route URI."); } - info_!("{}", route); + tracing::info!(%route); self.router.add(route); } } From 88cee8c263dc973654c3f6788fbeb74b022ce32b Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 21 Jul 2020 17:31:32 -0700 Subject: [PATCH 06/60] WIP Signed-off-by: Eliza Weisman --- core/lib/Cargo.toml | 5 +++++ core/lib/src/lib.rs | 2 +- core/lib/src/rocket.rs | 42 ++++++++++++++++++++++-------------------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 810f4f008d..0324588bf3 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -55,6 +55,11 @@ version = "0.2" default-features = false features = ["fmt", "env-filter", "smallvec"] +[dependencies.tracing-futures] +version = "0.2" +default-features = false +features = ["std-future"] + [build-dependencies] yansi = "0.5" version_check = "0.9.1" diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index 6e3a1dbe44..26b90d125b 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -89,7 +89,7 @@ pub use rocket_codegen::*; pub use async_trait::*; -#[macro_use] extern crate log; +#[macro_use] extern crate tracing; #[macro_use] extern crate pear; pub use futures; diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index db3188f6ed..31cff6138a 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -26,6 +26,7 @@ use crate::fairing::{Fairing, Fairings}; use crate::logger::PaintExt; use crate::ext::AsyncReadExt; use crate::shutdown::{ShutdownHandle, ShutdownHandleManaged}; +use tracing_futures::Instrument; use crate::http::{Method, Status, Header}; use crate::http::private::{Listener, Connection, Incoming}; @@ -209,7 +210,7 @@ async fn hyper_service_fn( let token = rocket.preprocess_request(&mut req, &data).await; let r = rocket.dispatch(token, &mut req, data).await; rocket.issue_response(r, tx).await; - }); + }).instrument(tracing::info_span!("Request", from = %h_addr)); rx.await.map_err(|e| io::Error::new(io::ErrorKind::Other, e)) } @@ -410,29 +411,30 @@ impl Rocket { request: &'r Request<'s>, data: Data ) -> Response<'r> { - info!("{}:", request); - - // Remember if the request is `HEAD` for later body stripping. - let was_head_request = request.method() == Method::Head; + async move { + // Remember if the request is `HEAD` for later body stripping. + let was_head_request = request.method() == Method::Head; - // Route the request and run the user's handlers. - let mut response = self.route_and_process(request, data).await; + // Route the request and run the user's handlers. + let mut response = self.route_and_process(request, data).await; - // Add a default 'Server' header if it isn't already there. - // TODO: If removing Hyper, write out `Date` header too. - if !response.headers().contains("Server") { - response.set_header(Header::new("Server", "Rocket")); - } + // Add a default 'Server' header if it isn't already there. + // TODO: If removing Hyper, write out `Date` header too. + if !response.headers().contains("Server") { + response.set_header(Header::new("Server", "Rocket")); + } - // Run the response fairings. - self.fairings.handle_response(request, &mut response).await; + // Run the response fairings. + self.fairings.handle_response(request, &mut response).await; - // Strip the body if this is a `HEAD` request. - if was_head_request { - response.strip_body(); - } + // Strip the body if this is a `HEAD` request. + if was_head_request { + response.strip_body(); + } - response + response + }.instrument(tracing::info_span!("request", "{}", request)) + .await } // Finds the error catcher for the status `status` and executes it for the @@ -525,7 +527,7 @@ impl Rocket { where Fut: Future + Send + 'static, Fut::Output: Send { fn execute(&self, fut: Fut) { - tokio::spawn(fut); + tokio::spawn(fut.in_current_span()); } } From d92073828d78dcd1bfaf7a79f5403963df4d2b3a Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 21 Jul 2020 17:50:46 -0700 Subject: [PATCH 07/60] skip span names for "rocket-ier" formatitng Signed-off-by: Eliza Weisman --- core/lib/src/rocket.rs | 19 ++++++++++--------- core/lib/src/trace.rs | 7 +++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 31cff6138a..0729d9253d 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -68,7 +68,7 @@ pub(crate) struct Token; impl Rocket { #[inline] fn _mount(&mut self, base: Origin<'static>, routes: Vec) { - let span = tracing::info_span!("Mounting", "{}: {}", Paint::blue(&base), Paint::emoji("🛰")); + let span = tracing::info_span!("mounting", at = %Paint::blue(&base), "{} Mounting", Paint::emoji("🛰 "),); let _e = span.enter(); for mut route in routes { @@ -85,13 +85,14 @@ impl Rocket { #[inline] fn _register(&mut self, catchers: Vec) { - info!("{}{}", Paint::emoji("👾 "), Paint::magenta("Catchers:")); + let span = tracing::info_span!("catchers", "{}{}", Paint::emoji("👾 "), Paint::magenta("Catchers:")); + let _e = span.enter(); for c in catchers { if self.catchers.get(&c.code).map_or(false, |e| !e.is_default) { - info_!("{} {}", c, Paint::yellow("(warning: duplicate catcher!)")); + info!("{} {}", c, Paint::yellow("(warning: duplicate catcher!)")); } else { - info_!("{}", c); + info!("{}", c); } self.catchers.insert(c.code, c); @@ -210,7 +211,7 @@ async fn hyper_service_fn( let token = rocket.preprocess_request(&mut req, &data).await; let r = rocket.dispatch(token, &mut req, data).await; rocket.issue_response(r, tx).await; - }).instrument(tracing::info_span!("Request", from = %h_addr)); + }.instrument(tracing::info_span!("connection", from = %h_addr, "Incoming"))); rx.await.map_err(|e| io::Error::new(io::ErrorKind::Other, e)) } @@ -616,7 +617,7 @@ impl Rocket { // // Temporary weaken log level for launch info. // logger::push_max_level(logger::LoggingLevel::Normal); // } - let span = tracing::info_span!("Configured", "for {} {}", config.environment, Paint::emoji("🔧 ")); + let span = tracing::info_span!("configured", "{}Configured for {}", Paint::emoji("🔧 "), config.environment,); let _e = span.enter(); tracing::info!(address = %&config.address); @@ -646,7 +647,7 @@ impl Rocket { } for (name, value) in config.extras() { - launch_info_!("{} {}: {}", + tracing::info!("{} {}: {}", Paint::yellow("[extra]"), name, Paint::default(LoggedValue(value)).bold()); } @@ -722,12 +723,12 @@ impl Rocket { pub fn mount>>(mut self, base: &str, routes: R) -> Self { let base_uri = Origin::parse_owned(base.to_string()) .unwrap_or_else(|e| { - error_!("Invalid origin URI '{}' used as mount point.", base); + error!("Invalid origin URI '{}' used as mount point.", base); panic!("Error: {}", e); }); if base_uri.query().is_some() { - error_!("Mount point '{}' contains query string.", base); + error!("Mount point '{}' contains query string.", base); panic!("Invalid mount point."); } diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 2c0262e358..377cd1a492 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -5,14 +5,13 @@ use tracing_subscriber::{ format::{self, FormatEvent, FormatFields}, FmtContext, FormattedFields, }, - layer::{Context, Layer}, + layer::Layer, prelude::*, registry::LookupSpan, }; use std::env; use std::fmt::{self, Write}; -use std::str::FromStr; use std::sync::atomic::{AtomicU64, Ordering::{Acquire, Release}}; use yansi::Paint; @@ -48,7 +47,7 @@ where LoggingLevel::Debug => "trace", LoggingLevel::Off => "off", }; - + tracing_subscriber::filter::EnvFilter::try_new(filter_str) .expect("filter string must parse") } @@ -102,7 +101,7 @@ where with_meta( writer, span.metadata(), - format_args!("{} {}", span.name(), fields.fields), + &fields.fields, )?; } else { with_meta(writer, span.metadata(), Paint::default(span.name()).bold())?; From bce8f1475f0444ae3c18599a8033e87870ca97b4 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 21 Jul 2020 17:58:18 -0700 Subject: [PATCH 08/60] this is cute, right? Signed-off-by: Eliza Weisman --- core/lib/src/rocket.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 0729d9253d..7c05837cab 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -211,7 +211,7 @@ async fn hyper_service_fn( let token = rocket.preprocess_request(&mut req, &data).await; let r = rocket.dispatch(token, &mut req, data).await; rocket.issue_response(r, tx).await; - }.instrument(tracing::info_span!("connection", from = %h_addr, "Incoming"))); + }.instrument(tracing::info_span!("connection", from = %h_addr, "{}Connection", Paint::emoji("📡 ")))); rx.await.map_err(|e| io::Error::new(io::ErrorKind::Other, e)) } From e3398e5a53421fdc80d38756411c94fa77b3414a Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 22 Jul 2020 17:07:27 -0700 Subject: [PATCH 09/60] fix contrib Signed-off-by: Eliza Weisman --- contrib/lib/Cargo.toml | 2 +- contrib/lib/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/lib/Cargo.toml b/contrib/lib/Cargo.toml index f9b2618e90..d62eed209f 100644 --- a/contrib/lib/Cargo.toml +++ b/contrib/lib/Cargo.toml @@ -45,7 +45,7 @@ memcache_pool = ["databases", "memcache", "r2d2-memcache"] tokio = { version = "0.2.0", optional = true } rocket_contrib_codegen = { version = "0.5.0-dev", path = "../codegen", optional = true } rocket = { version = "0.5.0-dev", path = "../../core/lib/", default-features = false } -log = "0.4" +tracing = { version = "0.1", default-features = false } # Serialization and templating dependencies. serde = { version = "1.0", optional = true } diff --git a/contrib/lib/src/lib.rs b/contrib/lib/src/lib.rs index 72271f1c5d..6c34172826 100644 --- a/contrib/lib/src/lib.rs +++ b/contrib/lib/src/lib.rs @@ -40,7 +40,7 @@ //! This crate is expected to grow with time, bringing in outside crates to be //! officially supported by Rocket. -#[allow(unused_imports)] #[macro_use] extern crate log; +#[allow(unused_imports)] #[macro_use] extern crate tracing; #[allow(unused_imports)] #[macro_use] extern crate rocket; #[cfg(feature="json")] #[macro_use] pub mod json; From 86c5ef550ea9140ceaf33ba29921116eb70c67e7 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 22 Jul 2020 17:15:47 -0700 Subject: [PATCH 10/60] use span names if there's no message Signed-off-by: Eliza Weisman --- core/lib/src/trace.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 377cd1a492..c82362e6cb 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -96,13 +96,16 @@ where if seen { write!(writer, " {} ", Paint::default("=>").bold())?; } + let meta = span.metadata(); let exts = span.extensions(); if let Some(fields) = exts.get::>() { - with_meta( - writer, - span.metadata(), - &fields.fields, - )?; + // If the span has a human-readable message, print that + // instead of the span's name (so that we can get nice emojis). + if meta.fields().iter().any(|field| field.name() == "message") { + with_meta(writer, meta, &fields.fields)?; + } else { + with_meta(writer, meta, format_args!("{} {}", Paint::default(span.name()).bold(), &fields.fields))?; + } } else { with_meta(writer, span.metadata(), Paint::default(span.name()).bold())?; } From 88feb8eb52d4fd909aeaf341127a3ad75f2a1534 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 22 Jul 2020 17:24:54 -0700 Subject: [PATCH 11/60] start removing underscore macros Signed-off-by: Eliza Weisman --- contrib/lib/src/compression/fairing.rs | 8 ++++---- contrib/lib/src/json.rs | 4 ++-- contrib/lib/src/msgpack.rs | 4 ++-- contrib/lib/src/templates/context.rs | 8 ++++---- core/lib/src/error.rs | 9 +++++---- core/lib/src/rocket.rs | 6 +++--- 6 files changed, 20 insertions(+), 19 deletions(-) diff --git a/contrib/lib/src/compression/fairing.rs b/contrib/lib/src/compression/fairing.rs index 8de4cee78e..6d7b510f3f 100644 --- a/contrib/lib/src/compression/fairing.rs +++ b/contrib/lib/src/compression/fairing.rs @@ -115,17 +115,17 @@ impl Fairing for Compression { if let Value::String(s) = ex { let mt = MediaType::parse_flexible(s); if mt.is_none() { - warn_!("Ignoring invalid media type '{:?}'", s); + warn!("Ignoring invalid media type '{:?}'", s); } mt } else { - warn_!("Ignoring non-string media type '{:?}'", ex); + warn!("Ignoring non-string media type '{:?}'", ex); None } }).collect(); } None => { - warn_!( + warn!( "Exclusions is not an array; using default compression exclusions '{:?}'", ctxt.exclusions ); @@ -134,7 +134,7 @@ impl Fairing for Compression { Err(ConfigError::Missing(_)) => { /* ignore missing */ } Err(e) => { e.pretty_print(); - warn_!( + warn!( "Using default compression exclusions '{:?}'", ctxt.exclusions ); diff --git a/contrib/lib/src/json.rs b/contrib/lib/src/json.rs index 06e7a1e82d..65e3e759db 100644 --- a/contrib/lib/src/json.rs +++ b/contrib/lib/src/json.rs @@ -151,7 +151,7 @@ impl<'a, T: Deserialize<'a>> FromTransformedData<'a> for Json { match serde_json::from_str(&string) { Ok(v) => Success(Json(v)), Err(e) => { - error_!("Couldn't parse JSON body: {:?}", e); + error!("Couldn't parse JSON body: {:?}", e); if e.is_data() { Failure((Status::UnprocessableEntity, JsonError::Parse(string, e))) } else { @@ -170,7 +170,7 @@ impl<'r, T: Serialize> Responder<'r, 'static> for Json { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { let string = serde_json::to_string(&self.0) .map_err(|e| { - error_!("JSON failed to serialize: {:?}", e); + error!("JSON failed to serialize: {:?}", e); Status::InternalServerError })?; diff --git a/contrib/lib/src/msgpack.rs b/contrib/lib/src/msgpack.rs index f2434e557e..e2de22c670 100644 --- a/contrib/lib/src/msgpack.rs +++ b/contrib/lib/src/msgpack.rs @@ -138,7 +138,7 @@ impl<'a, T: Deserialize<'a>> FromTransformedData<'a> for MsgPack { match rmp_serde::from_slice(&buf) { Ok(val) => Success(MsgPack(val)), Err(e) => { - error_!("Couldn't parse MessagePack body: {:?}", e); + error!("Couldn't parse MessagePack body: {:?}", e); match e { TypeMismatch(_) | OutOfRange | LengthMismatch(_) => { Failure((Status::UnprocessableEntity, e)) @@ -158,7 +158,7 @@ impl<'r, T: Serialize> Responder<'r, 'static> for MsgPack { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { let buf = rmp_serde::to_vec(&self.0) .map_err(|e| { - error_!("MsgPack failed to serialize: {:?}", e); + error!("MsgPack failed to serialize: {:?}", e); Status::InternalServerError })?; diff --git a/contrib/lib/src/templates/context.rs b/contrib/lib/src/templates/context.rs index 08cd06d03a..0f815026ba 100644 --- a/contrib/lib/src/templates/context.rs +++ b/contrib/lib/src/templates/context.rs @@ -28,10 +28,10 @@ impl Context { for path in glob::glob(glob_path).unwrap().filter_map(Result::ok) { let (name, data_type_str) = split_path(&root, &path); if let Some(info) = templates.get(&*name) { - warn_!("Template name '{}' does not have a unique path.", name); - info_!("Existing path: {:?}", info.path); - info_!("Additional path: {:?}", path); - warn_!("Using existing path for template '{}'.", name); + let span = warn_span!("invalid_template_path", "Template name '{}' does not have a unique path.", name); + info!(parent: &span, "Existing path: {:?}", info.path); + info!(parent: &span, "Additional path: {:?}", path); + warn!(parent: &span, "Using existing path for template '{}'.", name); continue; } diff --git a/core/lib/src/error.rs b/core/lib/src/error.rs index 7e1bb34e74..98479189dd 100644 --- a/core/lib/src/error.rs +++ b/core/lib/src/error.rs @@ -203,18 +203,19 @@ impl Drop for LaunchError { panic!("{}", e); } LaunchErrorKind::Collision(ref collisions) => { - error!("Rocket failed to launch due to the following routing collisions:"); + let span = error_span!("collisions", "Rocket failed to launch due to the following routing collisions:"); + let _e = span.enter(); for &(ref a, ref b) in collisions { - info_!("{} {} {}", a, Paint::red("collides with").italic(), b) + info!("{} {} {}", a, Paint::red("collides with").italic(), b) } - info_!("Note: Collisions can usually be resolved by ranking routes."); + info!("Note: Collisions can usually be resolved by ranking routes."); panic!("route collisions detected"); } LaunchErrorKind::FailedFairings(ref failures) => { error!("Rocket failed to launch due to failing fairings:"); for fairing in failures { - info_!("{}", fairing); + info!("{}", fairing); } panic!("launch fairing failure"); diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 7c05837cab..da28b5b961 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -140,7 +140,7 @@ impl Rocket { // process them as a stack to maintain proper ordering. let mut manifest = mem::replace(&mut self.manifest, vec![]); while !manifest.is_empty() { - trace_!("[MANIEST PROGRESS]: {:?}", manifest); + trace!("[MANIEST PROGRESS]: {:?}", manifest); match manifest.remove(0) { PreLaunchOp::Manage(_, callback) => callback(&mut self.managed_state), PreLaunchOp::Mount(base, routes) => self._mount(base, routes), @@ -226,10 +226,10 @@ impl Rocket { let result = self.write_response(response, tx); match result.await { Ok(()) => { - info_!("{}", Paint::green("Response succeeded.")); + info!("{}", Paint::green("Response succeeded.")); } Err(e) => { - error_!("Failed to write response: {:?}.", e); + error!("Failed to write response: {:?}.", e); } } } From cbe558183d46c8d399b8d4e5a006c5b46e8fb42a Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 22 Jul 2020 17:39:20 -0700 Subject: [PATCH 12/60] less obnoxious bold, nicer debug/trace Signed-off-by: Eliza Weisman --- core/lib/src/trace.rs | 55 ++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index c82362e6cb..26f53ea52e 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -104,10 +104,10 @@ where if meta.fields().iter().any(|field| field.name() == "message") { with_meta(writer, meta, &fields.fields)?; } else { - with_meta(writer, meta, format_args!("{} {}", Paint::default(span.name()).bold(), &fields.fields))?; + with_meta(writer, meta, format_args!("{} {}", span.name(), &fields.fields))?; } } else { - with_meta(writer, span.metadata(), Paint::default(span.name()).bold())?; + with_meta(writer, span.metadata(), span.name())?; } seen = true; Ok(()) @@ -156,9 +156,38 @@ fn with_meta( meta: &tracing::Metadata<'_>, f: impl fmt::Display, ) -> fmt::Result { + + struct WithFile<'a, F> { + meta: &'a tracing::Metadata<'a>, + f: F, + } + + impl fmt::Display for WithFile<'_, F> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match (self.meta.file(), self.meta.line()) { + (Some(file), Some(line)) => write!( + f, + "{}\n {} {}:{}", + self.f, + Paint::default("-->").bold(), + file, + line + ), + (Some(file), None) => write!( + f, + "{}\n {} {}", + self.f, + Paint::default("-->").bold(), + file, + ), + _ => write!(f, "{}", self.f), + } + } + } + + match *meta.level() { tracing::Level::INFO => writeln!(writer, "{}", Paint::blue(f).wrap()), - tracing::Level::TRACE => writeln!(writer, "{}", Paint::magenta(f).wrap()), tracing::Level::ERROR => writeln!( writer, "{} {}", @@ -171,24 +200,8 @@ fn with_meta( Paint::yellow("Warning:").bold(), Paint::yellow(f).wrap() ), - tracing::Level::DEBUG => match (meta.file(), meta.line()) { - (Some(file), Some(line)) => writeln!( - writer, - "{}\n {} {}:{}", - f, - Paint::blue("-->").bold(), - Paint::blue(file), - Paint::blue(line) - ), - (Some(file), None) => writeln!( - writer, - "{}\n {} {}", - f, - Paint::blue("-->").bold(), - Paint::blue(file), - ), - _ => writeln!(writer, "{}", f), - }, + tracing::Level::TRACE => writeln!(writer, "{}", Paint::magenta(WithFile { meta, f }).wrap()), + tracing::Level::DEBUG => writeln!(writer, "{}", Paint::blue(WithFile { meta, f }).wrap()), } } From 43c95db7ea8f364a8befbcf48472cfbb64e20a2d Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 22 Jul 2020 17:43:51 -0700 Subject: [PATCH 13/60] don't constantly screw up colors Signed-off-by: Eliza Weisman --- core/lib/src/trace.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 26f53ea52e..eacb725616 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -61,9 +61,9 @@ where // We'll format the field name and value separated with a colon. let name = field.name(); if name == "message" { - write!(writer, "{:?}", Paint::default(value).bold()) + write!(writer, "{:?}", Paint::new(value).bold()) } else { - write!(writer, "{}: {:?}", field, Paint::default(value).bold()) + write!(writer, "{}: {:?}", field, Paint::new(value).bold()) } }) .delimited(", ") @@ -129,9 +129,9 @@ where // We'll format the field name and value separated with a colon. let name = field.name(); if name == "message" { - write!(writer, "{:?}", Paint::default(value).bold()) + write!(writer, "{:?}", Paint::new(value).bold()) } else { - write!(writer, "{}: {:?}", field, Paint::default(value).bold()) + write!(writer, "{}: {:?}", field, Paint::new(value).bold()) } }) .delimited(", ") @@ -169,7 +169,7 @@ fn with_meta( f, "{}\n {} {}:{}", self.f, - Paint::default("-->").bold(), + Paint::new("-->").bold(), file, line ), @@ -177,7 +177,7 @@ fn with_meta( f, "{}\n {} {}", self.f, - Paint::default("-->").bold(), + Paint::new("-->").bold(), file, ), _ => write!(f, "{}", self.f), From 1eec8f65e6837d5e91fdc2ac97baf037ef04dee2 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 24 Jul 2020 17:37:59 -0700 Subject: [PATCH 14/60] unpatch Signed-off-by: Eliza Weisman --- Cargo.toml | 6 +----- core/lib/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ae76a49668..fb739db000 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,8 +42,4 @@ members = [ "examples/tls", "examples/fairings", "examples/hello_2018", -] - -[patch.crates-io] -tracing-subscriber = { git = "https://github.com/tokio-rs/tracing", rev = "ccdd7a26d406711947802510ffdc6db99a680f8b"} -tracing-core = { git = "https://github.com/tokio-rs/tracing", rev = "ccdd7a26d406711947802510ffdc6db99a680f8b"} \ No newline at end of file +] \ No newline at end of file diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 0324588bf3..cabe89c26f 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -51,7 +51,7 @@ default-features = false features = ["log", "std"] [dependencies.tracing-subscriber] -version = "0.2" +version = "0.2.9" default-features = false features = ["fmt", "env-filter", "smallvec"] From 2589dee8d43fac1a7b471db71539f4ea3b9b2818 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 11 Aug 2020 16:10:53 -0700 Subject: [PATCH 15/60] add docs & reexports Signed-off-by: Eliza Weisman --- core/lib/Cargo.toml | 6 +- core/lib/src/trace.rs | 245 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+), 5 deletions(-) diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index cabe89c26f..979b6a389f 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -40,16 +40,12 @@ atty = "0.2" async-trait = "0.1" ref-cast = "1.0" atomic = "0.4" +tracing = "0.1.19" [dependencies.tokio] version = "0.2.9" features = ["fs", "io-std", "io-util", "rt-threaded", "sync", "signal", "macros"] -[dependencies.tracing] -version = "0.1.15" -default-features = false -features = ["log", "std"] - [dependencies.tracing-subscriber] version = "0.2.9" default-features = false diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index eacb725616..e59bd3a114 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -1,3 +1,179 @@ +//! Tracing, telemetry, and logging. +//! +//! Rocket provides support for application-level diagnostics using +//! the [`tracing`] crate. `tracing` provides a _facade layer_ for scoped, +//! structured, application-level diagnostics. This means that diagnostic data +//! from Rocket applications, from Rocket itself, and from any dependencies that +//! use the [`tracing`] or [`log`] crates, can be emitted in a machine-readable +//! format and consumed in a wide variety of ways, including structured logging, +//! distributed tracing, and performance profiling. +//! +//! This module re-exports core components of the `tracing` API for use in +//! Rocket applications, and provides Rocket-specific APIs for use with +//! `tracing`. +//! +//! # Using Tracing +//! +//! Tracing's data model is based around two core concepts: [_spans_][spans] and +//! [_events_][events]. A span represents a _period of time_, with a beginning and +//! an end, during which a program was executing in a particular context or +//! performing a unit of work. An event represents a _momentary_ occurance +//! within a trace, comparable to a single log line. +//! +//! Spans and events are recorded using macros, the basics of which are likely +//! familiar to users of the [`log`] crate. The [`trace!`], [`debug!`], +//! [`info!`], [`warn!`], and [`error!`] macros record an event at a priority +//! level ranging from extremely verbose diagnostic information (`trace!`) to +//! high-priority warnings (`error!`). For example: +//! +//! ``` +//! use rocket::trace; +//! +//! trace::trace!("Apollo 13 presently at 177,861 nautical miles away."); +//! trace::debug!("Velocity now reading 3,263 feet per second."); +//! trace::info!("13, we'd like you to stir up your cryo tanks."); +//! trace::warn!("Houston, we've got a Main Bus B undervolt."); +//! trace::error!("Houston, we are venting something out into space!"); +//! ``` +//! +//! An event consists of one or more key-value pairs, called _fields_, and/or a +//! textual, human-readable _message_. For example, this will record an event +//! at the `info` level, with two fields, named `answer` and `question`: +//! +//! ``` +//! # use rocket::trace; +//! trace::info!(answer = 42, question = "life, the universe, and everything"); +//! ``` +//! The [`tracing` documentation][macros] provides details on how these macros are used. +//! +//! Spans may be recorded in a few different ways. Like events, they have a +//! priority level, and may have one or more fields. In addition, all spans also +//! have a _name_. The easiest way to record a span is to add the +//! [`#[tracing::instrument]`][instrument] attribute to a function. For example: +//! +//! +//! ``` +//! use rocket::trace; +//! +//! # #[derive(Debug)] struct Planet; +//! // Calling this function will enter a new span named `jump_to_hyperspace`. +//! #[trace::instrument] +//! async fn jump_to_hyperspace(destination: Planet) { +//! // This event will be recorded *within* the `jump_to_hyperspace` span. +//! tracing::debug!("preparing to jump to hyperspace..."); +//! +//! // ... +//! } +//! ``` +//! This will automatically create a span with the same name as the instrumented +//! function, and all the arguments to that function recorded as fields. +//! Additional arguments to `#[instrument]` allow customizing the span further. +//! See the [`tracing` crate's documentation](instrument) for details. +//! +//! In addition, spans may be created manually using the [`trace_span!`], +//! [`debug_span!`], [`info_span!`], [`warn_span!`], and [`error_span!`] macros. +//! Again, the [`tracing` documentation][macros] provides further details on how +//! to use these macros. +//! +//! # Customization +//! +//! Spans and events are recorded by a `tracing` component called a +//! [`Subscriber`], which implements a particular way of collecting and +//! recording trace data. By default, Rocket provides its own `Subscriber` +//! implementation, which logs events to the console. This `Subscriber` will be +//! installed when [`rocket::ignite`] is called. +//! +//! To override the default `Subscriber` with another implementation, simply +//! [set it as the default][default] prior to calling `rocket::ignite`. For +//! example: +//! ``` +//! # type MySubscriber = tracing_subscriber::registry::Registry; +//! #[rocket::launch] +//! fn rocket() -> rocket::Rocket { +//! let subscriber = MySubscriber::default(); +//! tracing::subscriber::set_global_default(subscriber) +//! .expect("the global default subscriber should not have been set"); +//! +//! rocket::ignite() +//! // ... +//! } +//! ``` +//! +//! Since `tracing` data is structured and machine-readable, it may be collected +//! in a variety of ways. The `tracing` community provides [several crates] for +//! logging in several commonly-used formats, emitting distributed tracing data +//! to collectors like [OpenTelemetry] and [honeycomb.io], and for +//! [multiple][timing] [forms][flame] of performance profiling. +//! +//! The [`tracing-subscriber`] crate provides an abstraction for building +//! a `Subscriber` by composing multiple [`Layer`]s which implement different +//! ways of collecting traces. This allows applications to record the same trace +//! data in multiple ways. +//! +//! In addition to providing a default subscriber out of the box, Rocket also +//! exposes its default logging and filtering behavior as `Layer`s. This means +//! that users who would like to combine the default logging layer with layers +//! from other crates may do so. For example: +//! +//! ```rust +//! # use tracing_subscriber::Layer; +//! # #[derive(Default)] struct SomeLayer; +//! # impl Layer for SomeLayer {} +//! # #[derive(Default)] struct SomeOtherLayer; +//! # impl Layer for SomeOtherLayer {} +//! #[rocket::launch] +//! fn rocket() -> rocket::Rocket { +//! use rocket::trace::prelude::*; +//! +//! let config = rocket::Config::read() +//! .expect("failed to read config!"); +//! +//! // Configure our trace subscriber... +//! tracing_subscriber::registry() +//! // Add Rocket's default log formatter. +//! .with(rocket::trace::logging_layer()) +//! // Add a custom layer... +//! .with(SomeLayer::default()) +//! // ...and another custom layer. +//! .with(SomeOtherLayer::default()) +//! // Filter what traces are enabled based on the Rocket config. +//! .with(rocket::filter_layer(config.log_level)) +//! // Set our subscriber as the default. +//! .init(); +//! +//! rocket::custom(config) +//! // ... +//! } +//! ``` +//! +//! [`tracing`]: https://docs.rs/tracing +//! [`log`]: https://docs.rs/log/ +//! [spans]: https://docs.rs/tracing/latest/tracing/#spans +//! [events]: https://docs.rs/tracing/latest/tracing/#events +//! [`span!`]: https://docs.rs/tracing/latest/tracing/macro.span.html +//! [`event!`]: https://docs.rs/tracing/latest/tracing/macro.event.html +//! [`trace!`]: https://docs.rs/tracing/latest/tracing/macro.trace.html +//! [`debug!`]: https://docs.rs/tracing/latest/tracing/macro.debug.html +//! [`info!`]: https://docs.rs/tracing/latest/tracing/macro.info.html +//! [`warn!`]: https://docs.rs/tracing/latest/tracing/macro.warn.html +//! [`error!`]: https://docs.rs/tracing/latest/tracing/macro.error.html +//! [macros]: https://docs.rs/tracing/latest/tracing/index.html#using-the-macros +//! [instrument]: https://docs.rs/tracing/latest/tracing/attr.instrument.html +//! [`trace_span!`]: https://docs.rs/tracing/latest/tracing/macro.trace_span.html +//! [`debug_span!`]: https://docs.rs/tracing/latest/tracing/macro.debug_span.html +//! [`info_span!`]: https://docs.rs/tracing/latest/tracing/macro.info_span.html +//! [`warn_span!`]: https://docs.rs/tracing/latest/tracing/macro.warn_span.html +//! [`error_span!`]: https://docs.rs/tracing/latest/tracing/macro.error_span.html +//! [`rocket::ignite`]: crate::ignite +//! [default]: https://docs.rs/tracing/latest/tracing/#in-executables +//! [`Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html +//! [several crates]: https://github.com/tokio-rs/tracing#related-crates +//! [`tracing-subscriber`]: https://docs.rs/tracing-subscriber/ +//! [`Layer`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html +//! [OpenTelemetry]: https://crates.io/crates/tracing-opentelemetry +//! [honeycomb.io]: https://crates.io/crates/tracing-honeycomb +//! [timing]: https://crates.io/crates/tracing-timing +//! [flame]: https://crates.io/crates/tracing-flame use crate::logger::{LoggingLevel, COLORS_ENV}; use tracing_subscriber::{ field, @@ -16,6 +192,14 @@ use std::sync::atomic::{AtomicU64, Ordering::{Acquire, Release}}; use yansi::Paint; +pub use tracing::{trace, debug, info, warn, error, instrument}; + +/// A prelude for working with `tracing` in Rocket applications. +pub mod prelude { + pub use tracing_subscriber::prelude::*; + pub use tracing_futures::Instrument as _; +} + pub(crate) fn try_init(level: LoggingLevel) -> bool { if level == LoggingLevel::Off { return false; @@ -37,6 +221,36 @@ pub(crate) fn try_init(level: LoggingLevel) -> bool { .is_ok() } +/// Returns a Rocket filtering [`Layer`] based on the provided logging level. +/// +/// The returned [`Layer`] can be added to another `tracing` subscriber to +/// configure it to filter spans and events based on the logging level +/// specified in the Rocket config. +/// +/// For example: +/// +/// ``` +/// # type MySubscriber = tracing_subscriber::registry::Registry; +/// #[rocket::launch] +/// fn rocket() -> rocket::Rocket { +/// use rocket::trace::prelude::*; +/// +/// let config = rocket::Config::read() +/// .expect("failed to read config!"); +/// +/// // Use some `tracing` subscriber from another crate... +/// MySubscriber::default() +/// // ...but filter spans and events based on the Rocket +/// // config file. +/// .with(rocket::trace::filter_layer(config.log_level)) +/// .init(); +/// +/// rocket::custom(config) +/// // ... +/// } +/// ``` +/// +/// [`Layer`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html pub fn filter_layer(level: LoggingLevel) -> impl Layer where S: tracing::Subscriber, @@ -52,6 +266,37 @@ where .expect("filter string must parse") } +/// Returns a Rocket-style log formatting layer. +/// +/// The returned layer can be added to a [`tracing-subscriber` +/// `Registry`][registry] to add Rocket-style log formatting in addition to +/// other [`Layer`s] providing different functionality. +/// +/// For example: +/// +/// ``` +/// # type MySubscriber = tracing_subscriber::registry::Registry; +/// #[rocket::launch] +/// fn rocket() -> rocket::Rocket { +/// use rocket::trace::prelude::*; +/// +/// let config = rocket::Config::read() +/// .expect("failed to read config!"); +/// +/// // Use some `tracing` subscriber from another crate... +/// MySubscriber::default() +/// // ...but filter spans and events based on the Rocket +/// // config file. +/// .with(rocket::trace::filter_layer(config.log_level)) +/// .init(); +/// +/// rocket::custom(config) +/// // ... +/// } +/// ``` +/// +/// [`Layer`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html +/// [`registry`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/registry/index.html pub fn logging_layer() -> impl Layer where S: tracing::Subscriber, From 533d7921439d95323a37008c2dfbc00a487c292f Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 11 Aug 2020 16:24:46 -0700 Subject: [PATCH 16/60] cleanup, remove logger module --- contrib/codegen/src/database.rs | 8 +- contrib/lib/src/helmet/helmet.rs | 8 +- contrib/lib/src/templates/fairing.rs | 12 +- .../lib/src/templates/handlebars_templates.rs | 8 +- contrib/lib/src/templates/metadata.rs | 6 +- contrib/lib/src/templates/mod.rs | 16 +- contrib/lib/src/templates/tera_templates.rs | 10 +- contrib/lib/tests/templates.rs | 2 +- core/lib/src/config/error.rs | 22 +- core/lib/src/config/mod.rs | 6 +- core/lib/src/data/data.rs | 8 +- core/lib/src/data/data_stream.rs | 6 +- core/lib/src/fairing/fairings.rs | 4 +- core/lib/src/lib.rs | 1 - core/lib/src/logger.rs | 261 ------------------ core/lib/src/request/form/form.rs | 6 +- core/lib/src/request/request.rs | 8 +- core/lib/src/request/state.rs | 2 +- core/lib/src/response/debug.rs | 4 +- core/lib/src/response/flash.rs | 6 +- core/lib/src/response/responder.rs | 6 +- core/lib/src/response/response.rs | 4 +- core/lib/src/rocket.rs | 34 +-- core/lib/src/router/mod.rs | 4 +- core/lib/src/trace.rs | 94 +++++-- 25 files changed, 169 insertions(+), 377 deletions(-) delete mode 100644 core/lib/src/logger.rs diff --git a/contrib/codegen/src/database.rs b/contrib/codegen/src/database.rs index 6cad8adcbc..e02bbb9dbd 100644 --- a/contrib/codegen/src/database.rs +++ b/contrib/codegen/src/database.rs @@ -101,15 +101,15 @@ pub fn database_attr(attr: TokenStream, input: TokenStream) -> Result Ok(rocket.manage(#pool_type(p))), Err(config_error) => { - ::rocket::logger::error( + ::rocket::trace::error( &format!("Database configuration failure: '{}'", #name)); - ::rocket::logger::error_(&format!("{}", config_error)); + ::rocket::trace::error_(&format!("{}", config_error)); Err(rocket) }, Ok(Err(pool_error)) => { - ::rocket::logger::error( + ::rocket::trace::error( &format!("Failed to initialize pool for '{}'", #name)); - ::rocket::logger::error_(&format!("{:?}", pool_error)); + ::rocket::trace::error_(&format!("{:?}", pool_error)); Err(rocket) }, } diff --git a/contrib/lib/src/helmet/helmet.rs b/contrib/lib/src/helmet/helmet.rs index 8ca3831622..210e3d2316 100644 --- a/contrib/lib/src/helmet/helmet.rs +++ b/contrib/lib/src/helmet/helmet.rs @@ -172,7 +172,7 @@ impl SpaceHelmet { let name = policy.name(); if response.headers().contains(name.as_str()) { warn!("Space Helmet: response contains a '{}' header.", name); - warn_!("Refusing to overwrite existing header."); + warn!("Refusing to overwrite existing header."); continue } @@ -206,9 +206,9 @@ impl Fairing for SpaceHelmet { && !cargo.config().environment.is_dev() && !self.is_enabled::() { - warn_!("Space Helmet: deploying with TLS without enabling HSTS."); - warn_!("Enabling default HSTS policy."); - info_!("To disable this warning, configure an HSTS policy."); + warn!("Space Helmet: deploying with TLS without enabling HSTS."); + warn!("Enabling default HSTS policy."); + info!("To disable this warning, configure an HSTS policy."); self.force_hsts.store(true, Ordering::Relaxed); } } diff --git a/contrib/lib/src/templates/fairing.rs b/contrib/lib/src/templates/fairing.rs index 07f55372b9..176e92afa7 100644 --- a/contrib/lib/src/templates/fairing.rs +++ b/contrib/lib/src/templates/fairing.rs @@ -61,8 +61,8 @@ mod context { Ok(watcher) => Some(Mutex::new((watcher, rx))), Err(e) => { warn!("Failed to enable live template reloading: {}", e); - debug_!("Reload error: {:?}", e); - warn_!("Live template reloading is unavailable."); + debug!("Reload error: {:?}", e); + warn!("Live template reloading is unavailable."); None } }; @@ -98,14 +98,14 @@ mod context { } if changed { - info_!("Change detected: reloading templates."); + info!("Change detected: reloading templates."); let mut ctxt = self.context_mut(); if let Some(mut new_ctxt) = Context::initialize(ctxt.root.clone()) { custom_callback(&mut new_ctxt.engines); *ctxt = new_ctxt; } else { - warn_!("An error occurred while reloading templates."); - warn_!("The previous templates will remain active."); + warn!("An error occurred while reloading templates."); + warn!("The previous templates will remain active."); }; } }); @@ -159,7 +159,7 @@ impl Fairing for TemplateFairing { Err(ConfigError::Missing(_)) => { /* ignore missing */ } Err(e) => { e.pretty_print(); - warn_!("Using default templates directory '{:?}'", template_root); + warn!("Using default templates directory '{:?}'", template_root); } }; diff --git a/contrib/lib/src/templates/handlebars_templates.rs b/contrib/lib/src/templates/handlebars_templates.rs index 9d667c7605..d6bd5b6f39 100644 --- a/contrib/lib/src/templates/handlebars_templates.rs +++ b/contrib/lib/src/templates/handlebars_templates.rs @@ -13,8 +13,8 @@ impl Engine for Handlebars<'static> { let path = &info.path; if let Err(e) = hb.register_template_file(name, path) { error!("Error in Handlebars template '{}'.", name); - info_!("{}", e); - info_!("Template path: '{}'.", path.to_string_lossy()); + info!("{}", e); + info!("Template path: '{}'.", path.to_string_lossy()); return None; } } @@ -24,14 +24,14 @@ impl Engine for Handlebars<'static> { fn render(&self, name: &str, context: C) -> Option { if self.get_template(name).is_none() { - error_!("Handlebars template '{}' does not exist.", name); + error!("Handlebars template '{}' does not exist.", name); return None; } match Handlebars::render(self, name, &context) { Ok(string) => Some(string), Err(e) => { - error_!("Error rendering Handlebars template '{}': {}", name, e); + error!("Error rendering Handlebars template '{}': {}", name, e); None } } diff --git a/contrib/lib/src/templates/metadata.rs b/contrib/lib/src/templates/metadata.rs index 0d1c386f6d..30ba9cb9b9 100644 --- a/contrib/lib/src/templates/metadata.rs +++ b/contrib/lib/src/templates/metadata.rs @@ -93,9 +93,9 @@ impl<'a, 'r> FromRequest<'a, 'r> for Metadata<'a> { .succeeded() .and_then(|cm| Some(Outcome::Success(Metadata(cm.inner())))) .unwrap_or_else(|| { - error_!("Uninitialized template context: missing fairing."); - info_!("To use templates, you must attach `Template::fairing()`."); - info_!("See the `Template` documentation for more information."); + error!("Uninitialized template context: missing fairing."); + info!("To use templates, you must attach `Template::fairing()`."); + info!("See the `Template` documentation for more information."); Outcome::Failure((Status::InternalServerError, ())) }) } diff --git a/contrib/lib/src/templates/mod.rs b/contrib/lib/src/templates/mod.rs index 09ebb7f27e..17e7d0fa07 100644 --- a/contrib/lib/src/templates/mod.rs +++ b/contrib/lib/src/templates/mod.rs @@ -362,19 +362,19 @@ impl Template { let name = &*self.name; let info = ctxt.templates.get(name).ok_or_else(|| { let ts: Vec<_> = ctxt.templates.keys().map(|s| s.as_str()).collect(); - error_!("Template '{}' does not exist.", name); - info_!("Known templates: {}", ts.join(",")); - info_!("Searched in '{:?}'.", ctxt.root); + error!("Template '{}' does not exist.", name); + info!("Known templates: {}", ts.join(",")); + info!("Searched in '{:?}'.", ctxt.root); Status::InternalServerError })?; let value = self.value.ok_or_else(|| { - error_!("The provided template context failed to serialize."); + error!("The provided template context failed to serialize."); Status::InternalServerError })?; let string = ctxt.engines.render(name, &info, value).ok_or_else(|| { - error_!("Template '{}' failed to render.", name); + error!("Template '{}' failed to render.", name); Status::InternalServerError })?; @@ -389,9 +389,9 @@ impl<'r> Responder<'r, 'static> for Template { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { let (render, content_type) = { let ctxt = req.managed_state::().ok_or_else(|| { - error_!("Uninitialized template context: missing fairing."); - info_!("To use templates, you must attach `Template::fairing()`."); - info_!("See the `Template` documentation for more information."); + error!("Uninitialized template context: missing fairing."); + info!("To use templates, you must attach `Template::fairing()`."); + info!("See the `Template` documentation for more information."); Status::InternalServerError })?.context(); diff --git a/contrib/lib/src/templates/tera_templates.rs b/contrib/lib/src/templates/tera_templates.rs index d841b41426..70cf2094c0 100644 --- a/contrib/lib/src/templates/tera_templates.rs +++ b/contrib/lib/src/templates/tera_templates.rs @@ -25,7 +25,7 @@ impl Engine for Tera { let mut error = Some(&e as &dyn Error); while let Some(err) = error { - info_!("{}", err); + info!("{}", err); error = err.source(); } @@ -37,14 +37,14 @@ impl Engine for Tera { fn render(&self, name: &str, context: C) -> Option { if self.get_template(name).is_err() { - error_!("Tera template '{}' does not exist.", name); + error!("Tera template '{}' does not exist.", name); return None; }; let tera_ctx = match Context::from_serialize(context) { Ok(ctx) => ctx, Err(_) => { - error_!( + error!( "Error generating context when rendering Tera template '{}'.", name ); @@ -55,11 +55,11 @@ impl Engine for Tera { match Tera::render(self, name, &tera_ctx) { Ok(string) => Some(string), Err(e) => { - error_!("Error rendering Tera template '{}'.", name); + error!("Error rendering Tera template '{}'.", name); let mut error = Some(&e as &dyn Error); while let Some(err) = error { - error_!("{}", err); + error!("{}", err); error = err.source(); } diff --git a/contrib/lib/tests/templates.rs b/contrib/lib/tests/templates.rs index 199454e7bd..ca3d062c3b 100644 --- a/contrib/lib/tests/templates.rs +++ b/contrib/lib/tests/templates.rs @@ -90,7 +90,7 @@ mod templates_tests { use rocket::local::blocking::Client; const EXPECTED: &'static str - = "Hello _test_!\n\n
<script /> hi
\nDone.\n\n"; + = "Hello _testtrace::\n\n
<script /> hi
\nDone.\n\n"; #[rocket::async_test] async fn test_handlebars_templates() { diff --git a/core/lib/src/config/error.rs b/core/lib/src/config/error.rs index bb8146045c..f466eb4bf0 100644 --- a/core/lib/src/config/error.rs +++ b/core/lib/src/config/error.rs @@ -64,46 +64,46 @@ impl ConfigError { RandFailure => error!("failed to read randomness from the OS"), Io(ref error, param) => { error!("I/O error while setting {}:", Paint::default(param).bold()); - info_!("{}", error); + info!("{}", error); } BadFilePath(ref path, reason) => { error!("configuration file path {} is invalid", Paint::default(path.display()).bold()); - info_!("{}", reason); + info!("{}", reason); } BadEntry(ref name, ref filename) => { let valid_entries = format!("{}, global", valid_envs); error!("{} is not a known configuration environment", Paint::default(format!("[{}]", name)).bold()); - info_!("in {}", Paint::default(filename.display()).bold()); - info_!("valid environments are: {}", Paint::default(&valid_entries).bold()); + info!("in {}", Paint::default(filename.display()).bold()); + info!("valid environments are: {}", Paint::default(&valid_entries).bold()); } BadEnv(ref name) => { error!("{} is not a valid ROCKET_ENV value", Paint::default(name).bold()); - info_!("valid environments are: {}", Paint::default(valid_envs).bold()); + info!("valid environments are: {}", Paint::default(valid_envs).bold()); } BadType(ref name, expected, actual, ref filename) => { error!("{} key could not be parsed", Paint::default(name).bold()); if let Some(filename) = filename { - info_!("in {}", Paint::default(filename.display()).bold()); + info!("in {}", Paint::default(filename.display()).bold()); } - info_!("expected value to be {}, but found {}", + info!("expected value to be {}, but found {}", Paint::default(expected).bold(), Paint::default(actual).bold()); } ParseError(_, ref filename, ref desc, line_col) => { error!("config file failed to parse due to invalid TOML"); - info_!("{}", desc); - info_!("in {}", Paint::default(filename.display()).bold()); + info!("{}", desc); + info!("in {}", Paint::default(filename.display()).bold()); if let Some((line, col)) = line_col { - info_!("at line {}, column {}", + info!("at line {}, column {}", Paint::default(line + 1).bold(), Paint::default(col + 1).bold()); } } BadEnvVal(ref key, ref value, ref error) => { error!("environment variable {} could not be parsed", Paint::default(format!("ROCKET_{}={}", key.to_uppercase(), value)).bold()); - info_!("{}", error); + info!("{}", error); } UnknownKey(ref key) => { error!("the configuration key {} is unknown and disallowed in \ diff --git a/core/lib/src/config/mod.rs b/core/lib/src/config/mod.rs index 3703ec0c75..d9a63ea943 100644 --- a/core/lib/src/config/mod.rs +++ b/core/lib/src/config/mod.rs @@ -204,12 +204,12 @@ pub use self::error::ConfigError; pub use self::environment::Environment; pub use self::config::Config; pub use self::builder::ConfigBuilder; -pub use crate::logger::LoggingLevel; +pub use crate::trace::LoggingLevel; pub(crate) use self::toml_ext::LoggedValue; use self::Environment::*; use self::environment::CONFIG_ENV; -use crate::logger::COLORS_ENV; +use crate::trace::COLORS_ENV; use self::toml_ext::parse_simple_toml_value; use crate::http::uncased::uncased_eq; @@ -468,7 +468,7 @@ mod test { use super::Environment::*; use super::Result; - use crate::logger::LoggingLevel; + use crate::trace::LoggingLevel; const TEST_CONFIG_FILENAME: &'static str = "/tmp/testing/Rocket.toml"; diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index b4126889c5..a13165e032 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -176,7 +176,7 @@ impl Data { // bytes can be read from `stream`. #[inline(always)] pub(crate) async fn new(body: hyper::Body) -> Data { - trace_!("Data::new({:?})", body); + trace!("Data::new({:?})", body); let mut stream = AsyncReadBody::from(body); @@ -184,7 +184,7 @@ impl Data { let eof = match stream.read_max(&mut peek_buf[..]).await { Ok(n) => { - trace_!("Filled peek buf with {} bytes.", n); + trace!("Filled peek buf with {} bytes.", n); // TODO.async: This has not gone away, and I don't entirely // understand what's happening here @@ -197,14 +197,14 @@ impl Data { n < PEEK_BYTES } Err(e) => { - error_!("Failed to read into peek buffer: {:?}.", e); + error!("Failed to read into peek buffer: {:?}.", e); // Likewise here as above. peek_buf.truncate(0); false } }; - trace_!("Peek bytes: {}/{} bytes.", peek_buf.len(), PEEK_BYTES); + trace!("Peek bytes: {}/{} bytes.", peek_buf.len(), PEEK_BYTES); Data { buffer: peek_buf, stream, is_complete: eof } } diff --git a/core/lib/src/data/data_stream.rs b/core/lib/src/data/data_stream.rs index 0ebd04fbd7..ab70b0192a 100644 --- a/core/lib/src/data/data_stream.rs +++ b/core/lib/src/data/data_stream.rs @@ -18,16 +18,16 @@ pub struct DataStream(pub(crate) Vec, pub(crate) AsyncReadBody); impl AsyncRead for DataStream { #[inline(always)] fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { - trace_!("DataStream::poll_read()"); + trace!("DataStream::poll_read()"); if self.0.len() > 0 { let count = std::cmp::min(buf.len(), self.0.len()); - trace_!("Reading peeked {} into dest {} = {} bytes", self.0.len(), buf.len(), count); + trace!("Reading peeked {} into dest {} = {} bytes", self.0.len(), buf.len(), count); let next = self.0.split_off(count); (&mut buf[..count]).copy_from_slice(&self.0[..]); self.0 = next; Poll::Ready(Ok(count)) } else { - trace_!("Delegating to remaining stream"); + trace!("Delegating to remaining stream"); Pin::new(&mut self.1).poll_read(cx, buf) } } diff --git a/core/lib/src/fairing/fairings.rs b/core/lib/src/fairing/fairings.rs index 68df195163..ab66cb81e5 100644 --- a/core/lib/src/fairing/fairings.rs +++ b/core/lib/src/fairing/fairings.rs @@ -1,6 +1,6 @@ use crate::{Cargo, Rocket, Request, Response, Data}; use crate::fairing::{Fairing, Kind}; -use crate::logger::PaintExt; +use crate::trace::PaintExt; use yansi::Paint; @@ -88,7 +88,7 @@ impl Fairings { .collect::>() .join(", "); - info_!("{} {}: {}", Paint::default(num).bold(), kind, Paint::default(&names).bold()); + info!("{} {}: {}", Paint::default(num).bold(), kind, Paint::default(&names).bold()); } } diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index 26b90d125b..adcd90e483 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -95,7 +95,6 @@ pub use async_trait::*; pub use futures; pub use tokio; -#[doc(hidden)] #[macro_use] pub mod logger; #[macro_use] pub mod outcome; pub mod local; pub mod request; diff --git a/core/lib/src/logger.rs b/core/lib/src/logger.rs deleted file mode 100644 index 0776a32cfb..0000000000 --- a/core/lib/src/logger.rs +++ /dev/null @@ -1,261 +0,0 @@ -//! Rocket's logging infrastructure. - -use std::str::FromStr; -use std::{env, fmt}; - -use log; -use yansi::Paint; - -pub(crate) const COLORS_ENV: &str = "ROCKET_CLI_COLORS"; - -struct RocketLogger(LoggingLevel); - -/// Defines the different levels for log messages. -#[derive(PartialEq, Eq, Debug, Clone, Copy)] -pub enum LoggingLevel { - /// Only shows errors, warnings, and launch information. - Critical, - /// Shows everything except debug and trace information. - Normal, - /// Shows everything. - Debug, - /// Shows nothing. - Off, -} - -impl LoggingLevel { - #[inline(always)] - fn to_level_filter(self) -> log::LevelFilter { - match self { - LoggingLevel::Critical => log::LevelFilter::Warn, - LoggingLevel::Normal => log::LevelFilter::Info, - LoggingLevel::Debug => log::LevelFilter::Trace, - LoggingLevel::Off => log::LevelFilter::Off, - } - } -} - -impl FromStr for LoggingLevel { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - let level = match s { - "critical" => LoggingLevel::Critical, - "normal" => LoggingLevel::Normal, - "debug" => LoggingLevel::Debug, - "off" => LoggingLevel::Off, - _ => return Err("a log level (off, debug, normal, critical)"), - }; - - Ok(level) - } -} - -impl fmt::Display for LoggingLevel { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let string = match *self { - LoggingLevel::Critical => "critical", - LoggingLevel::Normal => "normal", - LoggingLevel::Debug => "debug", - LoggingLevel::Off => "off", - }; - - write!(f, "{}", string) - } -} - -#[doc(hidden)] -#[macro_export] -macro_rules! log_ { ($name:ident: $($args:tt)*) => { tracing::$name!(target: "_", $($args)*) }; } -#[doc(hidden)] -#[macro_export] -macro_rules! launch_info { ($($args:tt)*) => { tracing::info!(target: "launch", $($args)*) } } -#[doc(hidden)] -#[macro_export] -macro_rules! launch_info_ { ($($args:tt)*) => { tracing::info!(target: "launch_", $($args)*) } } -#[doc(hidden)] -#[macro_export] -macro_rules! error_ { ($($args:expr),+) => { log_!(error: $($args),+); }; } -#[doc(hidden)] -#[macro_export] -macro_rules! info_ { ($($args:expr),+) => { log_!(info: $($args),+); }; } -#[doc(hidden)] -#[macro_export] -macro_rules! trace_ { ($($args:expr),+) => { log_!(trace: $($args),+); }; } -#[doc(hidden)] -#[macro_export] -macro_rules! debug_ { ($($args:expr),+) => { log_!(debug: $($args),+); }; } -#[doc(hidden)] -#[macro_export] -macro_rules! warn_ { ($($args:expr),+) => { log_!(warn: $($args),+); }; } - -impl log::Log for RocketLogger { - #[inline(always)] - fn enabled(&self, record: &log::Metadata<'_>) -> bool { - match self.0.to_level_filter().to_level() { - Some(max) => record.level() <= max || record.target().starts_with("launch"), - None => false, - } - } - - fn log(&self, record: &log::Record<'_>) { - // Print nothing if this level isn't enabled and this isn't launch info. - if !self.enabled(record.metadata()) { - return; - } - - // Don't print Hyper or Rustls messages unless debug is enabled. - let configged_level = self.0; - let from_hyper = record - .module_path() - .map_or(false, |m| m.starts_with("hyper::")); - let from_rustls = record - .module_path() - .map_or(false, |m| m.starts_with("rustls::")); - if configged_level != LoggingLevel::Debug && (from_hyper || from_rustls) { - return; - } - - // In Rocket, we abuse targets with suffix "_" to indicate indentation. - let is_launch = record.target().starts_with("launch"); - if record.target().ends_with('_') { - if configged_level != LoggingLevel::Critical || is_launch { - print!(" {} ", Paint::default("=>").bold()); - } - } - - match record.level() { - log::Level::Info => println!("{}", Paint::blue(record.args()).wrap()), - log::Level::Trace => println!("{}", Paint::magenta(record.args()).wrap()), - log::Level::Error => println!( - "{} {}", - Paint::red("Error:").bold(), - Paint::red(record.args()).wrap() - ), - log::Level::Warn => println!( - "{} {}", - Paint::yellow("Warning:").bold(), - Paint::yellow(record.args()).wrap() - ), - log::Level::Debug => { - print!("\n{} ", Paint::blue("-->").bold()); - if let Some(file) = record.file() { - print!("{}", Paint::blue(file)); - } - - if let Some(line) = record.line() { - println!(":{}", Paint::blue(line)); - } - - println!("{}", record.args()); - } - } - } - - fn flush(&self) { - // NOOP: We don't buffer any records. - } -} - -pub(crate) fn try_init(level: LoggingLevel, verbose: bool) -> bool { - if level == LoggingLevel::Off { - return false; - } - - if !atty::is(atty::Stream::Stdout) - || (cfg!(windows) && !Paint::enable_windows_ascii()) - || env::var_os(COLORS_ENV) - .map(|v| v == "0" || v == "off") - .unwrap_or(false) - { - Paint::disable(); - } - - push_max_level(level); - if let Err(e) = log::set_boxed_logger(Box::new(RocketLogger(level))) { - if verbose { - eprintln!("Logger failed to initialize: {}", e); - } - - pop_max_level(); - return false; - } - - true -} - -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; - -static PUSHED: AtomicBool = AtomicBool::new(false); -static LAST_LOG_FILTER: AtomicUsize = AtomicUsize::new(0); - -fn filter_to_usize(filter: log::LevelFilter) -> usize { - match filter { - log::LevelFilter::Off => 0, - log::LevelFilter::Error => 1, - log::LevelFilter::Warn => 2, - log::LevelFilter::Info => 3, - log::LevelFilter::Debug => 4, - log::LevelFilter::Trace => 5, - } -} - -fn usize_to_filter(num: usize) -> log::LevelFilter { - match num { - 0 => log::LevelFilter::Off, - 1 => log::LevelFilter::Error, - 2 => log::LevelFilter::Warn, - 3 => log::LevelFilter::Info, - 4 => log::LevelFilter::Debug, - 5 => log::LevelFilter::Trace, - _ => unreachable!("max num is 5 in filter_to_usize"), - } -} - -pub(crate) fn push_max_level(level: LoggingLevel) { - LAST_LOG_FILTER.store(filter_to_usize(log::max_level()), Ordering::Release); - PUSHED.store(true, Ordering::Release); - log::set_max_level(level.to_level_filter()); -} - -pub(crate) fn pop_max_level() { - if PUSHED.load(Ordering::Acquire) { - log::set_max_level(usize_to_filter(LAST_LOG_FILTER.load(Ordering::Acquire))); - } -} - -pub(crate) trait PaintExt { - fn emoji(item: &str) -> Paint<&str>; -} - -impl PaintExt for Paint<&str> { - /// Paint::masked(), but hidden on Windows due to broken output. See #1122. - fn emoji(item: &str) -> Paint<&str> { - if cfg!(windows) { - Paint::masked("") - } else { - Paint::masked(item) - } - } -} - -#[doc(hidden)] -pub fn init(level: LoggingLevel) -> bool { - try_init(level, true) -} - -// Expose logging macros as (hidden) funcions for use by core/contrib codegen. -macro_rules! external_log_function { - ($fn_name:ident: $macro_name:ident) => { - #[doc(hidden)] - #[inline(always)] - pub fn $fn_name(msg: &str) { - $macro_name!("{}", msg); - } - }; -} - -external_log_function!(error: error); -external_log_function!(error_: error_); -external_log_function!(warn: warn); -external_log_function!(warn_: warn_); diff --git a/core/lib/src/request/form/form.rs b/core/lib/src/request/form/form.rs index a8351d3893..63cffd2f7d 100644 --- a/core/lib/src/request/form/form.rs +++ b/core/lib/src/request/form/form.rs @@ -156,14 +156,14 @@ impl<'f, T: FromForm<'f>> Form { let mut items = FormItems::from(form_str); let result = T::from_form(&mut items, strict); if !items.exhaust() { - error_!("The request's form string was malformed."); + error!("The request's form string was malformed."); return Failure((Status::BadRequest, Malformed(form_str))); } match result { Ok(v) => Success(v), Err(e) => { - error_!("The incoming form failed to parse."); + error!("The incoming form failed to parse."); Failure((Status::UnprocessableEntity, Parse(e, form_str))) } } @@ -196,7 +196,7 @@ impl<'f, T: FromForm<'f> + Send + 'f> FromTransformedData<'f> for Form { use std::cmp::min; if !request.content_type().map_or(false, |ct| ct.is_form()) { - warn_!("Form data does not have form content type."); + warn!("Form data does not have form content type."); return Transform::Borrowed(Forward(data)); } diff --git a/core/lib/src/request/request.rs b/core/lib/src/request/request.rs index e5a693adfe..5e099d87da 100644 --- a/core/lib/src/request/request.rs +++ b/core/lib/src/request/request.rs @@ -267,7 +267,7 @@ impl<'r> Request<'r> { .get_one("X-Real-IP") .and_then(|ip| { ip.parse() - .map_err(|_| warn_!("'X-Real-IP' header is malformed: {}", ip)) + .map_err(|_| warn!("'X-Real-IP' header is malformed: {}", ip)) .ok() }) } @@ -336,10 +336,10 @@ impl<'r> Request<'r> { Cookies::new(jar, self.state.config.secret_key(), on_drop) } None => { - error_!("Multiple `Cookies` instances are active at once."); - info_!("An instance of `Cookies` must be dropped before another \ + error!("Multiple `Cookies` instances are active at once."); + info!("An instance of `Cookies` must be dropped before another \ can be retrieved."); - warn_!("The retrieved `Cookies` instance will be empty."); + warn!("The retrieved `Cookies` instance will be empty."); Cookies::empty() } } diff --git a/core/lib/src/request/state.rs b/core/lib/src/request/state.rs index 54b696e474..66aa84c61d 100644 --- a/core/lib/src/request/state.rs +++ b/core/lib/src/request/state.rs @@ -172,7 +172,7 @@ impl<'a, 'r, T: Send + Sync + 'static> FromRequest<'a, 'r> for State<'r, T> { match req.state.managed.try_get::() { Some(state) => Outcome::Success(State(state)), None => { - error_!("Attempted to retrieve unmanaged state!"); + error!("Attempted to retrieve unmanaged state!"); Outcome::Failure((Status::InternalServerError, ())) } } diff --git a/core/lib/src/response/debug.rs b/core/lib/src/response/debug.rs index cdd6f2a2c4..87138f0a51 100644 --- a/core/lib/src/response/debug.rs +++ b/core/lib/src/response/debug.rs @@ -63,8 +63,8 @@ impl From for Debug { impl<'r, E: std::fmt::Debug> Responder<'r, 'static> for Debug { fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { - warn_!("Debug: {:?}", Paint::default(&self.0)); - warn_!( + warn!("Debug: {:?}", Paint::default(&self.0)); + warn!( "Debug always responds with {}.", Status::InternalServerError ); diff --git a/core/lib/src/response/flash.rs b/core/lib/src/response/flash.rs index 46480b57c7..7e09098116 100644 --- a/core/lib/src/response/flash.rs +++ b/core/lib/src/response/flash.rs @@ -193,7 +193,7 @@ impl Flash { /// the response is the `Outcome` of the wrapped `Responder`. impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Flash { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { - trace_!("Flash: setting message: {}:{}", self.name, self.message); + trace!("Flash: setting message: {}:{}", self.name, self.message); req.cookies().add(self.cookie()); self.inner.respond_to(req) } @@ -243,9 +243,9 @@ impl<'a, 'r> FromRequest<'a, 'r> for Flash<&'a Request<'r>> { type Error = (); async fn from_request(req: &'a Request<'r>) -> request::Outcome { - trace_!("Flash: attempting to retrieve message."); + trace!("Flash: attempting to retrieve message."); req.cookies().get(FLASH_COOKIE_NAME).ok_or(()).and_then(|cookie| { - trace_!("Flash: retrieving message: {:?}", cookie); + trace!("Flash: retrieving message: {:?}", cookie); // Parse the flash message. let content = cookie.value(); diff --git a/core/lib/src/response/responder.rs b/core/lib/src/response/responder.rs index 53b3d85888..0c8f4c9eed 100644 --- a/core/lib/src/response/responder.rs +++ b/core/lib/src/response/responder.rs @@ -265,7 +265,7 @@ impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Option { match self { Some(r) => r.respond_to(req), None => { - warn_!("Response was `None`."); + warn!("Response was `None`."); Err(Status::NotFound) }, } @@ -310,8 +310,8 @@ impl<'r> Responder<'r, 'static> for Status { Response::build().status(self).ok() } _ => { - error_!("Invalid status used as responder: {}.", self); - warn_!("Fowarding to 500 (Internal Server Error) catcher."); + error!("Invalid status used as responder: {}.", self); + warn!("Fowarding to 500 (Internal Server Error) catcher."); Err(Status::InternalServerError) } } diff --git a/core/lib/src/response/response.rs b/core/lib/src/response/response.rs index d29066f243..dee85ac6e6 100644 --- a/core/lib/src/response/response.rs +++ b/core/lib/src/response/response.rs @@ -106,7 +106,7 @@ impl Body pub async fn into_bytes(mut self) -> Option> { let mut vec = Vec::new(); if let Err(e) = self.as_reader().read_to_end(&mut vec).await { - error_!("Error reading body: {:?}", e); + error!("Error reading body: {:?}", e); return None; } @@ -119,7 +119,7 @@ impl Body self.into_bytes().await.and_then(|bytes| match String::from_utf8(bytes) { Ok(string) => Some(string), Err(e) => { - error_!("Body is invalid UTF-8: {}", e); + error!("Body is invalid UTF-8: {}", e); None } }) diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index da28b5b961..5022f70f61 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -13,7 +13,7 @@ use ref_cast::RefCast; use yansi::Paint; use state::Container; -use crate::{logger, handler}; +use crate::{trace, handler}; use crate::config::{Config, FullConfig, ConfigError, LoggedValue}; use crate::request::{Request, FormItems}; use crate::data::Data; @@ -23,7 +23,7 @@ use crate::catcher::{self, Catcher}; use crate::outcome::Outcome; use crate::error::{LaunchError, LaunchErrorKind}; use crate::fairing::{Fairing, Fairings}; -use crate::logger::PaintExt; +use crate::trace::PaintExt; use crate::ext::AsyncReadExt; use crate::shutdown::{ShutdownHandle, ShutdownHandleManaged}; use tracing_futures::Instrument; @@ -336,7 +336,7 @@ impl Rocket { Outcome::Forward(data) => { // There was no matching route. Autohandle `HEAD` requests. if request.method() == Method::Head { - info_!("Autohandling {} request.", Paint::default("HEAD").bold()); + info!("Autohandling {} request.", Paint::default("HEAD").bold()); // Dispatch the request again with Method `GET`. request._set_method(Method::Get); @@ -384,7 +384,7 @@ impl Rocket { let matches = self.router.route(request); for route in matches { // Retrieve and set the requests parameters. - info_!("Matched: {}", route); + info!("Matched: {}", route); request.set_route(route); // Dispatch the request to the handler. @@ -393,14 +393,14 @@ impl Rocket { // Check if the request processing completed (Some) or if the // request needs to be forwarded. If it does, continue the loop // (None) to try again. - info_!("{} {}", Paint::default("Outcome:").bold(), outcome); + info!("{} {}", Paint::default("Outcome:").bold(), outcome); match outcome { o@Outcome::Success(_) | o@Outcome::Failure(_) => return o, Outcome::Forward(unused_data) => data = unused_data, } } - error_!("No matching routes for {}.", request); + error!("No matching routes for {}.", request); Outcome::Forward(data) } } @@ -449,7 +449,7 @@ impl Rocket { req: &'r Request<'s> ) -> impl Future> + 's { async move { - warn_!("Responding with {} catcher.", Paint::red(&status)); + warn!("Responding with {} catcher.", Paint::red(&status)); // For now, we reset the delta state to prevent any modifications // from earlier, unsuccessful paths from being reflected in error @@ -458,7 +458,7 @@ impl Rocket { // Try to get the active catcher but fallback to user's 500 catcher. let catcher = self.catchers.get(&status.code).unwrap_or_else(|| { - error_!("No catcher found for {}. Using 500 catcher.", status); + error!("No catcher found for {}. Using 500 catcher.", status); self.catchers.get(&500).expect("500 catcher.") }); @@ -466,8 +466,8 @@ impl Rocket { match catcher.handle(req).await { Ok(r) => return r, Err(err_status) => { - error_!("Catcher failed with status: {}!", err_status); - warn_!("Using default 500 error catcher."); + error!("Catcher failed with status: {}!", err_status); + warn!("Using default 500 error catcher."); let default = self.default_catchers.get(&500).expect("Default 500"); default.handle(req).await.expect("Default 500 response.") } @@ -492,15 +492,13 @@ impl Rocket { self.fairings.pretty_print_counts(); self.fairings.handle_launch(self.cargo()); - launch_info!("{}{} {}{}", + trace::info!( target: "launch", + "{}{} {}{}", Paint::emoji("🚀 "), Paint::default("Rocket has launched from").bold(), Paint::default(proto).bold().underline(), Paint::default(&full_addr).bold().underline()); - // Restore the log level back to what it originally was. - logger::pop_max_level(); - // Set the keep-alive. // TODO.async: implement keep-alive in Listener // let timeout = self.config.keep_alive.map(|s| Duration::from_secs(s as u64)); @@ -575,7 +573,7 @@ impl Rocket { }) .map(Rocket::configured) .unwrap_or_else(|e: ConfigError| { - logger::init(logger::LoggingLevel::Debug); + let _ = trace::try_init(trace::LoggingLevel::Debug); e.pretty_print(); std::process::exit(1) }) @@ -613,10 +611,6 @@ impl Rocket { #[inline] fn configured(config: Config) -> Rocket { crate::trace::try_init(config.log_level); - // if logger::try_init(config.log_level, false) { - // // Temporary weaken log level for launch info. - // logger::push_max_level(logger::LoggingLevel::Normal); - // } let span = tracing::info_span!("configured", "{}Configured for {}", Paint::emoji("🔧 "), config.environment,); let _e = span.enter(); @@ -1021,7 +1015,7 @@ impl Rocket { Either::Left((Err(err), server)) => { // Error setting up ctrl-c signal. Let the user know. warn!("Failed to enable `ctrl+c` graceful signal shutdown."); - info_!("Error: {}", err); + info!("Error: {}", err); server.await } // Server shut down before Ctrl-C; return the result. diff --git a/core/lib/src/router/mod.rs b/core/lib/src/router/mod.rs index 8e71e22cb3..c39d38f537 100644 --- a/core/lib/src/router/mod.rs +++ b/core/lib/src/router/mod.rs @@ -47,8 +47,8 @@ impl Router { .collect() }); - trace_!("Routing the request: {}", req); - trace_!("All matches: {:?}", matches); + trace!("Routing the request: {}", req); + trace!("All matches: {:?}", matches); matches } diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index e59bd3a114..ec5592b621 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -174,7 +174,6 @@ //! [honeycomb.io]: https://crates.io/crates/tracing-honeycomb //! [timing]: https://crates.io/crates/tracing-timing //! [flame]: https://crates.io/crates/tracing-flame -use crate::logger::{LoggingLevel, COLORS_ENV}; use tracing_subscriber::{ field, fmt::{ @@ -189,6 +188,7 @@ use tracing_subscriber::{ use std::env; use std::fmt::{self, Write}; use std::sync::atomic::{AtomicU64, Ordering::{Acquire, Release}}; +use std::str::FromStr; use yansi::Paint; @@ -200,25 +200,46 @@ pub mod prelude { pub use tracing_futures::Instrument as _; } -pub(crate) fn try_init(level: LoggingLevel) -> bool { - if level == LoggingLevel::Off { - return false; - } +/// Defines the different levels for log messages. +#[derive(PartialEq, Eq, Debug, Clone, Copy)] +pub enum LoggingLevel { + /// Only shows errors, warnings, and launch information. + Critical, + /// Shows everything except debug and trace information. + Normal, + /// Shows everything. + Debug, + /// Shows nothing. + Off, +} - if !atty::is(atty::Stream::Stdout) - || (cfg!(windows) && !Paint::enable_windows_ascii()) - || env::var_os(COLORS_ENV) - .map(|v| v == "0" || v == "off") - .unwrap_or(false) - { - Paint::disable(); +impl FromStr for LoggingLevel { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + let level = match s { + "critical" => LoggingLevel::Critical, + "normal" => LoggingLevel::Normal, + "debug" => LoggingLevel::Debug, + "off" => LoggingLevel::Off, + _ => return Err("a log level (off, debug, normal, critical)"), + }; + + Ok(level) } +} - tracing::subscriber::set_global_default(tracing_subscriber::registry() - .with(logging_layer()) - .with(filter_layer(level)) - ) - .is_ok() +impl fmt::Display for LoggingLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let string = match *self { + LoggingLevel::Critical => "critical", + LoggingLevel::Normal => "normal", + LoggingLevel::Debug => "debug", + LoggingLevel::Off => "off", + }; + + write!(f, "{}", string) + } } /// Returns a Rocket filtering [`Layer`] based on the provided logging level. @@ -318,6 +339,45 @@ where .event_format(EventFormat { last_id: AtomicU64::new(0) }) } +pub(crate) const COLORS_ENV: &str = "ROCKET_CLI_COLORS"; + +pub(crate) fn try_init(level: LoggingLevel) -> bool { + if level == LoggingLevel::Off { + return false; + } + + if !atty::is(atty::Stream::Stdout) + || (cfg!(windows) && !Paint::enable_windows_ascii()) + || env::var_os(COLORS_ENV) + .map(|v| v == "0" || v == "off") + .unwrap_or(false) + { + Paint::disable(); + } + + tracing::subscriber::set_global_default(tracing_subscriber::registry() + .with(logging_layer()) + .with(filter_layer(level)) + ) + .is_ok() +} + +pub(crate) trait PaintExt { + fn emoji(item: &str) -> Paint<&str>; +} + +impl PaintExt for Paint<&str> { + /// Paint::masked(), but hidden on Windows due to broken output. See #1122. + fn emoji(item: &str) -> Paint<&str> { + if cfg!(windows) { + Paint::masked("") + } else { + Paint::masked(item) + } + } +} + + struct EventFormat { last_id: AtomicU64, } From e7b55d7cc84d47b832623bafeeaccaeb3b3ae922 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 11 Aug 2020 16:28:20 -0700 Subject: [PATCH 17/60] whoops Signed-off-by: Eliza Weisman --- contrib/lib/tests/templates.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/lib/tests/templates.rs b/contrib/lib/tests/templates.rs index ca3d062c3b..199454e7bd 100644 --- a/contrib/lib/tests/templates.rs +++ b/contrib/lib/tests/templates.rs @@ -90,7 +90,7 @@ mod templates_tests { use rocket::local::blocking::Client; const EXPECTED: &'static str - = "Hello _testtrace::\n\n
<script /> hi
\nDone.\n\n"; + = "Hello _test_!\n\n
<script /> hi
\nDone.\n\n"; #[rocket::async_test] async fn test_handlebars_templates() { From 4bd3da2aa793f3ffa7d54ecdad578fc073ce228d Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 12 Aug 2020 17:57:24 -0700 Subject: [PATCH 18/60] trailing whitespace Signed-off-by: Eliza Weisman --- core/lib/src/data/data.rs | 2 +- core/lib/src/trace.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index d54a93de6a..3b29a41497 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -164,4 +164,4 @@ impl Data { pub fn peek_complete(&self) -> bool { self.is_complete } -} +} \ No newline at end of file diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index ec5592b621..4b648f4fb1 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -39,7 +39,7 @@ //! An event consists of one or more key-value pairs, called _fields_, and/or a //! textual, human-readable _message_. For example, this will record an event //! at the `info` level, with two fields, named `answer` and `question`: -//! +//! //! ``` //! # use rocket::trace; //! trace::info!(answer = 42, question = "life, the universe, and everything"); @@ -61,7 +61,7 @@ //! async fn jump_to_hyperspace(destination: Planet) { //! // This event will be recorded *within* the `jump_to_hyperspace` span. //! tracing::debug!("preparing to jump to hyperspace..."); -//! +//! //! // ... //! } //! ``` @@ -244,7 +244,7 @@ impl fmt::Display for LoggingLevel { /// Returns a Rocket filtering [`Layer`] based on the provided logging level. /// -/// The returned [`Layer`] can be added to another `tracing` subscriber to +/// The returned [`Layer`] can be added to another `tracing` subscriber to /// configure it to filter spans and events based on the logging level /// specified in the Rocket config. /// @@ -272,7 +272,7 @@ impl fmt::Display for LoggingLevel { /// ``` /// /// [`Layer`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html -pub fn filter_layer(level: LoggingLevel) -> impl Layer +pub fn filter_layer(level: LoggingLevel) -> impl Layer where S: tracing::Subscriber, { @@ -421,7 +421,7 @@ where seen = true; } Some(id) - } else { + } else { None }; @@ -461,7 +461,7 @@ fn with_meta( meta: &tracing::Metadata<'_>, f: impl fmt::Display, ) -> fmt::Result { - + struct WithFile<'a, F> { meta: &'a tracing::Metadata<'a>, f: F, From 99058873aae19fbdfa11daba2963bf7a4f128246 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 12 Aug 2020 18:02:21 -0700 Subject: [PATCH 19/60] trailing whitespace again Signed-off-by: Eliza Weisman --- core/lib/src/data/data.rs | 2 +- core/lib/src/data/data_stream.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index 3b29a41497..63a36e7673 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -136,7 +136,7 @@ impl Data { Ok(0) => { self.is_complete = true; break }, Ok(n) => len += n, Err(e) => { - error_!("Failed to read into peek buffer: {:?}.", e); + error!("Failed to read into peek buffer: {:?}.", e); break; } } diff --git a/core/lib/src/data/data_stream.rs b/core/lib/src/data/data_stream.rs index 4e03141877..55742da19e 100644 --- a/core/lib/src/data/data_stream.rs +++ b/core/lib/src/data/data_stream.rs @@ -120,7 +120,7 @@ impl AsyncRead for DataStream { ) -> Poll> { let span = tracing::trace_span!("DataStream::poll_read()"); let _e = span.enter(); - + if self.buffer.limit() > 0 { trace!("DataStream::buffer_read()"); match Pin::new(&mut self.buffer).poll_read(cx, buf) { From 118e13e6f0cad21de77f69a4f351b9ccdf45bf09 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 12 Aug 2020 18:02:44 -0700 Subject: [PATCH 20/60] fixup Signed-off-by: Eliza Weisman --- core/lib/src/rocket.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 2453a7abca..57e4946488 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -98,7 +98,7 @@ impl Rocket { }; if let Some(existing) = existing { - warn_!("Replacing existing '{}' catcher.", existing); + warn!("Replacing existing '{}' catcher.", existing); } } } From 03533ced8936e8ebd690d8f04c4b65ac9453b4e6 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 13 Aug 2020 11:34:21 -0700 Subject: [PATCH 21/60] use `macro_use` imports everywhere Signed-off-by: Eliza Weisman --- core/lib/src/data/data_stream.rs | 2 +- core/lib/src/rocket.rs | 48 ++++++++++++++++---------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/core/lib/src/data/data_stream.rs b/core/lib/src/data/data_stream.rs index 55742da19e..300434c773 100644 --- a/core/lib/src/data/data_stream.rs +++ b/core/lib/src/data/data_stream.rs @@ -118,7 +118,7 @@ impl AsyncRead for DataStream { cx: &mut Context<'_>, buf: &mut [u8] ) -> Poll> { - let span = tracing::trace_span!("DataStream::poll_read()"); + let span = trace_span!("DataStream::poll_read()"); let _e = span.enter(); if self.buffer.limit() > 0 { diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 57e4946488..f52c4ab045 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -67,26 +67,26 @@ pub(crate) struct Token; impl Rocket { #[inline] fn _mount(&mut self, base: Origin<'static>, routes: Vec) { - let span = tracing::info_span!("mounting", at = %Paint::blue(&base), "{} Mounting", Paint::emoji("🛰 "),); + let span = info_span!("mounting", at = %Paint::blue(&base), "{} Mounting", Paint::emoji("🛰 "),); let _e = span.enter(); for route in routes { let old_route = route.clone(); let route = route.map_base(|old| format!("{}{}", base, old)) .unwrap_or_else(|e| { - let span = tracing::error_span!("malformed_uri", "Route `{}` has a malformed URI.", old_route); + let span = error_span!("malformed_uri", "Route `{}` has a malformed URI.", old_route); error!(parent: &span, "{}", e); panic!("Invalid route URI."); }); - tracing::info!(%route); + info!(%route); self.router.add(route); } } #[inline] fn _register(&mut self, catchers: Vec) { - let span = tracing::info_span!("catchers", "{}{}", Paint::emoji("👾 "), Paint::magenta("Catchers:")); + let span = info_span!("catchers", "{}{}", Paint::emoji("👾 "), Paint::magenta("Catchers:")); let _e = span.enter(); for catcher in catchers { @@ -215,7 +215,7 @@ async fn hyper_service_fn( let token = rocket.preprocess_request(&mut req, &mut data).await; let r = rocket.dispatch(token, &mut req, data).await; rocket.issue_response(r, tx).await; - }.instrument(tracing::info_span!("connection", from = %h_addr, "{}Connection", Paint::emoji("📡 ")))); + }.instrument(info_span!("connection", from = %h_addr, "{}Connection", Paint::emoji("📡 ")))); rx.await.map_err(|e| io::Error::new(io::ErrorKind::Other, e)) } @@ -438,7 +438,7 @@ impl Rocket { } response - }.instrument(tracing::info_span!("request", "{}", request)) + }.instrument(info_span!("request", "{}", request)) .await } @@ -453,7 +453,7 @@ impl Rocket { req: &'r Request<'s> ) -> impl Future> + 's { async move { - tracing::warn_span!("Responding..."); + warn!("Responding..."); // For now, we reset the delta state to prevent any modifications // from earlier, unsuccessful paths from being reflected in error // response. We may wish to relax this in the future. @@ -481,7 +481,7 @@ impl Rocket { default.expect("Rocket has default 500 response") } } - }.instrument(tracing::warn_span!("handle_error", "Responding with {} catcher.", Paint::red(&status))) + }.instrument(warn_span!("handle_error", "Responding with {} catcher.", Paint::red(&status))) } // TODO.async: Solidify the Listener APIs and make this function public @@ -620,29 +620,29 @@ impl Rocket { #[inline] fn configured(config: Config) -> Rocket { crate::trace::try_init(config.log_level); - let span = tracing::info_span!("configured", "{}Configured for {}", Paint::emoji("🔧 "), config.environment,); + let span = info_span!("configured", "{}Configured for {}", Paint::emoji("🔧 "), config.environment,); let _e = span.enter(); - tracing::info!(address = %&config.address); - tracing::info!(port = %&config.port); - tracing::info!(log = %&config.log_level); - tracing::info!(workers = %&config.workers); - tracing::info!(secret_key = %&config.secret_key); - tracing::info!(limits = %&config.limits); + info!(address = %&config.address); + info!(port = %&config.port); + info!(log = %&config.log_level); + info!(workers = %&config.workers); + info!(secret_key = %&config.secret_key); + info!(limits = %&config.limits); match config.keep_alive { - Some(v) => tracing::info!(keep_alive = %Paint::default(format!("{}s", v)).bold()), - None => tracing::info!(keep_alive = %Paint::default("disabled").bold()), + Some(v) => info!(keep_alive = %Paint::default(format!("{}s", v)).bold()), + None => info!(keep_alive = %Paint::default("disabled").bold()), } let tls_configured = config.tls.is_some(); if tls_configured && cfg!(feature = "tls") { - tracing::info!(tls = %Paint::default("enabled").bold()); + info!(tls = %Paint::default("enabled").bold()); } else if tls_configured { - tracing::error!(tls = %Paint::default("disabled").bold()); - tracing::error!("tls is configured, but the tls feature is disabled"); + error!(tls = %Paint::default("disabled").bold()); + error!("tls is configured, but the tls feature is disabled"); } else { - tracing::info!(tls = %Paint::default("disabled").bold()); + info!(tls = %Paint::default("disabled").bold()); } if config.secret_key.is_generated() && config.environment.is_prod() { @@ -650,9 +650,9 @@ impl Rocket { } for (name, value) in config.extras() { - tracing::info!("{} {}: {}", - Paint::yellow("[extra]"), name, - Paint::default(LoggedValue(value)).bold()); + info!("{} {}: {}", + Paint::yellow("[extra]"), name, + Paint::default(LoggedValue(value)).bold()); } let managed_state = Container::new(); From 9e6adc65194c9640a52f186086b95913cfbc3361 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 13 Aug 2020 12:07:50 -0700 Subject: [PATCH 22/60] use spans for all indentation-based messages Signed-off-by: Eliza Weisman --- contrib/codegen/src/database.rs | 17 ++++--- contrib/lib/src/helmet/helmet.rs | 13 +++-- contrib/lib/src/templates/fairing.rs | 12 +++-- .../lib/src/templates/handlebars_templates.rs | 12 ++--- contrib/lib/src/templates/metadata.rs | 6 +-- contrib/lib/src/templates/mod.rs | 20 ++++---- contrib/lib/src/templates/tera_templates.rs | 15 +++--- core/lib/src/config/error.rs | 50 ++++++++++--------- core/lib/src/request/request.rs | 9 ++-- core/lib/src/trace.rs | 5 +- 10 files changed, 90 insertions(+), 69 deletions(-) diff --git a/contrib/codegen/src/database.rs b/contrib/codegen/src/database.rs index 575c056523..214339e260 100644 --- a/contrib/codegen/src/database.rs +++ b/contrib/codegen/src/database.rs @@ -99,15 +99,20 @@ pub fn database_attr(attr: TokenStream, input: TokenStream) -> Result Ok(rocket.manage(#pool_type(p))), Err(config_error) => { - ::rocket::trace::error( - &format!("Database configuration failure: '{}'", #name)); - ::rocket::trace::error_(&format!("{}", config_error)); + let span = ::rocket::trace::error_span!( + "db_config_error", + database = %#name, + "Database configuration failure" + ); + ::rocket::trace::error!(parent: &span, %error); Err(rocket) }, Ok(Err(pool_error)) => { - ::rocket::trace::error( - &format!("Failed to initialize pool for '{}'", #name)); - ::rocket::trace::error_(&format!("{:?}", pool_error)); + let span = ::rocket::trace::error_span!( + "db_pool_error" + database = %#name, + "Failed to initialize pool"); + ::rocket::trace::error!(parent: &span, error = ?pool_error); Err(rocket) }, } diff --git a/contrib/lib/src/helmet/helmet.rs b/contrib/lib/src/helmet/helmet.rs index 746bd04895..584a7afe1c 100644 --- a/contrib/lib/src/helmet/helmet.rs +++ b/contrib/lib/src/helmet/helmet.rs @@ -171,8 +171,11 @@ impl SpaceHelmet { for policy in self.policies.values() { let name = policy.name(); if response.headers().contains(name.as_str()) { - warn!("Space Helmet: response contains a '{}' header.", name); - warn!("Refusing to overwrite existing header."); + let span = warn_span!( + "Space Helmet: response contains already contains this header", + header = %name, + ); + warn!(parent: &span, "Refusing to overwrite existing header."); continue } @@ -206,9 +209,9 @@ impl Fairing for SpaceHelmet { && !cargo.config().environment.is_dev() && !self.is_enabled::() { - warn!("Space Helmet: deploying with TLS without enabling HSTS."); - warn!("Enabling default HSTS policy."); - info!("To disable this warning, configure an HSTS policy."); + let span = warn_span!("Space Helmet: deploying with TLS without enabling HSTS."); + warn!(parent: &span, "Enabling default HSTS policy."); + info!(parent: &span, "To disable this warning, configure an HSTS policy."); self.force_hsts.store(true, Ordering::Relaxed); } } diff --git a/contrib/lib/src/templates/fairing.rs b/contrib/lib/src/templates/fairing.rs index da8b2e58eb..c331a3dece 100644 --- a/contrib/lib/src/templates/fairing.rs +++ b/contrib/lib/src/templates/fairing.rs @@ -59,10 +59,10 @@ mod context { let watcher = match watcher { Ok(watcher) => Some(Mutex::new((watcher, rx))), - Err(e) => { - warn!("Failed to enable live template reloading: {}", e); - debug!("Reload error: {:?}", e); - warn!("Live template reloading is unavailable."); + Err(error) => { + let span = warn_span!("Failed to enable live template reloading", %error); + debug!(parent: &span, reload_error = ?error); + warn!(parent: &span, "Live template reloading is unavailable."); None } }; @@ -98,11 +98,13 @@ mod context { } if changed { - info!("Change detected: reloading templates."); + let span = info_span!("Change detected: reloading templates."); + let _entered = span.enter(); let mut ctxt = self.context_mut(); if let Some(mut new_ctxt) = Context::initialize(ctxt.root.clone()) { custom_callback(&mut new_ctxt.engines); *ctxt = new_ctxt; + info!("reloaded!"); } else { warn!("An error occurred while reloading templates."); warn!("The previous templates will remain active."); diff --git a/contrib/lib/src/templates/handlebars_templates.rs b/contrib/lib/src/templates/handlebars_templates.rs index d6bd5b6f39..5d4bffda77 100644 --- a/contrib/lib/src/templates/handlebars_templates.rs +++ b/contrib/lib/src/templates/handlebars_templates.rs @@ -12,9 +12,9 @@ impl Engine for Handlebars<'static> { for &(name, info) in templates { let path = &info.path; if let Err(e) = hb.register_template_file(name, path) { - error!("Error in Handlebars template '{}'.", name); - info!("{}", e); - info!("Template path: '{}'.", path.to_string_lossy()); + let span = error_span!("Error in Handlebars template", template = %name); + info!(parent: &span, template.error = %e); + info!(parent: &span, template.path = path.to_string_lossy()); return None; } } @@ -24,14 +24,14 @@ impl Engine for Handlebars<'static> { fn render(&self, name: &str, context: C) -> Option { if self.get_template(name).is_none() { - error!("Handlebars template '{}' does not exist.", name); + error!(template = %name, "Handlebars template does not exist."); return None; } match Handlebars::render(self, name, &context) { Ok(string) => Some(string), - Err(e) => { - error!("Error rendering Handlebars template '{}': {}", name, e); + Err(error) => { + error!(template = %name, %error, "Error rendering Handlebars template"); None } } diff --git a/contrib/lib/src/templates/metadata.rs b/contrib/lib/src/templates/metadata.rs index 2d85405a46..7660fce435 100644 --- a/contrib/lib/src/templates/metadata.rs +++ b/contrib/lib/src/templates/metadata.rs @@ -93,9 +93,9 @@ impl<'a, 'r> FromRequest<'a, 'r> for Metadata<'a> { .succeeded() .and_then(|cm| Some(request::Outcome::Success(Metadata(cm.inner())))) .unwrap_or_else(|| { - error!("Uninitialized template context: missing fairing."); - info!("To use templates, you must attach `Template::fairing()`."); - info!("See the `Template` documentation for more information."); + let span = error_span!("Uninitialized template context: missing fairing."); + info!(parent: &span, "To use templates, you must attach `Template::fairing()`."); + info!(parent: &span, "See the `Template` documentation for more information."); request::Outcome::Failure((Status::InternalServerError, ())) }) } diff --git a/contrib/lib/src/templates/mod.rs b/contrib/lib/src/templates/mod.rs index 17e7d0fa07..2e7a1efa02 100644 --- a/contrib/lib/src/templates/mod.rs +++ b/contrib/lib/src/templates/mod.rs @@ -345,9 +345,9 @@ impl Template { where S: Into>, C: Serialize { let ctxt = cargo.state::().map(ContextManager::context).or_else(|| { - warn!("Uninitialized template context: missing fairing."); - info!("To use templates, you must attach `Template::fairing()`."); - info!("See the `Template` documentation for more information."); + let span = warn_span!("Uninitialized template context: missing fairing."); + info!(parent: &span, "To use templates, you must attach `Template::fairing()`."); + info!(parent: &span, "See the `Template` documentation for more information."); None })?; @@ -362,9 +362,9 @@ impl Template { let name = &*self.name; let info = ctxt.templates.get(name).ok_or_else(|| { let ts: Vec<_> = ctxt.templates.keys().map(|s| s.as_str()).collect(); - error!("Template '{}' does not exist.", name); - info!("Known templates: {}", ts.join(",")); - info!("Searched in '{:?}'.", ctxt.root); + let span = error_span!("Template does not exist.", template = %name); + info!(parent: &span, "Known templates: {}", ts.join(",")); + info!(parent: &span, "Searched in '{:?}'.", ctxt.root); Status::InternalServerError })?; @@ -374,7 +374,7 @@ impl Template { })?; let string = ctxt.engines.render(name, &info, value).ok_or_else(|| { - error!("Template '{}' failed to render.", name); + error!(template = %name, "Template failed to render."); Status::InternalServerError })?; @@ -389,9 +389,9 @@ impl<'r> Responder<'r, 'static> for Template { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { let (render, content_type) = { let ctxt = req.managed_state::().ok_or_else(|| { - error!("Uninitialized template context: missing fairing."); - info!("To use templates, you must attach `Template::fairing()`."); - info!("See the `Template` documentation for more information."); + let span = error_span!("Uninitialized template context: missing fairing."); + info!(parent: &span, "To use templates, you must attach `Template::fairing()`."); + info!(parent: &span, "See the `Template` documentation for more information."); Status::InternalServerError })?.context(); diff --git a/contrib/lib/src/templates/tera_templates.rs b/contrib/lib/src/templates/tera_templates.rs index 70cf2094c0..adfe3aa7b1 100644 --- a/contrib/lib/src/templates/tera_templates.rs +++ b/contrib/lib/src/templates/tera_templates.rs @@ -21,11 +21,12 @@ impl Engine for Tera { // Finally try to tell Tera about all of the templates. if let Err(e) = tera.add_template_files(tera_templates) { - error!("Failed to initialize Tera templating."); + let span = error_span!("Failed to initialize Tera templating."); + let _entered = span.enter(); let mut error = Some(&e as &dyn Error); while let Some(err) = error { - info!("{}", err); + error!(error = %err); error = err.source(); } @@ -36,6 +37,8 @@ impl Engine for Tera { } fn render(&self, name: &str, context: C) -> Option { + let span = info_span!("Rendering Tera template", template = %name); + let _entered = span.enter(); if self.get_template(name).is_err() { error!("Tera template '{}' does not exist.", name); return None; @@ -46,7 +49,7 @@ impl Engine for Tera { Err(_) => { error!( "Error generating context when rendering Tera template '{}'.", - name + name, ); return None; } @@ -55,11 +58,11 @@ impl Engine for Tera { match Tera::render(self, name, &tera_ctx) { Ok(string) => Some(string), Err(e) => { - error!("Error rendering Tera template '{}'.", name); - + let span = error_span!("Error rendering Tera template", template = %name); + let _entered = span.enter(); let mut error = Some(&e as &dyn Error); while let Some(err) = error { - error!("{}", err); + error!(error = %err); error = err.source(); } diff --git a/core/lib/src/config/error.rs b/core/lib/src/config/error.rs index d36b0f9dbf..428fc1b078 100644 --- a/core/lib/src/config/error.rs +++ b/core/lib/src/config/error.rs @@ -63,47 +63,51 @@ impl ConfigError { IoError => error!("failed reading the config file: IO error"), RandFailure => error!("failed to read randomness from the OS"), Io(ref error, param) => { - error!("I/O error while setting {}:", Paint::default(param).bold()); - info!("{}", error); + let span = error_span!("I/O error while setting param", + param = %Paint::default(param).bold()); + info!(parent: &span, error = %error); } BadFilePath(ref path, reason) => { - error!("configuration file path {} is invalid", - Paint::default(path.display()).bold()); - info!("{}", reason); + let span = error_span!("configuration file path is invalid", + path = %Paint::default(path.display()).bold()); + info!(parent: &span, reason = %reason); } BadEntry(ref name, ref filename) => { let valid_entries = format!("{}, global", valid_envs); - error!("{} is not a known configuration environment", - Paint::default(format!("[{}]", name)).bold()); - info!("in {}", Paint::default(filename.display()).bold()); - info!("valid environments are: {}", Paint::default(&valid_entries).bold()); + let span = error_span!("Not a known configuration environment", + environment = %Paint::default(name).bold()); + info!(parent: &span, "in {}", Paint::default(filename.display()).bold()); + info!(parent: &span, "valid environments are: {}", Paint::default(&valid_entries).bold()); } BadEnv(ref name) => { - error!("{} is not a valid ROCKET_ENV value", Paint::default(name).bold()); - info!("valid environments are: {}", Paint::default(valid_envs).bold()); + let span = error_span!("Not a valid ROCKET_ENV value", + value = %Paint::default(name).bold()); + info!(parent: &span, "valid environments are: {}", Paint::default(valid_envs).bold()); } BadType(ref name, expected, actual, ref filename) => { - error!("{} key could not be parsed", Paint::default(name).bold()); + let span = error_span!("bad_type", "{} key could not be parsed", Paint::default(name).bold()); if let Some(filename) = filename { - info!("in {}", Paint::default(filename.display()).bold()); + info!(parent: &span, "in {}", Paint::default(filename.display()).bold()); } - info!("expected value to be {}, but found {}", - Paint::default(expected).bold(), Paint::default(actual).bold()); + info!(parent: &span, + "expected value to be {}, but found {}", + Paint::default(expected).bold(), Paint::default(actual).bold()); } ParseError(_, ref filename, ref desc, line_col) => { - error!("config file failed to parse due to invalid TOML"); - info!("{}", desc); - info!("in {}", Paint::default(filename.display()).bold()); + let span = error_span!("config file failed to parse due to invalid TOML"); + info!(parent: &span, "{}", desc); + info!(parent: &span, "in {}", Paint::default(filename.display()).bold()); if let Some((line, col)) = line_col { - info!("at line {}, column {}", - Paint::default(line + 1).bold(), Paint::default(col + 1).bold()); + info!(parent: &span, + line = %Paint::default(line + 1).bold(), + column = %Paint::default(col + 1).bold()); } } BadEnvVal(ref key, ref value, ref error) => { - error!("environment variable {} could not be parsed", - Paint::default(format!("ROCKET_{}={}", key.to_uppercase(), value)).bold()); - info!("{}", error); + let span = error_span!("environment variable could not be parsed", + variable = %Paint::default(format!("ROCKET_{}={}", key.to_uppercase(), value)).bold()); + info!(parent: &span, error = %error); } UnknownKey(ref key) => { error!("the configuration key {} is unknown and disallowed in \ diff --git a/core/lib/src/request/request.rs b/core/lib/src/request/request.rs index 477b3d355d..f0de1f312f 100644 --- a/core/lib/src/request/request.rs +++ b/core/lib/src/request/request.rs @@ -338,10 +338,11 @@ impl<'r> Request<'r> { Cookies::new(jar, self.state.config.secret_key(), on_drop) } None => { - error!("Multiple `Cookies` instances are active at once."); - info!("An instance of `Cookies` must be dropped before another \ - can be retrieved."); - warn!("The retrieved `Cookies` instance will be empty."); + let span = error_span!("Multiple `Cookies` instances are active at once."); + info!(parent: &span, + "An instance of `Cookies` must be dropped before another \ + can be retrieved."); + warn!(parent: &span, "The retrieved `Cookies` instance will be empty."); Cookies::empty() } } diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 4b648f4fb1..36ee7bacad 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -192,7 +192,10 @@ use std::str::FromStr; use yansi::Paint; -pub use tracing::{trace, debug, info, warn, error, instrument}; +pub use tracing::{ + trace, debug, info, warn, error, trace_span, debug_span, warn_span, + error_span, instrument, +}; /// A prelude for working with `tracing` in Rocket applications. pub mod prelude { From ec5066be398d456dab4faf2652051b117bc03c2b Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 13 Aug 2020 12:35:34 -0700 Subject: [PATCH 23/60] update codegen this tracks the removal of the logger module, and adds structured fields to codegen's warning messages. Signed-off-by: Eliza Weisman --- core/codegen/src/attribute/route.rs | 16 ++++++++-------- core/codegen/src/lib.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/codegen/src/attribute/route.rs b/core/codegen/src/attribute/route.rs index 9cb90c5441..6327a2b830 100644 --- a/core/codegen/src/attribute/route.rs +++ b/core/codegen/src/attribute/route.rs @@ -128,19 +128,19 @@ fn param_expr(seg: &Segment, ident: &syn::Ident, ty: &syn::Type) -> TokenStream define_vars_and_mods!(req, data, error, log, request, _None, _Some, _Ok, _Err, Outcome); let i = seg.index.expect("dynamic parameters must be indexed"); let span = ident.span().join(ty.span()).unwrap_or_else(|| ty.span()); - let name = ident.to_string(); // All dynamic parameter should be found if this function is being called; // that's the point of statically checking the URI parameters. let internal_error = quote!({ - #log::error("Internal invariant error: expected dynamic parameter not found."); - #log::error("Please report this error to the Rocket issue tracker."); + #log::error!("Internal invariant error: expected dynamic parameter not found."); + #log::error!("Please report this error to the Rocket issue tracker."); #Outcome::Forward(#data) }); // Returned when a dynamic parameter fails to parse. + let field_name = syn::Ident::new(&seg.name, seg.span); let parse_error = quote!({ - #log::warn_(&format!("Failed to parse '{}': {:?}", #name, #error)); + #log::warn!(#field_name = %#error, "Failed to parse dynamic parameter"); #Outcome::Forward(#data) }); @@ -231,7 +231,7 @@ fn query_exprs(route: &Route) -> Option { }, Kind::Static => quote!() }; - + let field_name = syn::Ident::new(name, segment.span); let matcher = match segment.kind { Kind::Single => quote_spanned! { span => (_, #name, __v) => { @@ -239,7 +239,7 @@ fn query_exprs(route: &Route) -> Option { let __v = match <#ty as #request::FromFormValue>::from_form_value(__v) { #_Ok(__v) => __v, #_Err(__e) => { - #log::warn_(&format!("Failed to parse '{}': {:?}", #name, __e)); + #log::warn!(#field_name = ?__e, "Failed to parse"); return #Outcome::Forward(#data); } }; @@ -261,7 +261,7 @@ fn query_exprs(route: &Route) -> Option { let #ident = match #ident.or_else(<#ty as #request::FromFormValue>::default) { #_Some(__v) => __v, #_None => { - #log::warn_(&format!("Missing required query parameter '{}'.", #name)); + #log::warn!(parameter = %#name, "Missing required query parameter"); return #Outcome::Forward(#data); } }; @@ -271,7 +271,7 @@ fn query_exprs(route: &Route) -> Option { let #ident = match <#ty as #request::FromQuery>::from_query(#Query(&#trail)) { #_Ok(__v) => __v, #_Err(__e) => { - #log::warn_(&format!("Failed to parse '{}': {:?}", #name, __e)); + #log::warn!(#field_name = ?__e, "Failed to parse"); return #Outcome::Forward(#data); } }; diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs index 56042ce63c..39261b8c31 100644 --- a/core/codegen/src/lib.rs +++ b/core/codegen/src/lib.rs @@ -82,7 +82,7 @@ vars_and_mods! { request => rocket::request, response => rocket::response, handler => rocket::handler, - log => rocket::logger, + log => rocket::trace, Outcome => rocket::outcome::Outcome, FromTransformedData => rocket::data::FromTransformedData, Transform => rocket::data::Transform, From 6144caa7f200227bd83e34284c614627e12973a4 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 13 Aug 2020 12:46:20 -0700 Subject: [PATCH 24/60] add spans to generated catcher/route fns Signed-off-by: Eliza Weisman --- core/codegen/src/attribute/catch.rs | 6 ++++-- core/codegen/src/attribute/route.rs | 6 ++++-- core/lib/src/trace.rs | 29 ++++++++++++++++++++++------- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/core/codegen/src/attribute/catch.rs b/core/codegen/src/attribute/catch.rs index fed74856c4..5e9bbb7272 100644 --- a/core/codegen/src/attribute/catch.rs +++ b/core/codegen/src/attribute/catch.rs @@ -77,12 +77,13 @@ pub fn _catch( let user_catcher_fn_name = catch.function.sig.ident.clone(); let generated_struct_name = user_catcher_fn_name.prepend(CATCH_STRUCT_PREFIX); let generated_fn_name = user_catcher_fn_name.prepend(CATCH_FN_PREFIX); + let generated_span_name = user_catcher_fn_name.to_string(); let (vis, catcher_status) = (&catch.function.vis, &catch.status); let status_code = Optional(catcher_status.as_ref().map(|s| s.0.code)); // Variables names we'll use and reuse. define_vars_and_mods!(catch.function.span().into() => - req, status, _Box, Request, Response, ErrorHandlerFuture, Status); + req, status, log, _Box, Request, Response, ErrorHandlerFuture, Status); // Determine the number of parameters that will be passed in. if catch.function.sig.inputs.len() > 2 { @@ -125,13 +126,14 @@ pub fn _catch( #status: #Status, #req: &'_b #Request ) -> #ErrorHandlerFuture<'_b> { + use #log::Instrument as _; #_Box::pin(async move { let __response = #catcher_response; #Response::build() .status(#status) .merge(__response) .ok() - }) + }.instrument(#log::warn_span!(#generated_span_name, status = %#status))) } /// Rocket code generated static catcher info. diff --git a/core/codegen/src/attribute/route.rs b/core/codegen/src/attribute/route.rs index 6327a2b830..28873a2425 100644 --- a/core/codegen/src/attribute/route.rs +++ b/core/codegen/src/attribute/route.rs @@ -408,13 +408,14 @@ fn codegen_route(route: Route) -> Result { } // Gather everything we need. - define_vars_and_mods!(req, data, _Box, Request, Data, StaticRouteInfo, HandlerFuture); + define_vars_and_mods!(req, log, data, _Box, Request, Data, StaticRouteInfo, HandlerFuture); let (vis, user_handler_fn) = (&route.function.vis, &route.function); let user_handler_fn_name = &user_handler_fn.sig.ident; let generated_fn_name = user_handler_fn_name.prepend(ROUTE_FN_PREFIX); let generated_struct_name = user_handler_fn_name.prepend(ROUTE_STRUCT_PREFIX); let generated_internal_uri_macro = generate_internal_uri_macro(&route); let generated_respond_expr = generate_respond_expr(&route); + let generated_span_name = user_handler_fn_name.to_string(); let method = route.attribute.method; let path = route.attribute.path.origin.0.to_string(); @@ -430,13 +431,14 @@ fn codegen_route(route: Route) -> Result { #req: &'_b #Request, #data: #Data ) -> #HandlerFuture<'_b> { + use #log::Instrument as _; #_Box::pin(async move { #(#req_guard_definitions)* #(#parameter_definitions)* #data_stmt #generated_respond_expr - }) + }.instrument(#log::info_span!(#generated_span_name))) } /// Rocket code generated wrapping URI macro. diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 36ee7bacad..920ded81a1 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -48,16 +48,29 @@ //! //! Spans may be recorded in a few different ways. Like events, they have a //! priority level, and may have one or more fields. In addition, all spans also -//! have a _name_. The easiest way to record a span is to add the -//! [`#[tracing::instrument]`][instrument] attribute to a function. For example: +//! have a _name_. //! +//! Rocket's code generation will automatically generate spans for route and +//! catcher functions, so any events emitted within those functions or functions +//! they call will be annotated with the name of the handler or catcher +//! function. For example: +//! ``` +//! #[get("/hello-world")] +//! fn hello_world() -> String { +//! // This event will occur within a span named `hello_world`. +//! rocket::trace::info!("saying hello!"); //! +//! "Hello world".to_string() +//! } //! ``` -//! use rocket::trace; +//! Spans may be added to other functions, as well. The easiest way to +//! do this is to add the [`#[rocket::trace::instrument]`][instrument] attribute +//! to that function. For example: //! +//! ``` //! # #[derive(Debug)] struct Planet; //! // Calling this function will enter a new span named `jump_to_hyperspace`. -//! #[trace::instrument] +//! #[rocket::trace::instrument] //! async fn jump_to_hyperspace(destination: Planet) { //! // This event will be recorded *within* the `jump_to_hyperspace` span. //! tracing::debug!("preparing to jump to hyperspace..."); @@ -193,14 +206,16 @@ use std::str::FromStr; use yansi::Paint; pub use tracing::{ - trace, debug, info, warn, error, trace_span, debug_span, warn_span, - error_span, instrument, + trace, debug, info, warn, error, trace_span, debug_span, info_span, + warn_span, error_span, instrument, }; +pub use tracing_futures::Instrument; + /// A prelude for working with `tracing` in Rocket applications. pub mod prelude { pub use tracing_subscriber::prelude::*; - pub use tracing_futures::Instrument as _; + pub use super::Instrument as _; } /// Defines the different levels for log messages. From e1b67dbc6903a5e36e98afffe74e87e7cb1e7ac3 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 13 Aug 2020 13:01:36 -0700 Subject: [PATCH 25/60] make catcher spans info Signed-off-by: Eliza Weisman --- core/codegen/src/attribute/catch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/codegen/src/attribute/catch.rs b/core/codegen/src/attribute/catch.rs index 5e9bbb7272..1870c7d3cd 100644 --- a/core/codegen/src/attribute/catch.rs +++ b/core/codegen/src/attribute/catch.rs @@ -133,7 +133,7 @@ pub fn _catch( .status(#status) .merge(__response) .ok() - }.instrument(#log::warn_span!(#generated_span_name, status = %#status))) + }.instrument(#log::info_span!(#generated_span_name, status = %#status))) } /// Rocket code generated static catcher info. From cd124d8904a0459a1df03ae80dba855da6e1045a Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 13 Aug 2020 13:11:44 -0700 Subject: [PATCH 26/60] bold span names when no message is present Signed-off-by: Eliza Weisman --- core/lib/src/trace.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 920ded81a1..c072306644 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -427,10 +427,10 @@ where if meta.fields().iter().any(|field| field.name() == "message") { with_meta(writer, meta, &fields.fields)?; } else { - with_meta(writer, meta, format_args!("{} {}", span.name(), &fields.fields))?; + with_meta(writer, meta, format_args!("{} {}", Paint::new(span.name()).bold(), &fields.fields))?; } } else { - with_meta(writer, span.metadata(), span.name())?; + with_meta(writer, span.metadata(), Paint::new(span.name()).bold())?; } seen = true; Ok(()) From bc492ba4f3b34c0636483ab4ebd4078beb8f111b Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 14 Aug 2020 09:58:30 -0700 Subject: [PATCH 27/60] add more info to generated spans Signed-off-by: Eliza Weisman --- core/codegen/src/attribute/catch.rs | 6 +++++- core/codegen/src/attribute/route.rs | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/core/codegen/src/attribute/catch.rs b/core/codegen/src/attribute/catch.rs index 1870c7d3cd..e4d6f89645 100644 --- a/core/codegen/src/attribute/catch.rs +++ b/core/codegen/src/attribute/catch.rs @@ -133,7 +133,11 @@ pub fn _catch( .status(#status) .merge(__response) .ok() - }.instrument(#log::info_span!(#generated_span_name, status = %#status))) + }.instrument(#log::info_span!( + #generated_span_name, + status = %#status, + "Catcher: {}", #generated_span_name + ))) } /// Rocket code generated static catcher info. diff --git a/core/codegen/src/attribute/route.rs b/core/codegen/src/attribute/route.rs index 28873a2425..e817ec0b83 100644 --- a/core/codegen/src/attribute/route.rs +++ b/core/codegen/src/attribute/route.rs @@ -438,7 +438,12 @@ fn codegen_route(route: Route) -> Result { #data_stmt #generated_respond_expr - }.instrument(#log::info_span!(#generated_span_name))) + }.instrument(#log::info_span!( + #generated_span_name, + method = %#method, + path = #path, + "Route: {}", #generated_span_name + ))) } /// Rocket code generated wrapping URI macro. From 88e965e07beef2d5ec3cc4d6bd8aa5e43578a076 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Sat, 31 Oct 2020 09:57:47 -0700 Subject: [PATCH 28/60] Replace `log` with `tracing` and `tracing-subscriber`. * Use `tracing` macros for 'info', 'error', 'warn', etc. * Use `tracing` '*_span' variants of macros where indentataion was used * Replace Rocket's implementation of `log::Log` with `logging_layer` and `filter_layer`, which return implementations of `tracing_subscriber::fmt::Layer`. Co-authored-by: Jeb Rosen --- contrib/lib/Cargo.toml | 2 +- contrib/lib/src/compression/fairing.rs | 8 +- contrib/lib/src/databases.rs | 20 +- contrib/lib/src/helmet/helmet.rs | 13 +- contrib/lib/src/json.rs | 4 +- contrib/lib/src/lib.rs | 2 +- contrib/lib/src/msgpack.rs | 4 +- contrib/lib/src/serve.rs | 4 +- contrib/lib/src/templates/context.rs | 8 +- contrib/lib/src/templates/fairing.rs | 24 +- .../lib/src/templates/handlebars_templates.rs | 12 +- contrib/lib/src/templates/metadata.rs | 6 +- contrib/lib/src/templates/mod.rs | 22 +- contrib/lib/src/templates/tera_templates.rs | 17 +- core/codegen/src/attribute/catch.rs | 10 +- core/codegen/src/attribute/route.rs | 74 ++- core/codegen/src/lib.rs | 2 +- core/lib/Cargo.toml | 11 + core/lib/src/config/config.rs | 52 +- core/lib/src/config/mod.rs | 4 +- core/lib/src/data/data.rs | 4 +- core/lib/src/data/data_stream.rs | 7 +- core/lib/src/error.rs | 23 +- core/lib/src/fairing/fairings.rs | 4 +- core/lib/src/lib.rs | 5 +- core/lib/src/logger.rs | 218 ------- core/lib/src/request/form/form.rs | 6 +- core/lib/src/request/request.rs | 2 +- core/lib/src/request/state.rs | 2 +- core/lib/src/response/debug.rs | 11 +- core/lib/src/response/flash.rs | 6 +- core/lib/src/response/responder.rs | 4 +- core/lib/src/response/response.rs | 4 +- core/lib/src/rocket.rs | 28 +- core/lib/src/router/mod.rs | 4 +- core/lib/src/server.rs | 82 +-- core/lib/src/trace.rs | 559 ++++++++++++++++++ examples/todo/src/main.rs | 8 +- 38 files changed, 837 insertions(+), 439 deletions(-) delete mode 100644 core/lib/src/logger.rs create mode 100644 core/lib/src/trace.rs diff --git a/contrib/lib/Cargo.toml b/contrib/lib/Cargo.toml index 6f67aa23db..3e49fb367d 100644 --- a/contrib/lib/Cargo.toml +++ b/contrib/lib/Cargo.toml @@ -45,7 +45,7 @@ memcache_pool = ["databases", "memcache", "r2d2-memcache"] tokio = { version = "0.2.0", optional = true } rocket_contrib_codegen = { version = "0.5.0-dev", path = "../codegen", optional = true } rocket = { version = "0.5.0-dev", path = "../../core/lib/", default-features = false } -log = "0.4" +tracing = { version = "0.1", default-features = false } # Serialization and templating dependencies. serde = { version = "1.0", optional = true } diff --git a/contrib/lib/src/compression/fairing.rs b/contrib/lib/src/compression/fairing.rs index 00f9897504..7ded9f141d 100644 --- a/contrib/lib/src/compression/fairing.rs +++ b/contrib/lib/src/compression/fairing.rs @@ -115,17 +115,17 @@ impl Fairing for Compression { if let Value::String(s) = ex { let mt = MediaType::parse_flexible(s); if mt.is_none() { - warn_!("Ignoring invalid media type '{:?}'", s); + warn!("Ignoring invalid media type '{:?}'", s); } mt } else { - warn_!("Ignoring non-string media type '{:?}'", ex); + warn!("Ignoring non-string media type '{:?}'", ex); None } }).collect(); } None => { - warn_!( + warn!( "Exclusions is not an array; using default compression exclusions '{:?}'", ctxt.exclusions ); @@ -134,7 +134,7 @@ impl Fairing for Compression { Err(ConfigError::Missing(_)) => { /* ignore missing */ } Err(e) => { e.pretty_print(); - warn_!( + warn!( "Using default compression exclusions '{:?}'", ctxt.exclusions ); diff --git a/contrib/lib/src/databases.rs b/contrib/lib/src/databases.rs index ddb89d0a4d..d7d948b78d 100644 --- a/contrib/lib/src/databases.rs +++ b/contrib/lib/src/databases.rs @@ -748,9 +748,9 @@ async fn run_blocking(job: F) -> R } macro_rules! dberr { - ($msg:literal, $db_name:expr, $efmt:literal, $error:expr, $rocket:expr) => ({ - rocket::error!(concat!("database ", $msg, " error for pool named `{}`"), $db_name); - error_!($efmt, $error); + ($target:literal, $msg:literal, $db_name:expr, $efmt:literal, $error:expr, $rocket:expr) => ({ + let span = rocket::trace::error_span!($target, "database {} error for pool named `{}`", $msg, $db_name); + rocket::trace::error!(parent: &span, $efmt, $error); return Err($rocket); }); } @@ -760,7 +760,7 @@ impl ConnectionPool { AdHoc::on_attach(fairing_name, move |rocket| async move { let config = match Config::from(db, &rocket) { Ok(config) => config, - Err(e) => dberr!("config", db, "{}", e, rocket), + Err(e) => dberr!("config_error", "config", db, "{}", e, rocket), }; let pool_size = config.pool_size; @@ -770,9 +770,9 @@ impl ConnectionPool { semaphore: Arc::new(Semaphore::new(pool_size as usize)), _marker: PhantomData, })), - Err(Error::Config(e)) => dberr!("config", db, "{}", e, rocket), - Err(Error::Pool(e)) => dberr!("pool init", db, "{}", e, rocket), - Err(Error::Custom(e)) => dberr!("pool manager", db, "{:?}", e, rocket), + Err(Error::Config(e)) => dberr!("config_error", "config", db, "{}", e, rocket), + Err(Error::Pool(e)) => dberr!("init_error", "pool init", db, "{}", e, rocket), + Err(Error::Custom(e)) => dberr!("init_error", "pool manager", db, "{:?}", e, rocket), } }) } @@ -782,7 +782,7 @@ impl ConnectionPool { let permit = match timeout(duration, self.semaphore.clone().acquire_owned()).await { Ok(p) => p, Err(_) => { - error_!("database connection retrieval timed out"); + error!("database connection retrieval timed out"); return Err(()); } }; @@ -795,7 +795,7 @@ impl ConnectionPool { _marker: PhantomData, }), Err(e) => { - error_!("failed to get a database connection: {}", e); + error!("failed to get a database connection: {}", e); Err(()) } } @@ -858,7 +858,7 @@ impl<'a, 'r, K: 'static, C: Poolable> FromRequest<'a, 'r> for Connection { match request.managed_state::>() { Some(c) => c.get().await.into_outcome(Status::ServiceUnavailable), None => { - error_!("Missing database fairing for `{}`", std::any::type_name::()); + error!("Missing database fairing for `{}`", std::any::type_name::()); Outcome::Failure((Status::InternalServerError, ())) } } diff --git a/contrib/lib/src/helmet/helmet.rs b/contrib/lib/src/helmet/helmet.rs index 91d036ae69..5dee537a9f 100644 --- a/contrib/lib/src/helmet/helmet.rs +++ b/contrib/lib/src/helmet/helmet.rs @@ -171,8 +171,11 @@ impl SpaceHelmet { for policy in self.policies.values() { let name = policy.name(); if response.headers().contains(name.as_str()) { - warn!("Space Helmet: response contains a '{}' header.", name); - warn_!("Refusing to overwrite existing header."); + let span = warn_span!( + "Space Helmet: response contains already contains this header", + header = %name, + ); + warn!(parent: &span, "Refusing to overwrite existing header."); continue } @@ -206,9 +209,9 @@ impl Fairing for SpaceHelmet { && rocket.figment().profile() != rocket::Config::DEBUG_PROFILE && !self.is_enabled::() { - warn_!("Space Helmet: deploying with TLS without enabling HSTS."); - warn_!("Enabling default HSTS policy."); - info_!("To disable this warning, configure an HSTS policy."); + let span = warn_span!("Space Helmet: deploying with TLS without enabling HSTS."); + warn!(parent: &span, "Enabling default HSTS policy."); + info!(parent: &span, "To disable this warning, configure an HSTS policy."); self.force_hsts.store(true, Ordering::Relaxed); } } diff --git a/contrib/lib/src/json.rs b/contrib/lib/src/json.rs index 2ac5867cb7..3d2b15e566 100644 --- a/contrib/lib/src/json.rs +++ b/contrib/lib/src/json.rs @@ -147,7 +147,7 @@ impl<'a, T: Deserialize<'a>> FromTransformedData<'a> for Json { match serde_json::from_str(&string) { Ok(v) => Success(Json(v)), Err(e) => { - error_!("Couldn't parse JSON body: {:?}", e); + error!("Couldn't parse JSON body: {:?}", e); if e.is_data() { Failure((Status::UnprocessableEntity, JsonError::Parse(string, e))) } else { @@ -166,7 +166,7 @@ impl<'r, T: Serialize> Responder<'r, 'static> for Json { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { let string = serde_json::to_string(&self.0) .map_err(|e| { - error_!("JSON failed to serialize: {:?}", e); + error!("JSON failed to serialize: {:?}", e); Status::InternalServerError })?; diff --git a/contrib/lib/src/lib.rs b/contrib/lib/src/lib.rs index 8c157418d5..78a5bc623e 100644 --- a/contrib/lib/src/lib.rs +++ b/contrib/lib/src/lib.rs @@ -40,7 +40,7 @@ //! This crate is expected to grow with time, bringing in outside crates to be //! officially supported by Rocket. -#[allow(unused_imports)] #[macro_use] extern crate log; +#[allow(unused_imports)] #[macro_use] extern crate tracing; #[allow(unused_imports)] #[macro_use] extern crate rocket; #[cfg(feature="json")] #[macro_use] pub mod json; diff --git a/contrib/lib/src/msgpack.rs b/contrib/lib/src/msgpack.rs index 0f476d4d4f..565ee2702c 100644 --- a/contrib/lib/src/msgpack.rs +++ b/contrib/lib/src/msgpack.rs @@ -138,7 +138,7 @@ impl<'a, T: Deserialize<'a>> FromTransformedData<'a> for MsgPack { match rmp_serde::from_slice(&buf) { Ok(val) => Success(MsgPack(val)), Err(e) => { - error_!("Couldn't parse MessagePack body: {:?}", e); + error!("Couldn't parse MessagePack body: {:?}", e); match e { TypeMismatch(_) | OutOfRange | LengthMismatch(_) => { Failure((Status::UnprocessableEntity, e)) @@ -158,7 +158,7 @@ impl<'r, T: Serialize> Responder<'r, 'static> for MsgPack { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { let buf = rmp_serde::to_vec(&self.0) .map_err(|e| { - error_!("MsgPack failed to serialize: {:?}", e); + error!("MsgPack failed to serialize: {:?}", e); Status::InternalServerError })?; diff --git a/contrib/lib/src/serve.rs b/contrib/lib/src/serve.rs index d6f288b10f..4d62aa2586 100644 --- a/contrib/lib/src/serve.rs +++ b/contrib/lib/src/serve.rs @@ -288,8 +288,8 @@ impl StaticFiles { let path = path.as_ref(); if !path.is_dir() { - error!("`StaticFiles` supplied with invalid path"); - info_!("'{}' is not a directory", Paint::white(path.display())); + let span = error_span!("`StaticFiles` supplied with invalid path"); + info!(parent: &span, "'{}' is not a directory", Paint::white(path.display())); panic!("refusing to continue due to invalid static files path"); } diff --git a/contrib/lib/src/templates/context.rs b/contrib/lib/src/templates/context.rs index 08cd06d03a..0f815026ba 100644 --- a/contrib/lib/src/templates/context.rs +++ b/contrib/lib/src/templates/context.rs @@ -28,10 +28,10 @@ impl Context { for path in glob::glob(glob_path).unwrap().filter_map(Result::ok) { let (name, data_type_str) = split_path(&root, &path); if let Some(info) = templates.get(&*name) { - warn_!("Template name '{}' does not have a unique path.", name); - info_!("Existing path: {:?}", info.path); - info_!("Additional path: {:?}", path); - warn_!("Using existing path for template '{}'.", name); + let span = warn_span!("invalid_template_path", "Template name '{}' does not have a unique path.", name); + info!(parent: &span, "Existing path: {:?}", info.path); + info!(parent: &span, "Additional path: {:?}", path); + warn!(parent: &span, "Using existing path for template '{}'.", name); continue; } diff --git a/contrib/lib/src/templates/fairing.rs b/contrib/lib/src/templates/fairing.rs index 6d5bbaacae..cd960b9b62 100644 --- a/contrib/lib/src/templates/fairing.rs +++ b/contrib/lib/src/templates/fairing.rs @@ -58,10 +58,10 @@ mod context { let watcher = match watcher { Ok(watcher) => Some(Mutex::new((watcher, rx))), - Err(e) => { - warn!("Failed to enable live template reloading: {}", e); - debug_!("Reload error: {:?}", e); - warn_!("Live template reloading is unavailable."); + Err(error) => { + let span = warn_span!("Failed to enable live template reloading", %error); + debug!(parent: &span, reload_error = ?error); + warn!(parent: &span, "Live template reloading is unavailable."); None } }; @@ -97,14 +97,16 @@ mod context { } if changed { - info_!("Change detected: reloading templates."); + let span = info_span!("Change detected: reloading templates."); + let _entered = span.enter(); let mut ctxt = self.context_mut(); if let Some(mut new_ctxt) = Context::initialize(ctxt.root.clone()) { custom_callback(&mut new_ctxt.engines); *ctxt = new_ctxt; + info!("reloaded!"); } else { - warn_!("An error occurred while reloading templates."); - warn_!("The previous templates will remain active."); + warn!("An error occurred while reloading templates."); + warn!("The previous templates will remain active."); }; } }); @@ -157,12 +159,12 @@ impl Fairing for TemplateFairing { let root = Source::from(&*path); match Context::initialize(path) { Some(mut ctxt) => { - use rocket::{logger::PaintExt, yansi::Paint}; + use rocket::{trace::PaintExt, yansi::Paint}; use crate::templates::Engines; - info!("{}{}", Paint::emoji("📐 "), Paint::magenta("Templating:")); - info_!("directory: {}", Paint::white(root)); - info_!("engines: {:?}", Paint::white(Engines::ENABLED_EXTENSIONS)); + let span = info_span!("templating", "{}{}", Paint::emoji("📐 "), Paint::magenta("Templating:")); + info!(parent: &span, "directory: {}", Paint::white(&root)); + info!(parent: &span, "engines: {:?}", Paint::white(Engines::ENABLED_EXTENSIONS)); (self.custom_callback)(&mut ctxt.engines); Ok(rocket.manage(ContextManager::new(ctxt))) diff --git a/contrib/lib/src/templates/handlebars_templates.rs b/contrib/lib/src/templates/handlebars_templates.rs index 9d667c7605..40c85ad766 100644 --- a/contrib/lib/src/templates/handlebars_templates.rs +++ b/contrib/lib/src/templates/handlebars_templates.rs @@ -12,9 +12,9 @@ impl Engine for Handlebars<'static> { for &(name, info) in templates { let path = &info.path; if let Err(e) = hb.register_template_file(name, path) { - error!("Error in Handlebars template '{}'.", name); - info_!("{}", e); - info_!("Template path: '{}'.", path.to_string_lossy()); + let span = error_span!("Error in Handlebars template", template = %name); + info!(parent: &span, template.error = %e); + info!(parent: &span, template.path = %path.to_string_lossy()); return None; } } @@ -24,14 +24,14 @@ impl Engine for Handlebars<'static> { fn render(&self, name: &str, context: C) -> Option { if self.get_template(name).is_none() { - error_!("Handlebars template '{}' does not exist.", name); + error!(template = %name, "Handlebars template does not exist."); return None; } match Handlebars::render(self, name, &context) { Ok(string) => Some(string), - Err(e) => { - error_!("Error rendering Handlebars template '{}': {}", name, e); + Err(error) => { + error!(template = %name, %error, "Error rendering Handlebars template"); None } } diff --git a/contrib/lib/src/templates/metadata.rs b/contrib/lib/src/templates/metadata.rs index 3c095aefb5..7660fce435 100644 --- a/contrib/lib/src/templates/metadata.rs +++ b/contrib/lib/src/templates/metadata.rs @@ -93,9 +93,9 @@ impl<'a, 'r> FromRequest<'a, 'r> for Metadata<'a> { .succeeded() .and_then(|cm| Some(request::Outcome::Success(Metadata(cm.inner())))) .unwrap_or_else(|| { - error_!("Uninitialized template context: missing fairing."); - info_!("To use templates, you must attach `Template::fairing()`."); - info_!("See the `Template` documentation for more information."); + let span = error_span!("Uninitialized template context: missing fairing."); + info!(parent: &span, "To use templates, you must attach `Template::fairing()`."); + info!(parent: &span, "See the `Template` documentation for more information."); request::Outcome::Failure((Status::InternalServerError, ())) }) } diff --git a/contrib/lib/src/templates/mod.rs b/contrib/lib/src/templates/mod.rs index 5459644d32..6fc9f115b5 100644 --- a/contrib/lib/src/templates/mod.rs +++ b/contrib/lib/src/templates/mod.rs @@ -345,9 +345,9 @@ impl Template { where S: Into>, C: Serialize { let ctxt = rocket.state::().map(ContextManager::context).or_else(|| { - warn!("Uninitialized template context: missing fairing."); - info!("To use templates, you must attach `Template::fairing()`."); - info!("See the `Template` documentation for more information."); + let span = warn_span!("Uninitialized template context: missing fairing."); + info!(parent: &span, "To use templates, you must attach `Template::fairing()`."); + info!(parent: &span, "See the `Template` documentation for more information."); None })?; @@ -362,19 +362,19 @@ impl Template { let name = &*self.name; let info = ctxt.templates.get(name).ok_or_else(|| { let ts: Vec<_> = ctxt.templates.keys().map(|s| s.as_str()).collect(); - error_!("Template '{}' does not exist.", name); - info_!("Known templates: {}", ts.join(",")); - info_!("Searched in '{:?}'.", ctxt.root); + let span = error_span!("Template does not exist.", template = %name); + info!(parent: &span, "Known templates: {}", ts.join(",")); + info!(parent: &span, "Searched in '{:?}'.", ctxt.root); Status::InternalServerError })?; let value = self.value.ok_or_else(|| { - error_!("The provided template context failed to serialize."); + error!("The provided template context failed to serialize."); Status::InternalServerError })?; let string = ctxt.engines.render(name, &info, value).ok_or_else(|| { - error_!("Template '{}' failed to render.", name); + error!(template = %name, "Template failed to render."); Status::InternalServerError })?; @@ -389,9 +389,9 @@ impl<'r> Responder<'r, 'static> for Template { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { let (render, content_type) = { let ctxt = req.managed_state::().ok_or_else(|| { - error_!("Uninitialized template context: missing fairing."); - info_!("To use templates, you must attach `Template::fairing()`."); - info_!("See the `Template` documentation for more information."); + let span = error_span!("Uninitialized template context: missing fairing."); + info!(parent: &span, "To use templates, you must attach `Template::fairing()`."); + info!(parent: &span, "See the `Template` documentation for more information."); Status::InternalServerError })?.context(); diff --git a/contrib/lib/src/templates/tera_templates.rs b/contrib/lib/src/templates/tera_templates.rs index d841b41426..5b0eaec105 100644 --- a/contrib/lib/src/templates/tera_templates.rs +++ b/contrib/lib/src/templates/tera_templates.rs @@ -21,11 +21,12 @@ impl Engine for Tera { // Finally try to tell Tera about all of the templates. if let Err(e) = tera.add_template_files(tera_templates) { - error!("Failed to initialize Tera templating."); + let span = error_span!("Failed to initialize Tera templating."); + let _entered = span.enter(); let mut error = Some(&e as &dyn Error); while let Some(err) = error { - info_!("{}", err); + error!(error = %err); error = err.source(); } @@ -36,15 +37,17 @@ impl Engine for Tera { } fn render(&self, name: &str, context: C) -> Option { + let span = info_span!("Rendering Tera template", template = %name); + let _entered = span.enter(); if self.get_template(name).is_err() { - error_!("Tera template '{}' does not exist.", name); + error!("Tera template '{}' does not exist.", name); return None; }; let tera_ctx = match Context::from_serialize(context) { Ok(ctx) => ctx, Err(_) => { - error_!( + error!( "Error generating context when rendering Tera template '{}'.", name ); @@ -55,11 +58,11 @@ impl Engine for Tera { match Tera::render(self, name, &tera_ctx) { Ok(string) => Some(string), Err(e) => { - error_!("Error rendering Tera template '{}'.", name); - + let span = error_span!("Error rendering Tera template", template = %name); + let _entered = span.enter(); let mut error = Some(&e as &dyn Error); while let Some(err) = error { - error_!("{}", err); + error!(error = %err); error = err.source(); } diff --git a/core/codegen/src/attribute/catch.rs b/core/codegen/src/attribute/catch.rs index 158acac62c..9e5e6b1bb8 100644 --- a/core/codegen/src/attribute/catch.rs +++ b/core/codegen/src/attribute/catch.rs @@ -74,12 +74,13 @@ pub fn _catch( // Gather everything we'll need to generate the catcher. let user_catcher_fn = &catch.function; let user_catcher_fn_name = catch.function.sig.ident.clone(); + let generated_span_name = user_catcher_fn_name.to_string(); let (vis, catcher_status) = (&catch.function.vis, &catch.status); let status_code = Optional(catcher_status.as_ref().map(|s| s.0.code)); // Variables names we'll use and reuse. define_vars_and_mods!(catch.function.span().into() => - req, status, _Box, Request, Response, StaticCatcherInfo, Catcher, + req, status, log, _Box, Request, Response, StaticCatcherInfo, Catcher, ErrorHandlerFuture, Status); // Determine the number of parameters that will be passed in. @@ -129,13 +130,18 @@ pub fn _catch( #status: #Status, #req: &'_b #Request ) -> #ErrorHandlerFuture<'_b> { + use #log::Instrument as _; #_Box::pin(async move { let __response = #catcher_response; #Response::build() .status(#status) .merge(__response) .ok() - }) + }.instrument(#log::info_span!( + #generated_span_name, + status = %#status, + "Catcher: {}", #generated_span_name + ))) } #StaticCatcherInfo { diff --git a/core/codegen/src/attribute/route.rs b/core/codegen/src/attribute/route.rs index de73995fab..88460b89ad 100644 --- a/core/codegen/src/attribute/route.rs +++ b/core/codegen/src/attribute/route.rs @@ -128,19 +128,19 @@ fn param_expr(seg: &Segment, ident: &syn::Ident, ty: &syn::Type) -> TokenStream define_vars_and_mods!(req, data, error, log, request, _None, _Some, _Ok, _Err, Outcome); let i = seg.index.expect("dynamic parameters must be indexed"); let span = ident.span().join(ty.span()).unwrap_or_else(|| ty.span()); - let name = ident.to_string(); // All dynamic parameter should be found if this function is being called; // that's the point of statically checking the URI parameters. let internal_error = quote!({ - #log::error("Internal invariant error: expected dynamic parameter not found."); - #log::error("Please report this error to the Rocket issue tracker."); + #log::error!("Internal invariant error: expected dynamic parameter not found."); + #log::error!("Please report this error to the Rocket issue tracker."); #Outcome::Forward(#data) }); // Returned when a dynamic parameter fails to parse. + let field_name = syn::Ident::new(&seg.name, seg.span); let parse_error = quote!({ - #log::warn_(&format!("Failed to parse '{}': {:?}", #name, #error)); + #log::warn!(#field_name = ?#error, "Failed to parse dynamic parameter"); #Outcome::Forward(#data) }); @@ -231,22 +231,24 @@ fn query_exprs(route: &Route) -> Option { }, Kind::Static => quote!() }; - let matcher = match segment.kind { - Kind::Single => quote_spanned! { span => - (_, #name, __v) => { - #[allow(unreachable_patterns, unreachable_code)] - let __v = match <#ty as #request::FromFormValue>::from_form_value(__v) { - #_Ok(__v) => __v, - #_Err(__e) => { - #log::warn_(&format!("Failed to parse '{}': {:?}", #name, __e)); - return #Outcome::Forward(#data); - } - }; - - #ident = #_Some(__v); + Kind::Single => { + let field_name = syn::Ident::new(name, segment.span); + quote_spanned! { span => + (_, #name, __v) => { + #[allow(unreachable_patterns, unreachable_code)] + let __v = match <#ty as #request::FromFormValue>::from_form_value(__v) { + #_Ok(__v) => __v, + #_Err(__e) => { + #log::warn!(#field_name = ?__e, "Failed to parse"); + return #Outcome::Forward(#data); + } + }; + + #ident = #_Some(__v); + } } - }, + } Kind::Static => quote! { (#name, _, _) => continue, }, @@ -261,21 +263,24 @@ fn query_exprs(route: &Route) -> Option { let #ident = match #ident.or_else(<#ty as #request::FromFormValue>::default) { #_Some(__v) => __v, #_None => { - #log::warn_(&format!("Missing required query parameter '{}'.", #name)); - return #Outcome::Forward(#data); - } - }; - }, - Kind::Multi => quote_spanned! { span => - #[allow(non_snake_case)] - let #ident = match <#ty as #request::FromQuery>::from_query(#Query(&#trail)) { - #_Ok(__v) => __v, - #_Err(__e) => { - #log::warn_(&format!("Failed to parse '{}': {:?}", #name, __e)); + #log::warn!(parameter = %#name, "Missing required query parameter"); return #Outcome::Forward(#data); } }; }, + Kind::Multi => { + let field_name = syn::Ident::new(name, segment.span); + quote_spanned! { span => + #[allow(non_snake_case)] + let #ident = match <#ty as #request::FromQuery>::from_query(#Query(&#trail)) { + #_Ok(__v) => __v, + #_Err(__e) => { + #log::warn!(#field_name = ?__e, "Failed to parse"); + return #Outcome::Forward(#data); + } + }; + } + } Kind::Static => quote!() }; @@ -408,11 +413,12 @@ fn codegen_route(route: Route) -> Result { } // Gather everything we need. - define_vars_and_mods!(req, data, _Box, Request, Data, Route, StaticRouteInfo, HandlerFuture); + define_vars_and_mods!(req, log, data, _Box, Request, Data, Route, StaticRouteInfo, HandlerFuture); let (vis, user_handler_fn) = (&route.function.vis, &route.function); let user_handler_fn_name = &user_handler_fn.sig.ident; let generated_internal_uri_macro = generate_internal_uri_macro(&route); let generated_respond_expr = generate_respond_expr(&route); + let generated_span_name = user_handler_fn_name.to_string(); let method = route.attribute.method; let path = route.attribute.path.origin.0.to_string(); @@ -434,13 +440,19 @@ fn codegen_route(route: Route) -> Result { #req: &'_b #Request, #data: #Data ) -> #HandlerFuture<'_b> { + use #log::Instrument as _; #_Box::pin(async move { #(#req_guard_definitions)* #(#parameter_definitions)* #data_stmt #generated_respond_expr - }) + }.instrument(#log::info_span!( + #generated_span_name, + method = %#method, + path = #path, + "Route: {}", #generated_span_name + ))) } #StaticRouteInfo { diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs index 27ed82e85e..0d3201fcf8 100644 --- a/core/codegen/src/lib.rs +++ b/core/codegen/src/lib.rs @@ -82,7 +82,7 @@ vars_and_mods! { request => rocket::request, response => rocket::response, handler => rocket::handler, - log => rocket::logger, + log => rocket::trace, Outcome => rocket::outcome::Outcome, FromTransformedData => rocket::data::FromTransformedData, Transform => rocket::data::Transform, diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index b91df79424..aba5070a90 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -44,11 +44,22 @@ serde = { version = "1.0", features = ["derive"] } figment = { version = "0.9.2", features = ["toml", "env"] } rand = "0.7" either = "1" +tracing = "0.1.19" [dependencies.tokio] version = "0.2.9" features = ["fs", "io-std", "io-util", "rt-threaded", "sync", "signal", "macros"] +[dependencies.tracing-subscriber] +version = "0.2.9" +default-features = false +features = ["fmt", "env-filter", "smallvec"] + +[dependencies.tracing-futures] +version = "0.2" +default-features = false +features = ["std-future"] + [build-dependencies] yansi = "0.5" version_check = "0.9.1" diff --git a/core/lib/src/config/config.rs b/core/lib/src/config/config.rs index 849b68d5de..02dc723980 100644 --- a/core/lib/src/config/config.rs +++ b/core/lib/src/config/config.rs @@ -219,9 +219,9 @@ impl Config { let figment = Figment::from(&provider); for (key, replacement) in Self::DEPRECATED_KEYS { if figment.find_value(key).is_ok() { - warn!("found value for deprecated config key `{}`", Paint::white(key)); + let span = warn_span!("deprecated_key", "found value for deprecated config key {}", Paint::white(key)); if let Some(new_key) = replacement { - info_!("key has been by replaced by `{}`", Paint::white(new_key)); + info!(parent: &span, "key has been by replaced by {}", Paint::white(new_key)); } } } @@ -235,14 +235,14 @@ impl Config { #[cfg(all(feature = "secrets", not(test), not(rocket_unsafe_secret_key)))] { if config.secret_key.is_zero() { if figment.profile() != Self::DEBUG_PROFILE { - crate::logger::try_init(LogLevel::Debug, true, false); - error!("secrets enabled in `release` without `secret_key`"); - info_!("disable `secrets` feature or configure a `secret_key`"); + crate::trace::try_init(LogLevel::Debug, true); + let span = error_span!("secrets enabled in `release` without `secret_key`"); + info!(parent: &span, "disable `secrets` feature or configure a `secret_key`"); panic!("aborting due to configuration error(s)") } else { - warn!("secrets enabled in `debug` without `secret_key`"); - info_!("disable `secrets` feature or configure a `secret_key`"); - info_!("this becomes a hard error in `release`"); + let span = warn_span!("secrets enabled in `debug` without `secret_key`"); + info!(parent: &span, "disable `secrets` feature or configure a `secret_key`"); + info!(parent: &span, "this becomes a hard error in `release`"); } // in debug, generate a key for a bit more security @@ -273,27 +273,29 @@ impl Config { } pub(crate) fn pretty_print(&self, profile: &Profile) { - use crate::logger::PaintExt; + use crate::trace::PaintExt; - launch_info!("{}Configured for {}.", Paint::emoji("🔧 "), profile); - launch_info_!("address: {}", Paint::default(&self.address).bold()); - launch_info_!("port: {}", Paint::default(&self.port).bold()); - launch_info_!("workers: {}", Paint::default(self.workers).bold()); - launch_info_!("log level: {}", Paint::default(self.log_level).bold()); - launch_info_!("secret key: {:?}", Paint::default(&self.secret_key).bold()); - launch_info_!("limits: {}", Paint::default(&self.limits).bold()); - launch_info_!("cli colors: {}", Paint::default(&self.cli_colors).bold()); + let span = info_span!("configured", "{}Configured for {}", Paint::emoji("🔧 "), profile); + let _e = span.enter(); + + info!(address = %&self.address); + info!(port = %&self.port); + info!(workers = %self.workers); + info!(log_level = %self.log_level); + info!(secret_key = ?&self.secret_key); + info!(limits = %&self.limits); + info!(cli_colors = %&self.cli_colors); let ka = self.keep_alive; if ka > 0 { - launch_info_!("keep-alive: {}", Paint::default(format!("{}s", ka)).bold()); + info!(keep_alive = %Paint::default(format!("{}s", ka)).bold()); } else { - launch_info_!("keep-alive: {}", Paint::default("disabled").bold()); + info!(keep_alive = %Paint::default("disabled").bold()); } match self.tls_enabled() { - true => launch_info_!("tls: {}", Paint::default("enabled").bold()), - false => launch_info_!("tls: {}", Paint::default("disabled").bold()), + true => info!(tls = %Paint::default("enabled").bold()), + false => info!(tls = %Paint::default("disabled").bold()), } } } @@ -315,21 +317,21 @@ impl Provider for Config { #[doc(hidden)] pub fn pretty_print_error(error: figment::Error) { - crate::logger::try_init(LogLevel::Debug, true, false); + crate::trace::try_init(LogLevel::Debug, true); for e in error { - error!("{}", e.kind); + let span = error_span!("config_error", "{}", e.kind); if let (Some(ref profile), Some(ref md)) = (&e.profile, &e.metadata) { if !e.path.is_empty() { let key = md.interpolate(profile, &e.path); - info_!("for key {}", Paint::white(key)); + info!(parent: &span, key = %Paint::white(&key)); } } if let Some(ref md) = e.metadata { if let Some(ref source) = md.source { - info_!("in {} {}", Paint::white(source), md.name); + info!(parent: &span, "in {} {}", Paint::white(source), md.name); } } } diff --git a/core/lib/src/config/mod.rs b/core/lib/src/config/mod.rs index e134b05a19..a82ebb8640 100644 --- a/core/lib/src/config/mod.rs +++ b/core/lib/src/config/mod.rs @@ -105,7 +105,7 @@ mod tls; #[doc(hidden)] pub use config::pretty_print_error; pub use config::Config; -pub use crate::logger::LogLevel; +pub use crate::trace::LogLevel; pub use secret_key::SecretKey; pub use tls::TlsConfig; @@ -115,7 +115,7 @@ mod tests { use figment::Figment; use crate::config::{Config, TlsConfig}; - use crate::logger::LogLevel; + use crate::trace::LogLevel; use crate::data::{Limits, ToByteUnit}; #[test] diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index de83b2da46..4434051797 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -165,7 +165,7 @@ impl Data { Ok(0) => { self.is_complete = true; break }, Ok(n) => len += n, Err(e) => { - error_!("Failed to read into peek buffer: {:?}.", e); + error!("Failed to read into peek buffer: {:?}.", e); break; } } @@ -193,4 +193,4 @@ impl Data { pub fn peek_complete(&self) -> bool { self.is_complete } -} +} \ No newline at end of file diff --git a/core/lib/src/data/data_stream.rs b/core/lib/src/data/data_stream.rs index b1e6b776a8..300434c773 100644 --- a/core/lib/src/data/data_stream.rs +++ b/core/lib/src/data/data_stream.rs @@ -118,15 +118,18 @@ impl AsyncRead for DataStream { cx: &mut Context<'_>, buf: &mut [u8] ) -> Poll> { + let span = trace_span!("DataStream::poll_read()"); + let _e = span.enter(); + if self.buffer.limit() > 0 { - trace_!("DataStream::buffer_read()"); + trace!("DataStream::buffer_read()"); match Pin::new(&mut self.buffer).poll_read(cx, buf) { Poll::Ready(Ok(0)) => { /* fall through */ }, poll => return poll, } } - trace_!("DataStream::stream_read()"); + trace!("DataStream::stream_read()"); Pin::new(&mut self.stream).poll_read(cx, buf) } } diff --git a/core/lib/src/error.rs b/core/lib/src/error.rs index 66e60df1c8..68d9ee468c 100644 --- a/core/lib/src/error.rs +++ b/core/lib/src/error.rs @@ -168,35 +168,36 @@ impl Drop for Error { match *self.kind() { ErrorKind::Bind(ref e) => { - error!("Rocket failed to bind network socket to given address/port."); - info_!("{}", e); + let span = error_span!("bind_error", "Rocket failed to bind network socket to given address/port."); + info!(parent: &span, "{}", e); panic!("aborting due to binding o error"); } ErrorKind::Io(ref e) => { - error!("Rocket failed to launch due to an I/O error."); - info_!("{}", e); + let span = error_span!("io_error", "Rocket failed to launch due to an I/O error."); + info!(parent: &span, "{}", e); panic!("aborting due to i/o error"); } ErrorKind::Collision(ref collisions) => { - error!("Rocket failed to launch due to the following routing collisions:"); + let span = error_span!("collisions", "Rocket failed to launch due to the following routing collisions:"); + let _e = span.enter(); for &(ref a, ref b) in collisions { - info_!("{} {} {}", a, Paint::red("collides with").italic(), b) + info!("{} {} {}", a, Paint::red("collides with").italic(), b) } - info_!("Note: Collisions can usually be resolved by ranking routes."); + info!(parent: &span, "Note: Collisions can usually be resolved by ranking routes."); panic!("route collisions detected"); } ErrorKind::FailedFairings(ref failures) => { - error!("Rocket failed to launch due to failing fairings:"); + let span = error_span!("fairing_error", "Rocket failed to launch due to failing fairings:"); for fairing in failures { - info_!("{}", fairing); + info!(parent: &span, "{}", fairing); } panic!("aborting due to launch fairing failure"); } ErrorKind::Runtime(ref err) => { - error!("An error occured in the runtime:"); - info_!("{}", err); + let span = error_span!("runtime_error", "An error occured in the runtime:"); + info!(parent: &span, "{}", err); panic!("aborting due to runtime failure"); } } diff --git a/core/lib/src/fairing/fairings.rs b/core/lib/src/fairing/fairings.rs index 84f7f12970..64e7ba5c71 100644 --- a/core/lib/src/fairing/fairings.rs +++ b/core/lib/src/fairing/fairings.rs @@ -1,6 +1,6 @@ use crate::{Rocket, Request, Response, Data}; use crate::fairing::{Fairing, Kind}; -use crate::logger::PaintExt; +use crate::trace::PaintExt; use yansi::Paint; @@ -88,7 +88,7 @@ impl Fairings { .collect::>() .join(", "); - info_!("{} {}: {}", Paint::default(num).bold(), kind, Paint::default(names).bold()); + info!("{} {}: {}", Paint::default(num).bold(), kind, Paint::default(&names).bold()); } } diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index 6992b5e4c6..f1f77b5b36 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -99,7 +99,7 @@ pub use rocket_codegen::*; pub use async_trait::*; -#[macro_use] extern crate log; +#[macro_use] extern crate tracing; /// These are public dependencies! Update docs if these are changed, especially /// figment's version number in docs. @@ -109,7 +109,6 @@ pub use futures; pub use tokio; pub use figment; -#[doc(hidden)] #[macro_use] pub mod logger; #[macro_use] pub mod outcome; pub mod local; pub mod request; @@ -120,6 +119,7 @@ pub mod handler; pub mod fairing; pub mod error; pub mod catcher; +pub mod trace; // Reexport of HTTP everything. pub mod http { @@ -139,7 +139,6 @@ mod server; mod codegen; mod ext; -#[doc(hidden)] pub use log::{info, warn, error, debug}; #[doc(inline)] pub use crate::response::Response; #[doc(hidden)] pub use crate::codegen::{StaticRouteInfo, StaticCatcherInfo}; #[doc(inline)] pub use crate::data::Data; diff --git a/core/lib/src/logger.rs b/core/lib/src/logger.rs deleted file mode 100644 index 3e825d576d..0000000000 --- a/core/lib/src/logger.rs +++ /dev/null @@ -1,218 +0,0 @@ -//! Rocket's logging infrastructure. - -use std::fmt; -use std::str::FromStr; - -use log; -use yansi::Paint; -use serde::{de, Serialize, Serializer, Deserialize, Deserializer}; - -#[derive(Debug)] -struct RocketLogger(LogLevel); - -/// Defines the maximum level of log messages to show. -#[derive(PartialEq, Eq, Debug, Clone, Copy)] -pub enum LogLevel { - /// Only shows errors and warnings: `"critical"`. - Critical, - /// Shows everything except debug and trace information: `"normal"`. - Normal, - /// Shows everything: `"debug"`. - Debug, - /// Shows nothing: "`"off"`". - Off, -} - -impl LogLevel { - fn as_str(&self) -> &str { - match self { - LogLevel::Critical => "critical", - LogLevel::Normal => "normal", - LogLevel::Debug => "debug", - LogLevel::Off => "off", - } - } - - #[inline(always)] - fn to_level_filter(self) -> log::LevelFilter { - match self { - LogLevel::Critical => log::LevelFilter::Warn, - LogLevel::Normal => log::LevelFilter::Info, - LogLevel::Debug => log::LevelFilter::Trace, - LogLevel::Off => log::LevelFilter::Off - } - } -} - -impl FromStr for LogLevel { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - let level = match &*s.to_ascii_lowercase() { - "critical" => LogLevel::Critical, - "normal" => LogLevel::Normal, - "debug" => LogLevel::Debug, - "off" => LogLevel::Off, - _ => return Err("a log level (off, debug, normal, critical)") - }; - - Ok(level) - } -} - -impl fmt::Display for LogLevel { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.as_str()) - } -} - -impl Serialize for LogLevel { - fn serialize(&self, ser: S) -> Result { - ser.serialize_str(self.as_str()) - } -} - -impl<'de> Deserialize<'de> for LogLevel { - fn deserialize>(de: D) -> Result { - let string = String::deserialize(de)?; - LogLevel::from_str(&string).map_err(|_| de::Error::invalid_value( - de::Unexpected::Str(&string), - &figment::error::OneOf( &["critical", "normal", "debug", "off"]) - )) - } -} - -#[doc(hidden)] #[macro_export] -macro_rules! log_ { ($name:ident: $($args:tt)*) => { $name!(target: "_", $($args)*) }; } -#[doc(hidden)] #[macro_export] -macro_rules! launch_info { ($($args:tt)*) => { info!(target: "launch", $($args)*) } } -#[doc(hidden)] #[macro_export] -macro_rules! launch_info_ { ($($args:tt)*) => { info!(target: "launch_", $($args)*) } } -#[doc(hidden)] #[macro_export] -macro_rules! error_ { ($($args:expr),+) => { log_!(error: $($args),+); }; } -#[doc(hidden)] #[macro_export] -macro_rules! info_ { ($($args:expr),+) => { log_!(info: $($args),+); }; } -#[doc(hidden)] #[macro_export] -macro_rules! trace_ { ($($args:expr),+) => { log_!(trace: $($args),+); }; } -#[doc(hidden)] #[macro_export] -macro_rules! debug_ { ($($args:expr),+) => { log_!(debug: $($args),+); }; } -#[doc(hidden)] #[macro_export] -macro_rules! warn_ { ($($args:expr),+) => { log_!(warn: $($args),+); }; } - -impl log::Log for RocketLogger { - #[inline(always)] - fn enabled(&self, record: &log::Metadata<'_>) -> bool { - match self.0.to_level_filter().to_level() { - Some(max) => record.level() <= max || record.target().starts_with("launch"), - None => false - } - } - - fn log(&self, record: &log::Record<'_>) { - // Print nothing if this level isn't enabled and this isn't launch info. - if !self.enabled(record.metadata()) { - return; - } - - // Don't print Hyper or Rustls messages unless debug is enabled. - let configged_level = self.0; - let from_hyper = record.module_path().map_or(false, |m| m.starts_with("hyper::")); - let from_rustls = record.module_path().map_or(false, |m| m.starts_with("rustls::")); - if configged_level != LogLevel::Debug && (from_hyper || from_rustls) { - return; - } - - // In Rocket, we abuse targets with suffix "_" to indicate indentation. - let is_launch = record.target().starts_with("launch"); - if record.target().ends_with('_') { - if configged_level != LogLevel::Critical || is_launch { - print!(" {} ", Paint::default("=>").bold()); - } - } - - match record.level() { - log::Level::Info => println!("{}", Paint::blue(record.args()).wrap()), - log::Level::Trace => println!("{}", Paint::magenta(record.args()).wrap()), - log::Level::Error => { - println!("{} {}", - Paint::red("Error:").bold(), - Paint::red(record.args()).wrap()) - } - log::Level::Warn => { - println!("{} {}", - Paint::yellow("Warning:").bold(), - Paint::yellow(record.args()).wrap()) - } - log::Level::Debug => { - print!("\n{} ", Paint::blue("-->").bold()); - if let Some(file) = record.file() { - print!("{}", Paint::blue(file)); - } - - if let Some(line) = record.line() { - println!(":{}", Paint::blue(line)); - } - - println!("{}", record.args()); - } - } - } - - fn flush(&self) { - // NOOP: We don't buffer any records. - } -} - -pub(crate) fn try_init(level: LogLevel, colors: bool, verbose: bool) -> bool { - if level == LogLevel::Off { - return false; - } - - if !atty::is(atty::Stream::Stdout) - || (cfg!(windows) && !Paint::enable_windows_ascii()) - || !colors - { - Paint::disable(); - } - - if let Err(e) = log::set_boxed_logger(Box::new(RocketLogger(level))) { - if verbose { - eprintln!("Logger failed to initialize: {}", e); - } - - return false; - } - - log::set_max_level(level.to_level_filter()); - true -} - -pub trait PaintExt { - fn emoji(item: &str) -> Paint<&str>; -} - -impl PaintExt for Paint<&str> { - /// Paint::masked(), but hidden on Windows due to broken output. See #1122. - fn emoji(_item: &str) -> Paint<&str> { - #[cfg(windows)] { Paint::masked("") } - #[cfg(not(windows))] { Paint::masked(_item) } - } -} - -#[doc(hidden)] -pub fn init(level: LogLevel) -> bool { - try_init(level, true, true) -} - -// Expose logging macros as (hidden) funcions for use by core/contrib codegen. -macro_rules! external_log_function { - ($fn_name:ident: $macro_name:ident) => ( - #[doc(hidden)] #[inline(always)] - pub fn $fn_name(msg: &str) { $macro_name!("{}", msg); } - ) -} - -external_log_function!(error: error); -external_log_function!(error_: error_); -external_log_function!(warn: warn); -external_log_function!(warn_: warn_); diff --git a/core/lib/src/request/form/form.rs b/core/lib/src/request/form/form.rs index 5b91e6489c..2f8df5ef5e 100644 --- a/core/lib/src/request/form/form.rs +++ b/core/lib/src/request/form/form.rs @@ -162,14 +162,14 @@ impl<'f, T: FromForm<'f>> Form { let mut items = FormItems::from(form_str); let result = T::from_form(&mut items, strict); if !items.exhaust() { - error_!("The request's form string was malformed."); + error!("The request's form string was malformed."); return Failure((Status::BadRequest, Malformed(form_str))); } match result { Ok(v) => Success(v), Err(e) => { - error_!("The incoming form failed to parse."); + error!("The incoming form failed to parse."); Failure((Status::UnprocessableEntity, Parse(e, form_str))) } } @@ -200,7 +200,7 @@ impl<'r, T: FromForm<'r> + Send + 'r> FromTransformedData<'r> for Form { ) -> TransformFuture<'r, Self::Owned, Self::Error> { Box::pin(async move { if !request.content_type().map_or(false, |ct| ct.is_form()) { - warn_!("Form data does not have form content type."); + warn!("Form data does not have form content type."); return Transform::Borrowed(Forward(data)); } diff --git a/core/lib/src/request/request.rs b/core/lib/src/request/request.rs index 6b1e4da12c..903feca758 100644 --- a/core/lib/src/request/request.rs +++ b/core/lib/src/request/request.rs @@ -252,7 +252,7 @@ impl<'r> Request<'r> { .get_one("X-Real-IP") .and_then(|ip| { ip.parse() - .map_err(|_| warn_!("'X-Real-IP' header is malformed: {}", ip)) + .map_err(|_| warn!("'X-Real-IP' header is malformed: {}", ip)) .ok() }) } diff --git a/core/lib/src/request/state.rs b/core/lib/src/request/state.rs index 811b46edbf..27a817d8e8 100644 --- a/core/lib/src/request/state.rs +++ b/core/lib/src/request/state.rs @@ -168,7 +168,7 @@ impl<'a, 'r, T: Send + Sync + 'static> FromRequest<'a, 'r> for State<'r, T> { match req.state.managed.try_get::() { Some(state) => Outcome::Success(State(state)), None => { - error_!("Attempted to retrieve unmanaged state!"); + error!("Attempted to retrieve unmanaged state!"); Outcome::Failure((Status::InternalServerError, ())) } } diff --git a/core/lib/src/response/debug.rs b/core/lib/src/response/debug.rs index 8ff46e3671..7933d72aa9 100644 --- a/core/lib/src/response/debug.rs +++ b/core/lib/src/response/debug.rs @@ -1,6 +1,6 @@ -use crate::request::Request; -use crate::response::{self, Response, Responder}; use crate::http::Status; +use crate::request::Request; +use crate::response::{self, Responder, Response}; use yansi::Paint; @@ -60,8 +60,11 @@ impl From for Debug { impl<'r, E: std::fmt::Debug> Responder<'r, 'static> for Debug { fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> { - warn_!("Debug: {:?}", Paint::default(self.0)); - warn_!("Debug always responds with {}.", Status::InternalServerError); + warn!("Debug: {:?}", Paint::default(&self.0)); + warn!( + "Debug always responds with {}.", + Status::InternalServerError + ); Response::build().status(Status::InternalServerError).ok() } } diff --git a/core/lib/src/response/flash.rs b/core/lib/src/response/flash.rs index 198b599424..3366cb74e6 100644 --- a/core/lib/src/response/flash.rs +++ b/core/lib/src/response/flash.rs @@ -193,7 +193,7 @@ impl Flash { /// the response is the `Outcome` of the wrapped `Responder`. impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Flash { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'o> { - trace_!("Flash: setting message: {}:{}", self.name, self.message); + trace!("Flash: setting message: {}:{}", self.name, self.message); req.cookies().add(self.cookie()); self.inner.respond_to(req) } @@ -242,9 +242,9 @@ impl<'a, 'r> FromRequest<'a, 'r> for Flash<&'a Request<'r>> { type Error = (); async fn from_request(req: &'a Request<'r>) -> request::Outcome { - trace_!("Flash: attempting to retrieve message."); + trace!("Flash: attempting to retrieve message."); req.cookies().get(FLASH_COOKIE_NAME).ok_or(()).and_then(|cookie| { - trace_!("Flash: retrieving message: {:?}", cookie); + trace!("Flash: retrieving message: {:?}", cookie); // Parse the flash message. let content = cookie.value(); diff --git a/core/lib/src/response/responder.rs b/core/lib/src/response/responder.rs index 90cf84dcae..a8cf71588d 100644 --- a/core/lib/src/response/responder.rs +++ b/core/lib/src/response/responder.rs @@ -277,7 +277,7 @@ impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for Option { match self { Some(r) => r.respond_to(req), None => { - warn_!("Response was `None`."); + warn!("Response was `None`."); Err(Status::NotFound) }, } @@ -322,7 +322,7 @@ impl<'r> Responder<'r, 'static> for Status { Response::build().status(self).ok() } _ => { - error_!("Invalid status used as responder: {}.", self); + error!("Invalid status used as responder: {}.", self); Err(Status::InternalServerError) } } diff --git a/core/lib/src/response/response.rs b/core/lib/src/response/response.rs index 0f46253ffd..b48e074846 100644 --- a/core/lib/src/response/response.rs +++ b/core/lib/src/response/response.rs @@ -106,7 +106,7 @@ impl Body pub async fn into_bytes(mut self) -> Option> { let mut vec = Vec::new(); if let Err(e) = self.as_reader().read_to_end(&mut vec).await { - error_!("Error reading body: {:?}", e); + error!("Error reading body: {:?}", e); return None; } @@ -119,7 +119,7 @@ impl Body self.into_bytes().await.and_then(|bytes| match String::from_utf8(bytes) { Ok(string) => Some(string), Err(e) => { - error_!("Body is invalid UTF-8: {}", e); + error!("Body is invalid UTF-8: {}", e); None } }) diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index ad4014839f..1b55d6049c 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -6,12 +6,12 @@ use figment::Figment; use tokio::sync::mpsc; use futures::future::FutureExt; -use crate::logger; +use crate::trace; use crate::config::Config; use crate::catcher::Catcher; use crate::router::{Router, Route}; use crate::fairing::{Fairing, Fairings}; -use crate::logger::PaintExt; +use crate::trace::PaintExt; use crate::shutdown::Shutdown; use crate::http::uri::Origin; use crate::error::{Error, ErrorKind}; @@ -82,7 +82,7 @@ impl Rocket { #[inline] pub fn custom(provider: T) -> Rocket { let (config, figment) = (Config::from(&provider), Figment::from(provider)); - logger::try_init(config.log_level, config.cli_colors, false); + let _ = trace::try_init(config.log_level, config.cli_colors); config.pretty_print(figment.profile()); let managed_state = Container::new(); @@ -163,22 +163,19 @@ impl Rocket { panic!("Invalid mount point."); } - info!("{}{} {}{}", - Paint::emoji("🛰 "), - Paint::magenta("Mounting"), - Paint::blue(&base_uri), - Paint::magenta(":")); + let span = info_span!("mounting", at = %Paint::blue(&base), "{} Mounting", Paint::emoji("🛰 ")); + let _e = span.enter(); for route in routes.into() { let old_route = route.clone(); let route = route.map_base(|old| format!("{}{}", base, old)) .unwrap_or_else(|e| { - error_!("Route `{}` has a malformed URI.", old_route); - error_!("{}", e); + let span = error_span!("malformed_uri", "Route `{}` has a malformed URI.", old_route); + error!(parent: &span, "{}", e); panic!("Invalid route URI."); }); - info_!("{}", route); + info!(%route); self.router.add(route); } @@ -210,10 +207,11 @@ impl Rocket { /// ``` #[inline] pub fn register(mut self, catchers: Vec) -> Self { - info!("{}{}", Paint::emoji("👾 "), Paint::magenta("Catchers:")); + let span = info_span!("catchers", "{}{}", Paint::emoji("👾 "), Paint::magenta("Catchers:")); + let _e = span.enter(); for catcher in catchers { - info_!("{}", catcher); + info!("{}", catcher); let existing = match catcher.code { Some(code) => self.catchers.insert(code, catcher), @@ -221,7 +219,7 @@ impl Rocket { }; if let Some(existing) = existing { - warn_!("Replacing existing '{}' catcher.", existing); + warn!("Replacing existing '{}' catcher.", existing); } } @@ -555,7 +553,7 @@ impl Rocket { Either::Left((Err(err), server)) => { // Error setting up ctrl-c signal. Let the user know. warn!("Failed to enable `ctrl-c` graceful signal shutdown."); - info_!("Error: {}", err); + info!("Error: {}", err); server.await } // Server shut down before Ctrl-C; return the result. diff --git a/core/lib/src/router/mod.rs b/core/lib/src/router/mod.rs index 8538394395..78d3ff5ee7 100644 --- a/core/lib/src/router/mod.rs +++ b/core/lib/src/router/mod.rs @@ -39,8 +39,8 @@ impl Router { .collect() }); - trace_!("Routing the request: {}", req); - trace_!("All matches: {:?}", matches); + trace!("Routing the request: {}", req); + trace!("All matches: {:?}", matches); matches } diff --git a/core/lib/src/server.rs b/core/lib/src/server.rs index b4ed0687e1..c3c691796b 100644 --- a/core/lib/src/server.rs +++ b/core/lib/src/server.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use futures::stream::StreamExt; use futures::future::{Future, BoxFuture}; use tokio::sync::oneshot; +use tracing_futures::Instrument; use yansi::Paint; use crate::Rocket; @@ -13,7 +14,7 @@ use crate::data::Data; use crate::response::{Body, Response}; use crate::outcome::Outcome; use crate::error::{Error, ErrorKind}; -use crate::logger::PaintExt; +use crate::trace::{self, PaintExt}; use crate::ext::AsyncReadExt; use crate::http::{Method, Status, Header, hyper}; @@ -67,7 +68,7 @@ async fn hyper_service_fn( let token = rocket.preprocess_request(&mut req, &mut data).await; let r = rocket.dispatch(token, &mut req, data).await; rocket.send_response(r, tx).await; - }); + }.instrument(info_span!("connection", from = %h_addr, "{}Connection", Paint::emoji("📡 ")))); // Receive the response written to `tx` by the task above. rx.await.map_err(|e| io::Error::new(io::ErrorKind::Other, e)) @@ -82,8 +83,8 @@ impl Rocket { tx: oneshot::Sender>, ) { match self.make_response(response, tx).await { - Ok(()) => info_!("{}", Paint::green("Response succeeded.")), - Err(e) => error_!("Failed to write response: {:?}.", e), + Ok(()) => info!("{}", Paint::green("Response succeeded.")), + Err(e) => error!("Failed to write response: {:?}.", e), } } @@ -185,29 +186,30 @@ impl Rocket { request: &'r Request<'s>, data: Data ) -> Response<'r> { - info!("{}:", request); - - // Remember if the request is `HEAD` for later body stripping. - let was_head_request = request.method() == Method::Head; + async move { + // Remember if the request is `HEAD` for later body stripping. + let was_head_request = request.method() == Method::Head; - // Route the request and run the user's handlers. - let mut response = self.route_and_process(request, data).await; + // Route the request and run the user's handlers. + let mut response = self.route_and_process(request, data).await; - // Add a default 'Server' header if it isn't already there. - // TODO: If removing Hyper, write out `Date` header too. - if !response.headers().contains("Server") { - response.set_header(Header::new("Server", "Rocket")); - } + // Add a default 'Server' header if it isn't already there. + // TODO: If removing Hyper, write out `Date` header too. + if !response.headers().contains("Server") { + response.set_header(Header::new("Server", "Rocket")); + } - // Run the response fairings. - self.fairings.handle_response(request, &mut response).await; + // Run the response fairings. + self.fairings.handle_response(request, &mut response).await; - // Strip the body if this is a `HEAD` request. - if was_head_request { - response.strip_body(); - } + // Strip the body if this is a `HEAD` request. + if was_head_request { + response.strip_body(); + } - response + response + }.instrument(info_span!("request", "{}", request)) + .await } /// Route the request and process the outcome to eventually get a response. @@ -222,7 +224,7 @@ impl Rocket { Outcome::Forward(data) => { // There was no matching route. Autohandle `HEAD` requests. if request.method() == Method::Head { - info_!("Autohandling {} request.", Paint::default("HEAD").bold()); + info!("Autohandling {} request.", Paint::default("HEAD").bold()); // Dispatch the request again with Method `GET`. request._set_method(Method::Get); @@ -266,7 +268,7 @@ impl Rocket { let matches = self.router.route(request); for route in matches { // Retrieve and set the requests parameters. - info_!("Matched: {}", route); + info!("Matched: {}", route); request.set_route(route); // Dispatch the request to the handler. @@ -275,14 +277,14 @@ impl Rocket { // Check if the request processing completed (Some) or if the // request needs to be forwarded. If it does, continue the loop // (None) to try again. - info_!("{} {}", Paint::default("Outcome:").bold(), outcome); + info!("{} {}", Paint::default("Outcome:").bold(), outcome); match outcome { o@Outcome::Success(_) | o@Outcome::Failure(_) => return o, Outcome::Forward(unused_data) => data = unused_data, } } - error_!("No matching routes for {}.", request); + error!("No matching routes for {}.", request); Outcome::Forward(data) } } @@ -298,7 +300,7 @@ impl Rocket { req: &'r Request<'s> ) -> impl Future> + 's { async move { - warn_!("Responding with {} catcher.", Paint::red(&status)); + warn!("Responding..."); // For now, we reset the delta state to prevent any modifications // from earlier, unsuccessful paths from being reflected in error @@ -310,10 +312,10 @@ impl Rocket { let response = if let Some(catcher) = self.catchers.get(&status.code) { catcher.handler.handle(status, req).await } else if let Some(ref default) = self.default_catcher { - warn_!("No {} catcher found. Using default catcher.", code); + warn!("No {} catcher found. Using default catcher.", code); default.handler.handle(status, req).await } else { - warn_!("No {} or default catcher found. Using Rocket default catcher.", code); + warn!("No {} or default catcher found. Using Rocket default catcher.", code); crate::catcher::default(status, req) }; @@ -321,13 +323,13 @@ impl Rocket { match response { Ok(r) => r, Err(err_status) => { - error_!("Catcher unexpectedly failed with {}.", err_status); - warn_!("Using Rocket's default 500 error catcher."); + error!("Catcher unexpectedly failed with {}.", err_status); + warn!("Using Rocket's default 500 error catcher."); let default = crate::catcher::default(Status::InternalServerError, req); default.expect("Rocket has default 500 response") } } - } + }.instrument(warn_span!("handle_error", "Responding with {} catcher.", Paint::red(&status))) } // TODO.async: Solidify the Listener APIs and make this function public @@ -351,7 +353,8 @@ impl Rocket { let proto = self.config.tls.as_ref().map_or("http://", |_| "https://"); let full_addr = format!("{}:{}", self.config.address, self.config.port); - launch_info!("{}{} {}{}", + trace::info!(target: "launch", + "{}{} {}{}", Paint::emoji("🚀 "), Paint::default("Rocket has launched from").bold(), Paint::default(proto).bold().underline(), @@ -379,8 +382,19 @@ impl Rocket { } }); - // NOTE: `hyper` uses `tokio::spawn()` as the default executor. + #[derive(Clone)] + struct InCurrentSpanExecutor; + + impl hyper::Executor for InCurrentSpanExecutor + where Fut: Future + Send + 'static, Fut::Output: Send + { + fn execute(&self, fut: Fut) { + tokio::spawn(fut.in_current_span()); + } + } + hyper::Server::builder(Incoming::from_listener(listener)) + .executor(InCurrentSpanExecutor) .http1_keepalive(http1_keepalive) .http2_keep_alive_interval(http2_keep_alive) .serve(service) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs new file mode 100644 index 0000000000..66777051cb --- /dev/null +++ b/core/lib/src/trace.rs @@ -0,0 +1,559 @@ +//! Tracing, telemetry, and logging. +//! +//! Rocket provides support for application-level diagnostics using +//! the [`tracing`] crate. `tracing` provides a _facade layer_ for scoped, +//! structured, application-level diagnostics. This means that diagnostic data +//! from Rocket applications, from Rocket itself, and from any dependencies that +//! use the [`tracing`] or [`log`] crates, can be emitted in a machine-readable +//! format and consumed in a wide variety of ways, including structured logging, +//! distributed tracing, and performance profiling. +//! +//! This module re-exports core components of the `tracing` API for use in +//! Rocket applications, and provides Rocket-specific APIs for use with +//! `tracing`. +//! +//! # Using Tracing +//! +//! Tracing's data model is based around two core concepts: [_spans_][spans] and +//! [_events_][events]. A span represents a _period of time_, with a beginning and +//! an end, during which a program was executing in a particular context or +//! performing a unit of work. An event represents a _momentary_ occurance +//! within a trace, comparable to a single log line. +//! +//! Spans and events are recorded using macros, the basics of which are likely +//! familiar to users of the [`log`] crate. The [`trace!`], [`debug!`], +//! [`info!`], [`warn!`], and [`error!`] macros record an event at a priority +//! level ranging from extremely verbose diagnostic information (`trace!`) to +//! high-priority warnings (`error!`). For example: +//! +//! ``` +//! use rocket::trace; +//! +//! trace::trace!("Apollo 13 presently at 177,861 nautical miles away."); +//! trace::debug!("Velocity now reading 3,263 feet per second."); +//! trace::info!("13, we'd like you to stir up your cryo tanks."); +//! trace::warn!("Houston, we've got a Main Bus B undervolt."); +//! trace::error!("Houston, we are venting something out into space!"); +//! ``` +//! +//! An event consists of one or more key-value pairs, called _fields_, and/or a +//! textual, human-readable _message_. For example, this will record an event +//! at the `info` level, with two fields, named `answer` and `question`: +//! +//! ``` +//! # use rocket::trace; +//! trace::info!(answer = 42, question = "life, the universe, and everything"); +//! ``` +//! The [`tracing` documentation][macros] provides details on how these macros are used. +//! +//! Spans may be recorded in a few different ways. Like events, they have a +//! priority level, and may have one or more fields. In addition, all spans also +//! have a _name_. +//! +//! Rocket's code generation will automatically generate spans for route and +//! catcher functions, so any events emitted within those functions or functions +//! they call will be annotated with the name of the handler or catcher +//! function. For example: +//! ``` +//! # use rocket::get; +//! #[get("/hello-world")] +//! fn hello_world() -> String { +//! // This event will occur within a span named `hello_world`. +//! rocket::trace::info!("saying hello!"); +//! +//! "Hello world".to_string() +//! } +//! ``` +//! Spans may be added to other functions, as well. The easiest way to +//! do this is to add the [`#[rocket::trace::instrument]`][instrument] attribute +//! to that function. For example: +//! +//! ``` +//! # #[derive(Debug)] struct Planet; +//! // Calling this function will enter a new span named `jump_to_hyperspace`. +//! #[rocket::trace::instrument] +//! async fn jump_to_hyperspace(destination: Planet) { +//! // This event will be recorded *within* the `jump_to_hyperspace` span. +//! tracing::debug!("preparing to jump to hyperspace..."); +//! +//! // ... +//! } +//! ``` +//! This will automatically create a span with the same name as the instrumented +//! function, and all the arguments to that function recorded as fields. +//! Additional arguments to `#[instrument]` allow customizing the span further. +//! See the [`tracing` crate's documentation](instrument) for details. +//! +//! In addition, spans may be created manually using the [`trace_span!`], +//! [`debug_span!`], [`info_span!`], [`warn_span!`], and [`error_span!`] macros. +//! Again, the [`tracing` documentation][macros] provides further details on how +//! to use these macros. +//! +//! # Customization +//! +//! Spans and events are recorded by a `tracing` component called a +//! [`Subscriber`], which implements a particular way of collecting and +//! recording trace data. By default, Rocket provides its own `Subscriber` +//! implementation, which logs events to the console. This `Subscriber` will be +//! installed when [`rocket::ignite`] is called. +//! +//! To override the default `Subscriber` with another implementation, simply +//! [set it as the default][default] prior to calling `rocket::ignite`. For +//! example: +//! ``` +//! # type MySubscriber = tracing_subscriber::registry::Registry; +//! #[rocket::launch] +//! fn rocket() -> rocket::Rocket { +//! let subscriber = MySubscriber::default(); +//! tracing::subscriber::set_global_default(subscriber) +//! .expect("the global default subscriber should not have been set"); +//! +//! rocket::ignite() +//! // ... +//! } +//! ``` +//! +//! Since `tracing` data is structured and machine-readable, it may be collected +//! in a variety of ways. The `tracing` community provides [several crates] for +//! logging in several commonly-used formats, emitting distributed tracing data +//! to collectors like [OpenTelemetry] and [honeycomb.io], and for +//! [multiple][timing] [forms][flame] of performance profiling. +//! +//! The [`tracing-subscriber`] crate provides an abstraction for building +//! a `Subscriber` by composing multiple [`Layer`]s which implement different +//! ways of collecting traces. This allows applications to record the same trace +//! data in multiple ways. +//! +//! In addition to providing a default subscriber out of the box, Rocket also +//! exposes its default logging and filtering behavior as `Layer`s. This means +//! that users who would like to combine the default logging layer with layers +//! from other crates may do so. For example: +//! +//! ```rust +//! # use tracing_subscriber::Layer; +//! # #[derive(Default)] struct SomeLayer; +//! # impl Layer for SomeLayer {} +//! # #[derive(Default)] struct SomeOtherLayer; +//! # impl Layer for SomeOtherLayer {} +//! #[rocket::launch] +//! fn rocket() -> rocket::Rocket { +//! use rocket::trace::prelude::*; +//! +//! let figment = rocket::Config::figment(); +//! let config = rocket::Config::from(&figment); +//! +//! // Configure our trace subscriber... +//! tracing_subscriber::registry() +//! // Add Rocket's default log formatter. +//! .with(rocket::trace::logging_layer()) +//! // Add a custom layer... +//! .with(SomeLayer::default()) +//! // ...and another custom layer. +//! .with(SomeOtherLayer::default()) +//! // Filter what traces are enabled based on the Rocket config. +//! .with(rocket::trace::filter_layer(config.log_level)) +//! // Set our subscriber as the default. +//! .init(); +//! +//! rocket::custom(figment) +//! // ... +//! } +//! ``` +//! +//! [`tracing`]: https://docs.rs/tracing +//! [`log`]: https://docs.rs/log/ +//! [spans]: https://docs.rs/tracing/latest/tracing/#spans +//! [events]: https://docs.rs/tracing/latest/tracing/#events +//! [`span!`]: https://docs.rs/tracing/latest/tracing/macro.span.html +//! [`event!`]: https://docs.rs/tracing/latest/tracing/macro.event.html +//! [`trace!`]: https://docs.rs/tracing/latest/tracing/macro.trace.html +//! [`debug!`]: https://docs.rs/tracing/latest/tracing/macro.debug.html +//! [`info!`]: https://docs.rs/tracing/latest/tracing/macro.info.html +//! [`warn!`]: https://docs.rs/tracing/latest/tracing/macro.warn.html +//! [`error!`]: https://docs.rs/tracing/latest/tracing/macro.error.html +//! [macros]: https://docs.rs/tracing/latest/tracing/index.html#using-the-macros +//! [instrument]: https://docs.rs/tracing/latest/tracing/attr.instrument.html +//! [`trace_span!`]: https://docs.rs/tracing/latest/tracing/macro.trace_span.html +//! [`debug_span!`]: https://docs.rs/tracing/latest/tracing/macro.debug_span.html +//! [`info_span!`]: https://docs.rs/tracing/latest/tracing/macro.info_span.html +//! [`warn_span!`]: https://docs.rs/tracing/latest/tracing/macro.warn_span.html +//! [`error_span!`]: https://docs.rs/tracing/latest/tracing/macro.error_span.html +//! [`rocket::ignite`]: crate::ignite +//! [default]: https://docs.rs/tracing/latest/tracing/#in-executables +//! [`Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html +//! [several crates]: https://github.com/tokio-rs/tracing#related-crates +//! [`tracing-subscriber`]: https://docs.rs/tracing-subscriber/ +//! [`Layer`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html +//! [OpenTelemetry]: https://crates.io/crates/tracing-opentelemetry +//! [honeycomb.io]: https://crates.io/crates/tracing-honeycomb +//! [timing]: https://crates.io/crates/tracing-timing +//! [flame]: https://crates.io/crates/tracing-flame +use tracing_subscriber::{ + field, + fmt::{ + format::{self, FormatEvent, FormatFields}, + FmtContext, FormattedFields, + }, + layer::Layer, + prelude::*, + registry::LookupSpan, +}; + +use std::fmt::{self, Write}; +use std::sync::atomic::{AtomicU64, Ordering::{Acquire, Release}}; +use std::str::FromStr; + +use yansi::Paint; +use serde::{de, Serialize, Serializer, Deserialize, Deserializer}; + +pub use tracing::{ + trace, debug, info, warn, error, trace_span, debug_span, info_span, + warn_span, error_span, instrument, +}; + +pub use tracing_futures::Instrument; + +/// A prelude for working with `tracing` in Rocket applications. +pub mod prelude { + pub use tracing_subscriber::prelude::*; + pub use super::Instrument as _; +} + +/// Defines the maximum level of log messages to show. +#[derive(PartialEq, Eq, Debug, Clone, Copy)] +pub enum LogLevel { + /// Only shows errors and warnings: `"critical"`. + Critical, + /// Shows everything except debug and trace information: `"normal"`. + Normal, + /// Shows everything: `"debug"`. + Debug, + /// Shows nothing: "`"off"`". + Off, +} + +impl LogLevel { + fn as_str(&self) -> &str { + match self { + LogLevel::Critical => "critical", + LogLevel::Normal => "normal", + LogLevel::Debug => "debug", + LogLevel::Off => "off", + } + } +} + +impl FromStr for LogLevel { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + let level = match &*s.to_ascii_lowercase() { + "critical" => LogLevel::Critical, + "normal" => LogLevel::Normal, + "debug" => LogLevel::Debug, + "off" => LogLevel::Off, + _ => return Err("a log level (off, debug, normal, critical)"), + }; + + Ok(level) + } +} + +impl fmt::Display for LogLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +impl Serialize for LogLevel { + fn serialize(&self, ser: S) -> Result { + ser.serialize_str(self.as_str()) + } +} + +impl<'de> Deserialize<'de> for LogLevel { + fn deserialize>(de: D) -> Result { + let string = String::deserialize(de)?; + LogLevel::from_str(&string).map_err(|_| de::Error::invalid_value( + de::Unexpected::Str(&string), + &figment::error::OneOf( &["critical", "normal", "debug", "off"]) + )) + } +} + + +/// Returns a Rocket filtering [`Layer`] based on the provided logging level. +/// +/// The returned [`Layer`] can be added to another `tracing` subscriber to +/// configure it to filter spans and events based on the logging level +/// specified in the Rocket config. +/// +/// For example: +/// +/// ``` +/// # type MySubscriber = tracing_subscriber::registry::Registry; +/// #[rocket::launch] +/// fn rocket() -> rocket::Rocket { +/// use rocket::trace::prelude::*; +/// +/// let figment = rocket::Config::figment(); +/// let config = rocket::Config::from(&figment); +/// +/// // Use some `tracing` subscriber from another crate... +/// MySubscriber::default() +/// // ...but filter spans and events based on the Rocket +/// // config file. +/// .with(rocket::trace::filter_layer(config.log_level)) +/// .init(); +/// +/// rocket::custom(figment) +/// // ... +/// } +/// ``` +/// +/// [`Layer`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html +pub fn filter_layer(level: LogLevel) -> impl Layer +where + S: tracing::Subscriber, +{ + let filter_str = match level { + LogLevel::Critical => "warn,rocket::launch=info,hyper=off,rustls=off", + LogLevel::Normal => "info,hyper=off,rustls=off", + LogLevel::Debug => "trace", + LogLevel::Off => "off", + }; + + tracing_subscriber::filter::EnvFilter::try_new(filter_str) + .expect("filter string must parse") +} + +/// Returns a Rocket-style log formatting layer. +/// +/// The returned layer can be added to a [`tracing-subscriber` +/// `Registry`][registry] to add Rocket-style log formatting in addition to +/// other [`Layer`s] providing different functionality. +/// +/// For example: +/// +/// ``` +/// # type MySubscriber = tracing_subscriber::registry::Registry; +/// #[rocket::launch] +/// fn rocket() -> rocket::Rocket { +/// use rocket::trace::prelude::*; +/// +/// let figment = rocket::Config::figment(); +/// let config = rocket::Config::from(&figment); +/// +/// // Use some `tracing` subscriber from another crate... +/// MySubscriber::default() +/// // ...but filter spans and events based on the Rocket +/// // config file. +/// .with(rocket::trace::filter_layer(config.log_level)) +/// .init(); +/// +/// rocket::custom(figment) +/// // ... +/// } +/// ``` +/// +/// [`Layer`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html +/// [`registry`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/registry/index.html +pub fn logging_layer() -> impl Layer +where + S: tracing::Subscriber, + S: for<'span> LookupSpan<'span>, +{ + let field_format = format::debug_fn(|writer, field, value| { + // We'll format the field name and value separated with a colon. + let name = field.name(); + if name == "message" { + write!(writer, "{:?}", Paint::new(value).bold()) + } else { + write!(writer, "{}: {:?}", field, Paint::new(value).bold()) + } + }) + .delimited(", ") + .display_messages(); + tracing_subscriber::fmt::layer() + .fmt_fields(field_format) + .event_format(EventFormat { last_id: AtomicU64::new(0) }) +} + +pub(crate) fn try_init(level: LogLevel, colors: bool) -> bool { + if level == LogLevel::Off { + return false; + } + + if !atty::is(atty::Stream::Stdout) + || (cfg!(windows) && !Paint::enable_windows_ascii()) + || !colors + { + Paint::disable(); + } + + tracing::subscriber::set_global_default(tracing_subscriber::registry() + .with(logging_layer()) + .with(filter_layer(level)) + ) + .is_ok() +} + +pub trait PaintExt { + fn emoji(item: &str) -> Paint<&str>; +} + +impl PaintExt for Paint<&str> { + /// Paint::masked(), but hidden on Windows due to broken output. See #1122. + fn emoji(_item: &str) -> Paint<&str> { + #[cfg(windows)] { Paint::masked("") } + #[cfg(not(windows))] { Paint::masked(_item) } + } +} + + +struct EventFormat { + last_id: AtomicU64, +} + +impl FormatEvent for EventFormat +where + S: tracing::Subscriber + for<'a> LookupSpan<'a>, + N: for<'a> FormatFields<'a> + 'static, +{ + fn format_event( + &self, + cx: &FmtContext<'_, S, N>, + writer: &mut dyn fmt::Write, + event: &tracing::Event<'_>, + ) -> fmt::Result { + let mut seen = false; + let id = if let Some(span) = cx.lookup_current() { + let id = span.id(); + if id.into_u64() != self.last_id.load(Acquire) { + cx.visit_spans(|span| { + if seen { + write!(writer, " {} ", Paint::default("=>").bold())?; + } + let meta = span.metadata(); + let exts = span.extensions(); + if let Some(fields) = exts.get::>() { + // If the span has a human-readable message, print that + // instead of the span's name (so that we can get nice emojis). + if meta.fields().iter().any(|field| field.name() == "message") { + with_meta(writer, meta, &fields.fields)?; + } else { + with_meta(writer, meta, format_args!("{} {}", Paint::new(span.name()).bold(), &fields.fields))?; + } + } else { + with_meta(writer, span.metadata(), Paint::new(span.name()).bold())?; + } + seen = true; + Ok(()) + })?; + } else { + seen = true; + } + Some(id) + } else { + None + }; + + if seen { + write!(writer, " {} ", Paint::default("=>").bold())?; + } + + // xxx(eliza): workaround + let fmt = format::debug_fn(|writer, field, value| { + // We'll format the field name and value separated with a colon. + let name = field.name(); + if name == "message" { + write!(writer, "{:?}", Paint::new(value).bold()) + } else { + write!(writer, "{}: {:?}", field, Paint::new(value).bold()) + } + }) + .delimited(", ") + .display_messages(); + with_meta( + writer, + event.metadata(), + &FmtVisitor { + fmt: &fmt, + records: event, + }, + )?; + if let Some(id) = id { + self.last_id.store(id.into_u64(), Release); + } + Ok(()) + } +} + +fn with_meta( + writer: &mut dyn Write, + meta: &tracing::Metadata<'_>, + f: impl fmt::Display, +) -> fmt::Result { + + struct WithFile<'a, F> { + meta: &'a tracing::Metadata<'a>, + f: F, + } + + impl fmt::Display for WithFile<'_, F> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match (self.meta.file(), self.meta.line()) { + (Some(file), Some(line)) => write!( + f, + "{}\n {} {}:{}", + self.f, + Paint::new("-->").bold(), + file, + line + ), + (Some(file), None) => write!( + f, + "{}\n {} {}", + self.f, + Paint::new("-->").bold(), + file, + ), + _ => write!(f, "{}", self.f), + } + } + } + + + match *meta.level() { + tracing::Level::INFO => writeln!(writer, "{}", Paint::blue(f).wrap()), + tracing::Level::ERROR => writeln!( + writer, + "{} {}", + Paint::red("Error:").bold(), + Paint::red(f).wrap() + ), + tracing::Level::WARN => writeln!( + writer, + "{} {}", + Paint::yellow("Warning:").bold(), + Paint::yellow(f).wrap() + ), + tracing::Level::TRACE => writeln!(writer, "{}", Paint::magenta(WithFile { meta, f }).wrap()), + tracing::Level::DEBUG => writeln!(writer, "{}", Paint::blue(WithFile { meta, f }).wrap()), + } +} + +struct FmtVisitor<'a, F, R> { + fmt: &'a F, + records: R, +} + +impl fmt::Display for FmtVisitor<'_, F, R> +where + F: for<'w> FormatFields<'w>, + R: field::RecordFields, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.fmt.format_fields(f, &self.records) + } +} diff --git a/examples/todo/src/main.rs b/examples/todo/src/main.rs index b5be64874c..c1d6127233 100644 --- a/examples/todo/src/main.rs +++ b/examples/todo/src/main.rs @@ -44,7 +44,7 @@ impl Context { match Task::all(conn).await { Ok(tasks) => Context { msg, tasks }, Err(e) => { - error_!("DB Task::all() error: {}", e); + error!("DB Task::all() error: {}", e); Context { msg: Some(("error".into(), "Fail to access database.".into())), tasks: vec![] @@ -60,7 +60,7 @@ async fn new(todo_form: Form, conn: DbConn) -> Flash { if todo.description.is_empty() { Flash::error(Redirect::to("/"), "Description cannot be empty.") } else if let Err(e) = Task::insert(todo, &conn).await { - error_!("DB insertion error: {}", e); + error!("DB insertion error: {}", e); Flash::error(Redirect::to("/"), "Todo could not be inserted due an internal error.") } else { Flash::success(Redirect::to("/"), "Todo successfully added.") @@ -72,7 +72,7 @@ async fn toggle(id: i32, conn: DbConn) -> Result { match Task::toggle_with_id(id, &conn).await { Ok(_) => Ok(Redirect::to("/")), Err(e) => { - error_!("DB toggle({}) error: {}", id, e); + error!("DB toggle({}) error: {}", id, e); Err(Template::render("index", Context::err(&conn, "Failed to toggle task.").await)) } } @@ -83,7 +83,7 @@ async fn delete(id: i32, conn: DbConn) -> Result, Template> { match Task::delete_with_id(id, &conn).await { Ok(_) => Ok(Flash::success(Redirect::to("/"), "Todo was deleted.")), Err(e) => { - error_!("DB deletion({}) error: {}", id, e); + error!("DB deletion({}) error: {}", id, e); Err(Template::render("index", Context::err(&conn, "Failed to delete task.").await)) } } From 6fbbf940aa74b628a38e91427d3e2da1cdac8abe Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sun, 1 Nov 2020 10:17:35 -0800 Subject: [PATCH 29/60] style: remove most usage of span.enter() --- contrib/lib/src/templates/fairing.rs | 7 +++---- contrib/lib/src/templates/tera_templates.rs | 10 ++++------ core/lib/src/data/data_stream.rs | 5 ++--- core/lib/src/error.rs | 3 +-- core/lib/src/rocket.rs | 22 ++++++++++----------- 5 files changed, 20 insertions(+), 27 deletions(-) diff --git a/contrib/lib/src/templates/fairing.rs b/contrib/lib/src/templates/fairing.rs index cd960b9b62..69b804f2e9 100644 --- a/contrib/lib/src/templates/fairing.rs +++ b/contrib/lib/src/templates/fairing.rs @@ -98,15 +98,14 @@ mod context { if changed { let span = info_span!("Change detected: reloading templates."); - let _entered = span.enter(); let mut ctxt = self.context_mut(); if let Some(mut new_ctxt) = Context::initialize(ctxt.root.clone()) { custom_callback(&mut new_ctxt.engines); *ctxt = new_ctxt; - info!("reloaded!"); + info!(parent: &span, "reloaded!"); } else { - warn!("An error occurred while reloading templates."); - warn!("The previous templates will remain active."); + warn!(parent: &span, "An error occurred while reloading templates."); + warn!(parent: &span, "The previous templates will remain active."); }; } }); diff --git a/contrib/lib/src/templates/tera_templates.rs b/contrib/lib/src/templates/tera_templates.rs index 5b0eaec105..99a68af2fc 100644 --- a/contrib/lib/src/templates/tera_templates.rs +++ b/contrib/lib/src/templates/tera_templates.rs @@ -22,11 +22,10 @@ impl Engine for Tera { // Finally try to tell Tera about all of the templates. if let Err(e) = tera.add_template_files(tera_templates) { let span = error_span!("Failed to initialize Tera templating."); - let _entered = span.enter(); let mut error = Some(&e as &dyn Error); while let Some(err) = error { - error!(error = %err); + error!(parent: &span, error = %err); error = err.source(); } @@ -38,9 +37,8 @@ impl Engine for Tera { fn render(&self, name: &str, context: C) -> Option { let span = info_span!("Rendering Tera template", template = %name); - let _entered = span.enter(); if self.get_template(name).is_err() { - error!("Tera template '{}' does not exist.", name); + error!(parent: &span, "Tera template '{}' does not exist.", name); return None; }; @@ -48,6 +46,7 @@ impl Engine for Tera { Ok(ctx) => ctx, Err(_) => { error!( + parent: &span, "Error generating context when rendering Tera template '{}'.", name ); @@ -59,10 +58,9 @@ impl Engine for Tera { Ok(string) => Some(string), Err(e) => { let span = error_span!("Error rendering Tera template", template = %name); - let _entered = span.enter(); let mut error = Some(&e as &dyn Error); while let Some(err) = error { - error!(error = %err); + error!(parent: &span, error = %err); error = err.source(); } diff --git a/core/lib/src/data/data_stream.rs b/core/lib/src/data/data_stream.rs index 300434c773..9406b12377 100644 --- a/core/lib/src/data/data_stream.rs +++ b/core/lib/src/data/data_stream.rs @@ -119,17 +119,16 @@ impl AsyncRead for DataStream { buf: &mut [u8] ) -> Poll> { let span = trace_span!("DataStream::poll_read()"); - let _e = span.enter(); if self.buffer.limit() > 0 { - trace!("DataStream::buffer_read()"); + trace!(parent: &span, "DataStream::buffer_read()"); match Pin::new(&mut self.buffer).poll_read(cx, buf) { Poll::Ready(Ok(0)) => { /* fall through */ }, poll => return poll, } } - trace!("DataStream::stream_read()"); + trace!(parent: &span, "DataStream::stream_read()"); Pin::new(&mut self.stream).poll_read(cx, buf) } } diff --git a/core/lib/src/error.rs b/core/lib/src/error.rs index 68d9ee468c..035ae0fc69 100644 --- a/core/lib/src/error.rs +++ b/core/lib/src/error.rs @@ -179,9 +179,8 @@ impl Drop for Error { } ErrorKind::Collision(ref collisions) => { let span = error_span!("collisions", "Rocket failed to launch due to the following routing collisions:"); - let _e = span.enter(); for &(ref a, ref b) in collisions { - info!("{} {} {}", a, Paint::red("collides with").italic(), b) + info!(parent: &span, "{} {} {}", a, Paint::red("collides with").italic(), b) } info!(parent: &span, "Note: Collisions can usually be resolved by ranking routes."); diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 1b55d6049c..19fece5e05 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -152,26 +152,25 @@ impl Rocket { /// ``` #[inline] pub fn mount>>(mut self, base: &str, routes: R) -> Self { + let span = info_span!("mounting", at = %Paint::blue(&base), "{} Mounting", Paint::emoji("🛰 ")); + let base_uri = Origin::parse_owned(base.to_string()) .unwrap_or_else(|e| { - error!("Invalid mount point URI: {}.", Paint::white(base)); + error!(parent: &span, "Invalid mount point URI: {}.", Paint::white(base)); panic!("Error: {}", e); }); if base_uri.query().is_some() { - error!("Mount point '{}' contains query string.", base); + error!(parent: &span, "Mount point '{}' contains query string.", base); panic!("Invalid mount point."); } - let span = info_span!("mounting", at = %Paint::blue(&base), "{} Mounting", Paint::emoji("🛰 ")); - let _e = span.enter(); - for route in routes.into() { let old_route = route.clone(); let route = route.map_base(|old| format!("{}{}", base, old)) .unwrap_or_else(|e| { - let span = error_span!("malformed_uri", "Route `{}` has a malformed URI.", old_route); - error!(parent: &span, "{}", e); + let span2 = error_span!(parent: span, "malformed_uri", "Route `{}` has a malformed URI.", old_route); + error!(parent: &span2, "{}", e); panic!("Invalid route URI."); }); @@ -208,10 +207,9 @@ impl Rocket { #[inline] pub fn register(mut self, catchers: Vec) -> Self { let span = info_span!("catchers", "{}{}", Paint::emoji("👾 "), Paint::magenta("Catchers:")); - let _e = span.enter(); for catcher in catchers { - info!("{}", catcher); + info!(parent: &span, "{}", catcher); let existing = match catcher.code { Some(code) => self.catchers.insert(code, catcher), @@ -219,7 +217,7 @@ impl Rocket { }; if let Some(existing) = existing { - warn!("Replacing existing '{}' catcher.", existing); + warn!(parent: &span, "Replacing existing '{}' catcher.", existing); } } @@ -552,8 +550,8 @@ impl Rocket { } Either::Left((Err(err), server)) => { // Error setting up ctrl-c signal. Let the user know. - warn!("Failed to enable `ctrl-c` graceful signal shutdown."); - info!("Error: {}", err); + let span = warn_span!("graceful_shutdown_error", "Failed to enable `ctrl-c` graceful signal shutdown."); + info!(parent: &span, "Error: {}", err); server.await } // Server shut down before Ctrl-C; return the result. From c8e4b6bc9376872805faf44e0d4b5988bc8694c2 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sun, 1 Nov 2020 10:37:39 -0800 Subject: [PATCH 30/60] fix: macros are weird sometimes --- core/lib/src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/src/error.rs b/core/lib/src/error.rs index 035ae0fc69..c7e4196bef 100644 --- a/core/lib/src/error.rs +++ b/core/lib/src/error.rs @@ -180,7 +180,7 @@ impl Drop for Error { ErrorKind::Collision(ref collisions) => { let span = error_span!("collisions", "Rocket failed to launch due to the following routing collisions:"); for &(ref a, ref b) in collisions { - info!(parent: &span, "{} {} {}", a, Paint::red("collides with").italic(), b) + info!(parent: &span, "{} {} {}", a, Paint::red("collides with").italic(), b); } info!(parent: &span, "Note: Collisions can usually be resolved by ranking routes."); From eeeab8891db72aa78a11ee67d5fd1f977e2eebe9 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sun, 1 Nov 2020 10:40:49 -0800 Subject: [PATCH 31/60] fix: use of moved value --- core/lib/src/rocket.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 19fece5e05..09519fb86b 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -169,7 +169,7 @@ impl Rocket { let old_route = route.clone(); let route = route.map_base(|old| format!("{}{}", base, old)) .unwrap_or_else(|e| { - let span2 = error_span!(parent: span, "malformed_uri", "Route `{}` has a malformed URI.", old_route); + let span2 = error_span!(parent: span.clone(), "malformed_uri", "Route `{}` has a malformed URI.", old_route); error!(parent: &span2, "{}", e); panic!("Invalid route URI."); }); From 9f8621427f56bcac311269758273128203d61827 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Mon, 2 Nov 2020 11:32:53 -0800 Subject: [PATCH 32/60] allow libtest to capture trace logs Signed-off-by: Eliza Weisman --- core/lib/Cargo.toml | 2 +- core/lib/src/trace.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index aba5070a90..4f7b5f5cb7 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -51,7 +51,7 @@ version = "0.2.9" features = ["fs", "io-std", "io-util", "rt-threaded", "sync", "signal", "macros"] [dependencies.tracing-subscriber] -version = "0.2.9" +version = "0.2.14" default-features = false features = ["fmt", "env-filter", "smallvec"] diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 66777051cb..ce29100a9c 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -376,6 +376,10 @@ where .display_messages(); tracing_subscriber::fmt::layer() .fmt_fields(field_format) + // Configure the formatter to use `print!` rather than + // `stdout().write_str(...)`, so that logs are captured by libtest's test + // capturing. + .with_test_writer() .event_format(EventFormat { last_id: AtomicU64::new(0) }) } From 9e954378dd435458636df3ec59d71d4cd032217a Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Mon, 2 Nov 2020 11:42:22 -0800 Subject: [PATCH 33/60] remove workaround Signed-off-by: Eliza Weisman --- core/lib/src/trace.rs | 49 +++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index ce29100a9c..d02b94a6b0 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -466,23 +466,11 @@ where write!(writer, " {} ", Paint::default("=>").bold())?; } - // xxx(eliza): workaround - let fmt = format::debug_fn(|writer, field, value| { - // We'll format the field name and value separated with a colon. - let name = field.name(); - if name == "message" { - write!(writer, "{:?}", Paint::new(value).bold()) - } else { - write!(writer, "{}: {:?}", field, Paint::new(value).bold()) - } - }) - .delimited(", ") - .display_messages(); with_meta( writer, event.metadata(), - &FmtVisitor { - fmt: &fmt, + DisplayFields { + fmt: cx, records: event, }, )?; @@ -493,6 +481,22 @@ where } } + +struct DisplayFields<'a, F, R> { + fmt: &'a F, + records: &'a R, +} + +impl fmt::Display for DisplayFields<'_, F, R> +where + F: for<'writer> FormatFields<'writer>, + R: tracing_subscriber::field::RecordFields, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.fmt.format_fields(f, self.fields) + } +} + fn with_meta( writer: &mut dyn Write, meta: &tracing::Metadata<'_>, @@ -545,19 +549,4 @@ fn with_meta( tracing::Level::TRACE => writeln!(writer, "{}", Paint::magenta(WithFile { meta, f }).wrap()), tracing::Level::DEBUG => writeln!(writer, "{}", Paint::blue(WithFile { meta, f }).wrap()), } -} - -struct FmtVisitor<'a, F, R> { - fmt: &'a F, - records: R, -} - -impl fmt::Display for FmtVisitor<'_, F, R> -where - F: for<'w> FormatFields<'w>, - R: field::RecordFields, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.fmt.format_fields(f, &self.records) - } -} +} \ No newline at end of file From 9776ca34bd84fd9f5da54d7942ca2b5b53d95302 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Mon, 2 Nov 2020 16:16:57 -0800 Subject: [PATCH 34/60] remove workarounds, add `log` compat Signed-off-by: Eliza Weisman --- core/lib/Cargo.toml | 8 +++++--- core/lib/src/trace.rs | 48 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 4f7b5f5cb7..12783f03fb 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -19,16 +19,16 @@ edition = "2018" all-features = true [features] -default = [] +default = ["log"] tls = ["rocket_http/tls"] secrets = ["rocket_http/private-cookies"] +log = ["log_crate", "tracing/log", "tracing-subscriber/tracing-log", "tracing-log"] [dependencies] rocket_codegen = { version = "0.5.0-dev", path = "../codegen" } rocket_http = { version = "0.5.0-dev", path = "../http" } futures = "0.3.0" yansi = "0.5" -log = { version = "0.4", features = ["std"] } num_cpus = "1.0" state = "0.4.1" time = "0.2.11" @@ -45,13 +45,15 @@ figment = { version = "0.9.2", features = ["toml", "env"] } rand = "0.7" either = "1" tracing = "0.1.19" +tracing-log = { version = "0.1", optional = true } +log_crate = { package = "log", version = "0.4", optional = true } [dependencies.tokio] version = "0.2.9" features = ["fs", "io-std", "io-util", "rt-threaded", "sync", "signal", "macros"] [dependencies.tracing-subscriber] -version = "0.2.14" +version = "0.2.15" default-features = false features = ["fmt", "env-filter", "smallvec"] diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index d02b94a6b0..64aef2d0a8 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -189,7 +189,6 @@ //! [timing]: https://crates.io/crates/tracing-timing //! [flame]: https://crates.io/crates/tracing-flame use tracing_subscriber::{ - field, fmt::{ format::{self, FormatEvent, FormatFields}, FmtContext, FormattedFields, @@ -212,6 +211,7 @@ pub use tracing::{ }; pub use tracing_futures::Instrument; +pub use tracing_subscriber::registry; /// A prelude for working with `tracing` in Rocket applications. pub mod prelude { @@ -395,6 +395,17 @@ pub(crate) fn try_init(level: LogLevel, colors: bool) -> bool { Paint::disable(); } + // Try to enable a `log` compatibility layer to collect logs from + // dependencies using the `log` crate as `tracing` diagnostics. + #[cfg(feature = "log")] + if try_init_log(level).is_err() { + // We failed to set the default `log` logger. This means that the user + // has already set a `log` logger. In that case, don't try to set up a + // `tracing` subscriber as well --- instead, Rocket's `tracing` events + // will be recorded as `log` records. + return false; + } + tracing::subscriber::set_global_default(tracing_subscriber::registry() .with(logging_layer()) .with(filter_layer(level)) @@ -423,6 +434,7 @@ impl FormatEvent for EventFormat where S: tracing::Subscriber + for<'a> LookupSpan<'a>, N: for<'a> FormatFields<'a> + 'static, + for<'a> FmtContext<'a, S, N>: FormatFields<'a>, { fn format_event( &self, @@ -471,7 +483,7 @@ where event.metadata(), DisplayFields { fmt: cx, - records: event, + event, }, )?; if let Some(id) = id { @@ -484,16 +496,16 @@ where struct DisplayFields<'a, F, R> { fmt: &'a F, - records: &'a R, + event: &'a R, } -impl fmt::Display for DisplayFields<'_, F, R> +impl<'a, F, R> fmt::Display for DisplayFields<'a, F, R> where F: for<'writer> FormatFields<'writer>, R: tracing_subscriber::field::RecordFields, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.fmt.format_fields(f, self.fields) + self.fmt.format_fields(f, self.event) } } @@ -549,4 +561,30 @@ fn with_meta( tracing::Level::TRACE => writeln!(writer, "{}", Paint::magenta(WithFile { meta, f }).wrap()), tracing::Level::DEBUG => writeln!(writer, "{}", Paint::blue(WithFile { meta, f }).wrap()), } +} + +/// Set up `tracing`/`log` compatibility. +#[cfg(feature = "log")] +fn try_init_log(filter: LogLevel) -> Result<(), impl std::error::Error> { + use log_crate::LevelFilter; + + let builder = tracing_log::LogTracer::builder() + // Hyper and Rocket both use `tracing`. If `tracing`'s `log` feature + // is enabled and the `tracing` macros in Hyper and Rocket also emit + // `log` records, ignore them, because the native `tracing` events + // will already be collected. + .ignore_all(vec!["rocket", "hyper", "tracing::span"]); + let builder = match filter { + LogLevel::Critical => builder + .ignore_crate("rustls") + // Set the max level for all `log` records to Warn. Rocket's + // `launch` events will be collected by the native `tracing` + // subscriber, so we don't need to allow `log` records at Info + // in order to see them. + .with_max_level(LevelFilter::Warn), + LogLevel::Normal => builder.ignore_crate("rustls").with_max_level(LevelFilter::Info), + LogLevel::Debug => builder.with_max_level(LevelFilter::Trace), + LogLevel::Off => return Ok(()), + }; + builder.init() } \ No newline at end of file From fa5f30db9679d7aa5bb5bfaf43aeab0a288816bf Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Mon, 2 Nov 2020 16:19:08 -0800 Subject: [PATCH 35/60] examples for a custom subscriber, env_logger Signed-off-by: Eliza Weisman --- Cargo.toml | 2 ++ examples/trace_env_logger/Cargo.toml | 10 ++++++++++ examples/trace_env_logger/src/main.rs | 19 ++++++++++++++++++ examples/trace_subscriber/Cargo.toml | 11 +++++++++++ examples/trace_subscriber/src/main.rs | 28 +++++++++++++++++++++++++++ 5 files changed, 70 insertions(+) create mode 100644 examples/trace_env_logger/Cargo.toml create mode 100644 examples/trace_env_logger/src/main.rs create mode 100644 examples/trace_subscriber/Cargo.toml create mode 100644 examples/trace_subscriber/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index fb739db000..3995aeb856 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,4 +42,6 @@ members = [ "examples/tls", "examples/fairings", "examples/hello_2018", + "examples/trace_env_logger", + "examples/trace_subscriber", ] \ No newline at end of file diff --git a/examples/trace_env_logger/Cargo.toml b/examples/trace_env_logger/Cargo.toml new file mode 100644 index 0000000000..c2b0500d2b --- /dev/null +++ b/examples/trace_env_logger/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "trace_env_logger" +version = "0.0.0" +workspace = "../../" +edition = "2018" +publish = false + +[dependencies] +rocket = { path = "../../core/lib" } +env_logger = "0.8" \ No newline at end of file diff --git a/examples/trace_env_logger/src/main.rs b/examples/trace_env_logger/src/main.rs new file mode 100644 index 0000000000..f8bde2dc2a --- /dev/null +++ b/examples/trace_env_logger/src/main.rs @@ -0,0 +1,19 @@ +use rocket::trace; + +#[rocket::get("/hello//")] +fn hello(name: String, age: u8) -> String { + trace::info!(?name, age, "saying hello to"); + format!("Hello, {} year old named {}!", age, name) +} + +#[rocket::get("/hello/")] +fn hi(name: String) -> String { + trace::info!(?name, "saying hi to"); + name +} + +#[rocket::launch] +fn rocket() -> rocket::Rocket { + env_logger::init(); + rocket::ignite().mount("/", rocket::routes![hello, hi]) +} diff --git a/examples/trace_subscriber/Cargo.toml b/examples/trace_subscriber/Cargo.toml new file mode 100644 index 0000000000..beb46fd8e9 --- /dev/null +++ b/examples/trace_subscriber/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "trace_subscriber" +version = "0.0.0" +workspace = "../../" +edition = "2018" +publish = false + +[dependencies] +rocket = { path = "../../core/lib" } +tracing-subscriber = "0.2.15" +tracing = "0.1" \ No newline at end of file diff --git a/examples/trace_subscriber/src/main.rs b/examples/trace_subscriber/src/main.rs new file mode 100644 index 0000000000..5514a61ff9 --- /dev/null +++ b/examples/trace_subscriber/src/main.rs @@ -0,0 +1,28 @@ +use rocket::trace; + +#[rocket::get("/hello//")] +fn hello(name: String, age: u8) -> String { + trace::info!(?name, age, "saying hello to"); + format!("Hello, {} year old named {}!", age, name) +} + +#[rocket::get("/hello/")] +fn hi(name: String) -> String { + trace::info!(?name, "saying hi to"); + name +} + +#[rocket::launch] +fn rocket() -> rocket::Rocket { + use trace::prelude::*; + + let figment = rocket::Config::figment(); + let config = rocket::Config::from(&figment); + + rocket::trace::registry() + .with(tracing_subscriber::fmt::layer()) + .with(rocket::trace::filter_layer(config.log_level)) + .init(); + + rocket::custom(figment).mount("/", rocket::routes![hello, hi]) +} From e68d66dd998bc4ee87113b0549c62f7114f44030 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 4 Nov 2020 09:47:36 -0800 Subject: [PATCH 36/60] use structured fields in more places Signed-off-by: Eliza Weisman --- contrib/lib/src/serve.rs | 2 +- contrib/lib/src/templates/context.rs | 7 ++++--- .../lib/src/templates/handlebars_templates.rs | 7 ++++--- contrib/lib/src/templates/metadata.rs | 7 ++++--- contrib/lib/src/templates/mod.rs | 21 +++++++++++-------- core/lib/src/error.rs | 19 +++++++++-------- core/lib/src/rocket.rs | 20 ++++++++++-------- core/lib/src/server.rs | 4 ++-- 8 files changed, 48 insertions(+), 39 deletions(-) diff --git a/contrib/lib/src/serve.rs b/contrib/lib/src/serve.rs index 4d62aa2586..2407d06684 100644 --- a/contrib/lib/src/serve.rs +++ b/contrib/lib/src/serve.rs @@ -289,7 +289,7 @@ impl StaticFiles { let path = path.as_ref(); if !path.is_dir() { let span = error_span!("`StaticFiles` supplied with invalid path"); - info!(parent: &span, "'{}' is not a directory", Paint::white(path.display())); + info!(parent: &span, %path = Paint::white(path.display(), "Path is not a directory")); panic!("refusing to continue due to invalid static files path"); } diff --git a/contrib/lib/src/templates/context.rs b/contrib/lib/src/templates/context.rs index 0f815026ba..fe96840ff4 100644 --- a/contrib/lib/src/templates/context.rs +++ b/contrib/lib/src/templates/context.rs @@ -29,9 +29,10 @@ impl Context { let (name, data_type_str) = split_path(&root, &path); if let Some(info) = templates.get(&*name) { let span = warn_span!("invalid_template_path", "Template name '{}' does not have a unique path.", name); - info!(parent: &span, "Existing path: {:?}", info.path); - info!(parent: &span, "Additional path: {:?}", path); - warn!(parent: &span, "Using existing path for template '{}'.", name); + let _enter = span.enter(); + info!(path = ?info.path, "Existing"); + info!(path = ?path, "Additiona"); + warn!("Using existing path for template '{}'.", name); continue; } diff --git a/contrib/lib/src/templates/handlebars_templates.rs b/contrib/lib/src/templates/handlebars_templates.rs index 40c85ad766..c2fde92782 100644 --- a/contrib/lib/src/templates/handlebars_templates.rs +++ b/contrib/lib/src/templates/handlebars_templates.rs @@ -12,9 +12,10 @@ impl Engine for Handlebars<'static> { for &(name, info) in templates { let path = &info.path; if let Err(e) = hb.register_template_file(name, path) { - let span = error_span!("Error in Handlebars template", template = %name); - info!(parent: &span, template.error = %e); - info!(parent: &span, template.path = %path.to_string_lossy()); + let span = error_span!("template_error", template = %name, "Error in Handlebars template",); + let _enter = span.enter(); + info!(template.error = %e); + info!(template.path = %path.to_string_lossy()); return None; } } diff --git a/contrib/lib/src/templates/metadata.rs b/contrib/lib/src/templates/metadata.rs index 7660fce435..40cf87e5c2 100644 --- a/contrib/lib/src/templates/metadata.rs +++ b/contrib/lib/src/templates/metadata.rs @@ -93,9 +93,10 @@ impl<'a, 'r> FromRequest<'a, 'r> for Metadata<'a> { .succeeded() .and_then(|cm| Some(request::Outcome::Success(Metadata(cm.inner())))) .unwrap_or_else(|| { - let span = error_span!("Uninitialized template context: missing fairing."); - info!(parent: &span, "To use templates, you must attach `Template::fairing()`."); - info!(parent: &span, "See the `Template` documentation for more information."); + error_span!("missing_fairing", "Uninitialized template context: missing fairing.").in_scope(|| { + info!("To use templates, you must attach `Template::fairing()`."); + info!("See the `Template` documentation for more information."); + }); request::Outcome::Failure((Status::InternalServerError, ())) }) } diff --git a/contrib/lib/src/templates/mod.rs b/contrib/lib/src/templates/mod.rs index ab0402e5c8..49a7f2bd3b 100644 --- a/contrib/lib/src/templates/mod.rs +++ b/contrib/lib/src/templates/mod.rs @@ -377,9 +377,10 @@ impl Template { where S: Into>, C: Serialize { let ctxt = rocket.state::().map(ContextManager::context).or_else(|| { - let span = warn_span!("Uninitialized template context: missing fairing."); - info!(parent: &span, "To use templates, you must attach `Template::fairing()`."); - info!(parent: &span, "See the `Template` documentation for more information."); + warn_span!("missing_fairing", "Uninitialized template context: missing fairing.").in_scope(|| { + info!("To use templates, you must attach `Template::fairing()`."); + info!("See the `Template` documentation for more information."); + }); None })?; @@ -394,9 +395,10 @@ impl Template { let name = &*self.name; let info = ctxt.templates.get(name).ok_or_else(|| { let ts: Vec<_> = ctxt.templates.keys().map(|s| s.as_str()).collect(); - let span = error_span!("Template does not exist.", template = %name); - info!(parent: &span, "Known templates: {}", ts.join(",")); - info!(parent: &span, "Searched in '{:?}'.", ctxt.root); + error_span!("Template does not exist.", template = %name).in_scope(|| { + info!("Known templates: {}", ts.join(",")); + info!("Searched in '{:?}'.", ctxt.root); + }); Status::InternalServerError })?; @@ -421,9 +423,10 @@ impl<'r> Responder<'r, 'static> for Template { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { let (render, content_type) = { let ctxt = req.managed_state::().ok_or_else(|| { - let span = error_span!("Uninitialized template context: missing fairing."); - info!(parent: &span, "To use templates, you must attach `Template::fairing()`."); - info!(parent: &span, "See the `Template` documentation for more information."); + error_span!("missing_fairing", "Uninitialized template context: missing fairing.").in_scope(|| { + info!("To use templates, you must attach `Template::fairing()`."); + info!("See the `Template` documentation for more information."); + }); Status::InternalServerError })?.context(); diff --git a/core/lib/src/error.rs b/core/lib/src/error.rs index c7e4196bef..ee9658456e 100644 --- a/core/lib/src/error.rs +++ b/core/lib/src/error.rs @@ -167,36 +167,37 @@ impl Drop for Error { } match *self.kind() { - ErrorKind::Bind(ref e) => { + ErrorKind::Bind(ref error) => { let span = error_span!("bind_error", "Rocket failed to bind network socket to given address/port."); - info!(parent: &span, "{}", e); + info!(parent: &span, %error); panic!("aborting due to binding o error"); } - ErrorKind::Io(ref e) => { + ErrorKind::Io(ref error) => { let span = error_span!("io_error", "Rocket failed to launch due to an I/O error."); - info!(parent: &span, "{}", e); + info!(parent: &span, %error); panic!("aborting due to i/o error"); } ErrorKind::Collision(ref collisions) => { let span = error_span!("collisions", "Rocket failed to launch due to the following routing collisions:"); + let _enter = span.enter(); for &(ref a, ref b) in collisions { - info!(parent: &span, "{} {} {}", a, Paint::red("collides with").italic(), b); + info!("{} {} {}", a, Paint::red("collides with").italic(), b); } - info!(parent: &span, "Note: Collisions can usually be resolved by ranking routes."); + info!("Note: Collisions can usually be resolved by ranking routes."); panic!("route collisions detected"); } ErrorKind::FailedFairings(ref failures) => { let span = error_span!("fairing_error", "Rocket failed to launch due to failing fairings:"); for fairing in failures { - info!(parent: &span, "{}", fairing); + info!(%fairing); } panic!("aborting due to launch fairing failure"); } - ErrorKind::Runtime(ref err) => { + ErrorKind::Runtime(ref error) => { let span = error_span!("runtime_error", "An error occured in the runtime:"); - info!(parent: &span, "{}", err); + info!(%error); panic!("aborting due to runtime failure"); } } diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 09519fb86b..cf71b230b2 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -153,24 +153,25 @@ impl Rocket { #[inline] pub fn mount>>(mut self, base: &str, routes: R) -> Self { let span = info_span!("mounting", at = %Paint::blue(&base), "{} Mounting", Paint::emoji("🛰 ")); + let _e = span.enter(); let base_uri = Origin::parse_owned(base.to_string()) .unwrap_or_else(|e| { - error!(parent: &span, "Invalid mount point URI: {}.", Paint::white(base)); + error!(uri = %Paint::white(base), "Invalid mount point URI"); panic!("Error: {}", e); }); if base_uri.query().is_some() { - error!(parent: &span, "Mount point '{}' contains query string.", base); + error!("Mount point '{}' contains query string.", base); panic!("Invalid mount point."); } for route in routes.into() { let old_route = route.clone(); let route = route.map_base(|old| format!("{}{}", base, old)) - .unwrap_or_else(|e| { - let span2 = error_span!(parent: span.clone(), "malformed_uri", "Route `{}` has a malformed URI.", old_route); - error!(parent: &span2, "{}", e); + .unwrap_or_else(|error| { + let span = error_span!("malformed_uri", route = %old_route, "Route has a malformed URI."); + error!(parent: &span, %error); panic!("Invalid route URI."); }); @@ -207,9 +208,10 @@ impl Rocket { #[inline] pub fn register(mut self, catchers: Vec) -> Self { let span = info_span!("catchers", "{}{}", Paint::emoji("👾 "), Paint::magenta("Catchers:")); + let _enter = span.enter(); for catcher in catchers { - info!(parent: &span, "{}", catcher); + info!(%catcher); let existing = match catcher.code { Some(code) => self.catchers.insert(code, catcher), @@ -217,7 +219,7 @@ impl Rocket { }; if let Some(existing) = existing { - warn!(parent: &span, "Replacing existing '{}' catcher.", existing); + warn!("Replacing existing '{}' catcher.", existing); } } @@ -548,10 +550,10 @@ impl Rocket { shutdown_handle.shutdown(); server.await } - Either::Left((Err(err), server)) => { + Either::Left((Err(error), server)) => { // Error setting up ctrl-c signal. Let the user know. let span = warn_span!("graceful_shutdown_error", "Failed to enable `ctrl-c` graceful signal shutdown."); - info!(parent: &span, "Error: {}", err); + info!(parent: &span, %error); server.await } // Server shut down before Ctrl-C; return the result. diff --git a/core/lib/src/server.rs b/core/lib/src/server.rs index c3c691796b..7be28e20ff 100644 --- a/core/lib/src/server.rs +++ b/core/lib/src/server.rs @@ -84,7 +84,7 @@ impl Rocket { ) { match self.make_response(response, tx).await { Ok(()) => info!("{}", Paint::green("Response succeeded.")), - Err(e) => error!("Failed to write response: {:?}.", e), + Err(error) => error!(%error, "Failed to write response"), } } @@ -268,7 +268,7 @@ impl Rocket { let matches = self.router.route(request); for route in matches { // Retrieve and set the requests parameters. - info!("Matched: {}", route); + info!(matched = %route); request.set_route(route); // Dispatch the request to the handler. From 4e9a3080713ea03032be9d3c1a2ac3b89adf1de1 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 4 Nov 2020 09:57:00 -0800 Subject: [PATCH 37/60] post-rebase fixup Signed-off-by: Eliza Weisman --- core/codegen/src/attribute/route.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/codegen/src/attribute/route.rs b/core/codegen/src/attribute/route.rs index 9c02f33803..4be14c297e 100644 --- a/core/codegen/src/attribute/route.rs +++ b/core/codegen/src/attribute/route.rs @@ -138,7 +138,7 @@ fn param_expr(seg: &Segment, ident: &syn::Ident, ty: &syn::Type) -> TokenStream }); // Returned when a dynamic parameter fails to parse. - let field_name = syn::Ident::new(&seg.name, seg.span); + let field_name = seg.name.ident(); let parse_error = quote!({ #log::warn!(#field_name = ?#error, "Failed to parse dynamic parameter"); #Outcome::Forward(#data) From a868cab597dc904f99e6c936482a02e5fd1998de Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 4 Nov 2020 09:59:47 -0800 Subject: [PATCH 38/60] use `in_scope` for errors Signed-off-by: Eliza Weisman --- core/lib/src/error.rs | 22 +++++++++++++--------- core/lib/src/rocket.rs | 6 ++---- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/core/lib/src/error.rs b/core/lib/src/error.rs index ee9658456e..68c244a38b 100644 --- a/core/lib/src/error.rs +++ b/core/lib/src/error.rs @@ -168,14 +168,16 @@ impl Drop for Error { match *self.kind() { ErrorKind::Bind(ref error) => { - let span = error_span!("bind_error", "Rocket failed to bind network socket to given address/port."); - info!(parent: &span, %error); - panic!("aborting due to binding o error"); + error_span!("bind_error", "Rocket failed to bind network socket to given address/port.").in_scope(|| { + info!(%error); + panic!("aborting due to binding o error"); + }); } ErrorKind::Io(ref error) => { - let span = error_span!("io_error", "Rocket failed to launch due to an I/O error."); - info!(parent: &span, %error); - panic!("aborting due to i/o error"); + error_span!("io_error", "Rocket failed to launch due to an I/O error.").in_scope(|| { + info!(%error); + panic!("aborting due to i/o error"); + }); } ErrorKind::Collision(ref collisions) => { let span = error_span!("collisions", "Rocket failed to launch due to the following routing collisions:"); @@ -189,6 +191,7 @@ impl Drop for Error { } ErrorKind::FailedFairings(ref failures) => { let span = error_span!("fairing_error", "Rocket failed to launch due to failing fairings:"); + let _enter = span.enter(); for fairing in failures { info!(%fairing); } @@ -196,9 +199,10 @@ impl Drop for Error { panic!("aborting due to launch fairing failure"); } ErrorKind::Runtime(ref error) => { - let span = error_span!("runtime_error", "An error occured in the runtime:"); - info!(%error); - panic!("aborting due to runtime failure"); + error_span!("runtime_error", "An error occured in the runtime:").in_scope(|| { + info!(%error); + panic!("aborting due to runtime failure"); + }); } } } diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index cf71b230b2..c025a1aca1 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -170,8 +170,7 @@ impl Rocket { let old_route = route.clone(); let route = route.map_base(|old| format!("{}{}", base, old)) .unwrap_or_else(|error| { - let span = error_span!("malformed_uri", route = %old_route, "Route has a malformed URI."); - error!(parent: &span, %error); + error!(route = %old_route, %error, "Route has a malformed URI."); panic!("Invalid route URI."); }); @@ -552,8 +551,7 @@ impl Rocket { } Either::Left((Err(error), server)) => { // Error setting up ctrl-c signal. Let the user know. - let span = warn_span!("graceful_shutdown_error", "Failed to enable `ctrl-c` graceful signal shutdown."); - info!(parent: &span, %error); + warn!(%error, "Failed to enable `ctrl-c` graceful signal shutdown."); server.await } // Server shut down before Ctrl-C; return the result. From cb1b024b3492efd4fb421c30123d8429573f1a22 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 4 Nov 2020 10:29:02 -0800 Subject: [PATCH 39/60] allow adding directives to Rocket's filter Signed-off-by: Eliza Weisman --- core/lib/src/trace.rs | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 64aef2d0a8..f7c585cb21 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -211,7 +211,7 @@ pub use tracing::{ }; pub use tracing_futures::Instrument; -pub use tracing_subscriber::registry; +pub use tracing_subscriber::{registry, EnvFilter as Filter}; /// A prelude for working with `tracing` in Rocket applications. pub mod prelude { @@ -288,7 +288,12 @@ impl<'de> Deserialize<'de> for LogLevel { /// configure it to filter spans and events based on the logging level /// specified in the Rocket config. /// -/// For example: +/// Additional [filtering directives][dirs] may be added to the returned filter +/// layer in order to enable or disable specific targets. +/// +/// # Examples +/// +/// Using Rocket's filtering with a custom `tracing` subscriber: /// /// ``` /// # type MySubscriber = tracing_subscriber::registry::Registry; @@ -311,11 +316,37 @@ impl<'de> Deserialize<'de> for LogLevel { /// } /// ``` /// +/// Adding additional directives to Rocket's default filter: +/// +/// ``` +/// #[rocket::launch] +/// fn rocket() -> rocket::Rocket { +/// use rocket::trace::prelude::*; +/// +/// let figment = rocket::Config::figment(); +/// let config = rocket::Config::from(&figment); +/// +/// // Use Rocket's default filter for the configured log level... +/// let trace_filter = rocket::trace::filter_layer(config.log_level) +/// // ...but always enable the `DEBUG` level for `my_crate`. +/// .add_directive("my_crate=debug".parse().unwrap()) +/// +/// // Build a custom `tracing` subscriber... +/// rocket::trace::registry() +/// // ...using the default Rocket log formatter... +/// .with(rocket::trace::logging_layer()) +/// // ...but replacing the default filter with our customized one. +/// .with(trace_filter) +/// .init(); +/// +/// rocket::custom(figment) +/// // ... +/// } +/// ``` +/// /// [`Layer`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html -pub fn filter_layer(level: LogLevel) -> impl Layer -where - S: tracing::Subscriber, -{ +/// [dirs]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives +pub fn filter_layer(level: LogLevel) -> Filter { let filter_str = match level { LogLevel::Critical => "warn,rocket::launch=info,hyper=off,rustls=off", LogLevel::Normal => "info,hyper=off,rustls=off", From 6de118051e57e0bd87cf0f6f61e4a98739081add Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 4 Nov 2020 10:29:20 -0800 Subject: [PATCH 40/60] make examples a little fancier Signed-off-by: Eliza Weisman --- examples/trace_env_logger/Rocket.toml | 4 ++++ examples/trace_subscriber/src/main.rs | 25 +++++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 examples/trace_env_logger/Rocket.toml diff --git a/examples/trace_env_logger/Rocket.toml b/examples/trace_env_logger/Rocket.toml new file mode 100644 index 0000000000..43be321148 --- /dev/null +++ b/examples/trace_env_logger/Rocket.toml @@ -0,0 +1,4 @@ +[debug] +# Disable CLI colors and emoji when using `env_logger`; it doesn't format them +# as nicely as Rocket does. +cli_colors = false \ No newline at end of file diff --git a/examples/trace_subscriber/src/main.rs b/examples/trace_subscriber/src/main.rs index 5514a61ff9..8f82ce70b7 100644 --- a/examples/trace_subscriber/src/main.rs +++ b/examples/trace_subscriber/src/main.rs @@ -2,14 +2,26 @@ use rocket::trace; #[rocket::get("/hello//")] fn hello(name: String, age: u8) -> String { - trace::info!(?name, age, "saying hello to"); - format!("Hello, {} year old named {}!", age, name) + trace::info!("saying hello..."); + greet(name, Some(age)) } #[rocket::get("/hello/")] fn hi(name: String) -> String { trace::info!(?name, "saying hi to"); - name + greet(name, None) +} + +#[trace::instrument(level = "info")] +fn greet(name: String, age: Option) -> String { + trace::debug!(?age); + if let Some(age) = age { + trace::debug!("found an age, saying hello"); + format!("Hello, {} year old named {}!", age, name) + } else { + trace::debug!("no age, just saying hi..."); + name + } } #[rocket::launch] @@ -19,9 +31,14 @@ fn rocket() -> rocket::Rocket { let figment = rocket::Config::figment(); let config = rocket::Config::from(&figment); + // Use Rocket's default log filter... + let filter = rocket::trace::filter_layer(config.log_level) + // ...but enable the debug level for the app crate. + .add_directive("trace_subscriber=debug".parse().unwrap()); + rocket::trace::registry() .with(tracing_subscriber::fmt::layer()) - .with(rocket::trace::filter_layer(config.log_level)) + .with(filter) .init(); rocket::custom(figment).mount("/", rocket::routes![hello, hi]) From 4ddaedf4d7b020d691d9049e599c7a2df038f4dd Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 4 Nov 2020 10:39:21 -0800 Subject: [PATCH 41/60] fix trace in contrib Signed-off-by: Eliza Weisman --- contrib/lib/src/serve.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contrib/lib/src/serve.rs b/contrib/lib/src/serve.rs index 2407d06684..4916ab4e24 100644 --- a/contrib/lib/src/serve.rs +++ b/contrib/lib/src/serve.rs @@ -288,9 +288,10 @@ impl StaticFiles { let path = path.as_ref(); if !path.is_dir() { - let span = error_span!("`StaticFiles` supplied with invalid path"); - info!(parent: &span, %path = Paint::white(path.display(), "Path is not a directory")); - panic!("refusing to continue due to invalid static files path"); + error_span!("`StaticFiles` supplied with invalid path").in_scope(|| { + info!(path = %Paint::white(path.display()), "Path is not a directory"); + panic!("refusing to continue due to invalid static files path"); + }); } StaticFiles { root: path.into(), options, rank: Self::DEFAULT_RANK } From 0025a58d90ee575796f758abb6ffa3fa491c612c Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 4 Nov 2020 10:39:34 -0800 Subject: [PATCH 42/60] propagate spans to spawned threads Signed-off-by: Eliza Weisman --- contrib/lib/src/databases.rs | 10 +++++++++- core/lib/src/rocket.rs | 5 ++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/contrib/lib/src/databases.rs b/contrib/lib/src/databases.rs index d7d948b78d..be3d4fdc4a 100644 --- a/contrib/lib/src/databases.rs +++ b/contrib/lib/src/databases.rs @@ -377,6 +377,7 @@ use rocket::http::Status; use rocket::tokio::sync::{OwnedSemaphorePermit, Semaphore, Mutex}; use rocket::tokio::time::timeout; +use rocket::trace::prelude::*; use self::r2d2::ManageConnection; @@ -738,6 +739,11 @@ pub struct Connection { async fn run_blocking(job: F) -> R where F: FnOnce() -> R + Send + 'static, R: Send + 'static, { + let span = tracing::Span::current(); + let job = move || { + let _enter = span.enter(); + job() + }; match tokio::task::spawn_blocking(job).await { Ok(ret) => ret, Err(e) => match e.try_into_panic() { @@ -836,7 +842,9 @@ impl Drop for Connection { let permit = self.permit.take(); tokio::spawn(async move { let mut connection = connection.lock_owned().await; + let span = tracing::Span::current(); tokio::task::spawn_blocking(move || { + let _e = span.enter(); if let Some(conn) = connection.take() { drop(conn); } @@ -845,7 +853,7 @@ impl Drop for Connection { // released after the connection is. drop(permit); }) - }); + }.in_current_span()); } } diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index c025a1aca1..5b6e0768c7 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -6,12 +6,11 @@ use figment::Figment; use tokio::sync::mpsc; use futures::future::FutureExt; -use crate::trace; +use crate::trace::{self, PaintExt, prelude::*}; use crate::config::Config; use crate::catcher::Catcher; use crate::router::{Router, Route}; use crate::fairing::{Fairing, Fairings}; -use crate::trace::PaintExt; use crate::shutdown::Shutdown; use crate::http::uri::Origin; use crate::error::{Error, ErrorKind}; @@ -296,7 +295,7 @@ impl Rocket { let mut fairings = std::mem::replace(&mut self.fairings, Fairings::new()); let rocket = fairings.attach(fairing, self).await; (rocket, fairings) - }; + }.in_current_span(); // TODO: Reuse a single thread to run all attach fairings. let (rocket, mut fairings) = match tokio::runtime::Handle::try_current() { From f15d432ab37337dab9de7c524b0a8ead68b599a3 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 4 Nov 2020 10:45:33 -0800 Subject: [PATCH 43/60] put all request processing inside request span Signed-off-by: Eliza Weisman --- core/lib/src/server.rs | 90 ++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/core/lib/src/server.rs b/core/lib/src/server.rs index 7be28e20ff..7672562f36 100644 --- a/core/lib/src/server.rs +++ b/core/lib/src/server.rs @@ -1,10 +1,11 @@ use std::io; use std::sync::Arc; +use std::task::{Context, Poll}; use futures::stream::StreamExt; use futures::future::{Future, BoxFuture}; use tokio::sync::oneshot; -use tracing_futures::Instrument; +use tracing::instrument::{Instrument, Instrumented}; use yansi::Paint; use crate::Rocket; @@ -50,7 +51,7 @@ async fn hyper_service_fn( let mut req = match req_res { Ok(req) => req, Err(e) => { - error!("Bad incoming request: {}", e); + error!(error = %e, "Bad incoming request"); // TODO: We don't have a request to pass in, so we just // fabricate one. This is weird. We should let the user know // that we failed to parse a request (by invoking some special @@ -61,14 +62,20 @@ async fn hyper_service_fn( } }; - // Retrieve the data from the hyper body. - let mut data = Data::from_hyp(h_body).await; + let span = info_span!("request", "{}", req); + async { + // Retrieve the data from the hyper body. + let mut data = Data::from_hyp(h_body).await; - // Dispatch the request to get a response, then write that response out. - let token = rocket.preprocess_request(&mut req, &mut data).await; - let r = rocket.dispatch(token, &mut req, data).await; - rocket.send_response(r, tx).await; - }.instrument(info_span!("connection", from = %h_addr, "{}Connection", Paint::emoji("📡 ")))); + // Dispatch the request to get a response, then write that response out. + let token = rocket.preprocess_request(&mut req, &mut data).await; + let r = rocket.dispatch(token, &mut req, data).await; + rocket.send_response(r, tx).await; + } + .instrument(span) + .await + + }.in_current_span()); // Receive the response written to `tx` by the task above. rx.await.map_err(|e| io::Error::new(io::ErrorKind::Other, e)) @@ -186,30 +193,27 @@ impl Rocket { request: &'r Request<'s>, data: Data ) -> Response<'r> { - async move { - // Remember if the request is `HEAD` for later body stripping. - let was_head_request = request.method() == Method::Head; + // Remember if the request is `HEAD` for later body stripping. + let was_head_request = request.method() == Method::Head; - // Route the request and run the user's handlers. - let mut response = self.route_and_process(request, data).await; + // Route the request and run the user's handlers. + let mut response = self.route_and_process(request, data).await; - // Add a default 'Server' header if it isn't already there. - // TODO: If removing Hyper, write out `Date` header too. - if !response.headers().contains("Server") { - response.set_header(Header::new("Server", "Rocket")); - } + // Add a default 'Server' header if it isn't already there. + // TODO: If removing Hyper, write out `Date` header too. + if !response.headers().contains("Server") { + response.set_header(Header::new("Server", "Rocket")); + } - // Run the response fairings. - self.fairings.handle_response(request, &mut response).await; + // Run the response fairings. + self.fairings.handle_response(request, &mut response).await; - // Strip the body if this is a `HEAD` request. - if was_head_request { - response.strip_body(); - } + // Strip the body if this is a `HEAD` request. + if was_head_request { + response.strip_body(); + } - response - }.instrument(info_span!("request", "{}", request)) - .await + response } /// Route the request and process the outcome to eventually get a response. @@ -375,10 +379,16 @@ impl Rocket { let service = hyper::make_service_fn(move |conn: &::Connection| { let rocket = rocket.clone(); let remote = conn.remote_addr().unwrap_or_else(|| ([0, 0, 0, 0], 0).into()); + let span = info_span!("connection", from = %remote, "{}Connection", Paint::emoji("📡 ")); async move { - Ok::<_, std::convert::Infallible>(hyper::service_fn(move |req| { + let service = hyper::service_fn(move |req| { hyper_service_fn(rocket.clone(), remote, req) - })) + }); + let service = InstrumentedService { + span, + service, + }; + Ok::<_, std::convert::Infallible>(service) } }); @@ -403,3 +413,23 @@ impl Rocket { .map_err(|e| Error::new(ErrorKind::Runtime(Box::new(e)))) } } + +struct InstrumentedService { + span: tracing::Span, + service: S +} + +impl, R> hyper::Service for InstrumentedService { + type Response = S::Response; + type Future = Instrumented; + type Error = S::Error; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + let _e = self.span.enter(); + self.service.poll_ready(cx) + } + + fn call(&mut self, request: R) -> Self::Future { + self.service.call(request).instrument(self.span.clone()) + } +} \ No newline at end of file From dd77f67cf0f332d4468e88474ca0897edac0dddc Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Tue, 3 Nov 2020 23:08:12 -0800 Subject: [PATCH 44/60] add another span in 'Fairings::pretty_print_counts' --- core/lib/src/fairing/fairings.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/lib/src/fairing/fairings.rs b/core/lib/src/fairing/fairings.rs index 64e7ba5c71..b3276b4dfc 100644 --- a/core/lib/src/fairing/fairings.rs +++ b/core/lib/src/fairing/fairings.rs @@ -94,7 +94,8 @@ impl Fairings { pub fn pretty_print_counts(&self) { if !self.all_fairings.is_empty() { - info!("{}{}:", Paint::emoji("📡 "), Paint::magenta("Fairings")); + let span = info_span!("fairings", "{}{}:", Paint::emoji("📡 "), Paint::magenta("Fairings")); + let _e = span.enter(); self.info_for("launch", &self.launch); self.info_for("request", &self.request); self.info_for("response", &self.response); From d5006de54696beee920e5109da3428c9211aa5e2 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Tue, 3 Nov 2020 23:08:32 -0800 Subject: [PATCH 45/60] feat: use rocket::trace::info in fairings, manual_routes examples --- examples/fairings/src/main.rs | 11 ++++++----- examples/manual_routes/src/main.rs | 7 ++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/examples/fairings/src/main.rs b/examples/fairings/src/main.rs index 849eb4f6d8..d8b30518da 100644 --- a/examples/fairings/src/main.rs +++ b/examples/fairings/src/main.rs @@ -6,6 +6,7 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use rocket::{Request, State, Data, Response}; use rocket::fairing::{AdHoc, Fairing, Info, Kind}; use rocket::http::{Method, ContentType, Status}; +use rocket::trace::info; struct Token(i64); @@ -67,20 +68,20 @@ fn rocket() -> rocket::Rocket { .mount("/", routes![hello, token]) .attach(Counter::default()) .attach(AdHoc::on_attach("Token State", |rocket| async { - println!("Adding token managed state..."); + info!("Adding token managed state..."); match rocket.figment().extract_inner("token") { Ok(value) => Ok(rocket.manage(Token(value))), Err(_) => Err(rocket) } })) .attach(AdHoc::on_launch("Launch Message", |_| { - println!("Rocket is about to launch!"); + info!("Rocket is about to launch!"); })) .attach(AdHoc::on_request("PUT Rewriter", |req, _| { Box::pin(async move { - println!(" => Incoming request: {}", req); + info!("Incoming request: {}", req); if req.uri().path() == "/" { - println!(" => Changing method to `PUT`."); + info!("Changing method to `PUT`."); req.set_method(Method::Put); } }) @@ -88,7 +89,7 @@ fn rocket() -> rocket::Rocket { .attach(AdHoc::on_response("Response Rewriter", |req, res| { Box::pin(async move { if req.uri().path() == "/" { - println!(" => Rewriting response body."); + info!("Rewriting response body."); res.set_sized_body(None, Cursor::new("Hello, fairings!")); } }) diff --git a/examples/manual_routes/src/main.rs b/examples/manual_routes/src/main.rs index aa16a3bb89..72ca292b13 100644 --- a/examples/manual_routes/src/main.rs +++ b/examples/manual_routes/src/main.rs @@ -11,6 +11,7 @@ use rocket::handler::{Handler, Outcome, HandlerFuture}; use rocket::catcher::{Catcher, ErrorHandlerFuture}; use rocket::outcome::{try_outcome, IntoOutcome}; use rocket::tokio::fs::File; +use rocket::trace::info; fn forward<'r>(_req: &'r Request, data: Data) -> HandlerFuture<'r> { Box::pin(async move { Outcome::forward(data) }) @@ -42,7 +43,7 @@ fn echo_url<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> { fn upload<'r>(req: &'r Request, data: Data) -> HandlerFuture<'r> { Box::pin(async move { if !req.content_type().map_or(false, |ct| ct.is_plain()) { - println!(" => Content-Type of upload must be text/plain. Ignoring."); + info!("Content-Type of upload must be text/plain. Ignoring."); return Outcome::failure(Status::BadRequest); } @@ -52,10 +53,10 @@ fn upload<'r>(req: &'r Request, data: Data) -> HandlerFuture<'r> { return Outcome::from(req, format!("OK: {} bytes uploaded.", n)); } - println!(" => Failed copying."); + info!("Failed copying."); Outcome::failure(Status::InternalServerError) } else { - println!(" => Couldn't open file: {:?}", file.unwrap_err()); + info!("Couldn't open file: {:?}", file.unwrap_err()); Outcome::failure(Status::InternalServerError) } }) From 4bf5f337f35c9b560f414a5a89b6ea947ec56861 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Wed, 4 Nov 2020 20:30:43 -0800 Subject: [PATCH 46/60] remove trailing whitespace --- contrib/lib/src/serve.rs | 2 +- contrib/lib/src/templates/metadata.rs | 2 +- contrib/lib/src/templates/mod.rs | 2 +- core/lib/src/error.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/lib/src/serve.rs b/contrib/lib/src/serve.rs index 4916ab4e24..1a1a86a192 100644 --- a/contrib/lib/src/serve.rs +++ b/contrib/lib/src/serve.rs @@ -288,7 +288,7 @@ impl StaticFiles { let path = path.as_ref(); if !path.is_dir() { - error_span!("`StaticFiles` supplied with invalid path").in_scope(|| { + error_span!("`StaticFiles` supplied with invalid path").in_scope(|| { info!(path = %Paint::white(path.display()), "Path is not a directory"); panic!("refusing to continue due to invalid static files path"); }); diff --git a/contrib/lib/src/templates/metadata.rs b/contrib/lib/src/templates/metadata.rs index 40cf87e5c2..cf935a5804 100644 --- a/contrib/lib/src/templates/metadata.rs +++ b/contrib/lib/src/templates/metadata.rs @@ -95,7 +95,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for Metadata<'a> { .unwrap_or_else(|| { error_span!("missing_fairing", "Uninitialized template context: missing fairing.").in_scope(|| { info!("To use templates, you must attach `Template::fairing()`."); - info!("See the `Template` documentation for more information."); + info!("See the `Template` documentation for more information."); }); request::Outcome::Failure((Status::InternalServerError, ())) }) diff --git a/contrib/lib/src/templates/mod.rs b/contrib/lib/src/templates/mod.rs index 49a7f2bd3b..1fbf59cffd 100644 --- a/contrib/lib/src/templates/mod.rs +++ b/contrib/lib/src/templates/mod.rs @@ -379,7 +379,7 @@ impl Template { let ctxt = rocket.state::().map(ContextManager::context).or_else(|| { warn_span!("missing_fairing", "Uninitialized template context: missing fairing.").in_scope(|| { info!("To use templates, you must attach `Template::fairing()`."); - info!("See the `Template` documentation for more information."); + info!("See the `Template` documentation for more information."); }); None })?; diff --git a/core/lib/src/error.rs b/core/lib/src/error.rs index 68c244a38b..b2e2d996f0 100644 --- a/core/lib/src/error.rs +++ b/core/lib/src/error.rs @@ -168,7 +168,7 @@ impl Drop for Error { match *self.kind() { ErrorKind::Bind(ref error) => { - error_span!("bind_error", "Rocket failed to bind network socket to given address/port.").in_scope(|| { + error_span!("bind_error", "Rocket failed to bind network socket to given address/port.").in_scope(|| { info!(%error); panic!("aborting due to binding o error"); }); From 50a224d0119ac929365c5018e3a6541750bde42f Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Wed, 11 Nov 2020 22:49:05 -0800 Subject: [PATCH 47/60] fix(codegen): log parameter names in a way that will work for any kind of namesource, keyword or not --- core/codegen/src/attribute/route.rs | 30 +++++++++++++---------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/core/codegen/src/attribute/route.rs b/core/codegen/src/attribute/route.rs index 4be14c297e..2fc6d13100 100644 --- a/core/codegen/src/attribute/route.rs +++ b/core/codegen/src/attribute/route.rs @@ -137,10 +137,10 @@ fn param_expr(seg: &Segment, ident: &syn::Ident, ty: &syn::Type) -> TokenStream #Outcome::Forward(#data) }); + let name = seg.name.name(); // Returned when a dynamic parameter fails to parse. - let field_name = seg.name.ident(); let parse_error = quote!({ - #log::warn!(#field_name = ?#error, "Failed to parse dynamic parameter"); + #log::warn!(parameter = #name, error = ?#error, "Failed to parse dynamic parameter"); #Outcome::Forward(#data) }); @@ -234,14 +234,13 @@ fn query_exprs(route: &Route) -> Option { let name = segment.name.name(); let matcher = match segment.kind { Kind::Single => { - let field_name = syn::Ident::new(name, segment.span); quote_spanned! { span => (_, #name, __v) => { #[allow(unreachable_patterns, unreachable_code)] let __v = match <#ty as #request::FromFormValue>::from_form_value(__v) { #_Ok(__v) => __v, #_Err(__e) => { - #log::warn!(#field_name = ?__e, "Failed to parse"); + #log::warn!(parameter = %#name, error = ?__e, "Failed to parse"); return #Outcome::Forward(#data); } }; @@ -269,19 +268,16 @@ fn query_exprs(route: &Route) -> Option { } }; }, - Kind::Multi => { - let field_name = syn::Ident::new(name, segment.span); - quote_spanned! { span => - #[allow(non_snake_case)] - let #ident = match <#ty as #request::FromQuery>::from_query(#Query(&#trail)) { - #_Ok(__v) => __v, - #_Err(__e) => { - #log::warn!(#field_name = ?__e, "Failed to parse"); - return #Outcome::Forward(#data); - } - }; - } - } + Kind::Multi => quote_spanned! { span => + #[allow(non_snake_case)] + let #ident = match <#ty as #request::FromQuery>::from_query(#Query(&#trail)) { + #_Ok(__v) => __v, + #_Err(__e) => { + #log::warn!(parameter = %#name, error = ?__e, "Failed to parse"); + return #Outcome::Forward(#data); + } + }; + }, Kind::Static => quote!() }; From 55faca7e3b6ab002deda00796965b2cd327e32c3 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Wed, 11 Nov 2020 22:50:35 -0800 Subject: [PATCH 48/60] fix compile error in manual_routes example --- examples/manual_routes/src/main.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/examples/manual_routes/src/main.rs b/examples/manual_routes/src/main.rs index 72ca292b13..bf4df6d997 100644 --- a/examples/manual_routes/src/main.rs +++ b/examples/manual_routes/src/main.rs @@ -47,17 +47,19 @@ fn upload<'r>(req: &'r Request, data: Data) -> HandlerFuture<'r> { return Outcome::failure(Status::BadRequest); } - let file = File::create(env::temp_dir().join("upload.txt")).await; - if let Ok(file) = file { - if let Ok(n) = data.open(2.mebibytes()).stream_to(file).await { - return Outcome::from(req, format!("OK: {} bytes uploaded.", n)); + match File::create(env::temp_dir().join("upload.txt")).await { + Ok(file) => { + if let Ok(n) = data.open(2.mebibytes()).stream_to(file).await { + return Outcome::from(req, format!("OK: {} bytes uploaded.", n)); + } + + info!("Failed copying."); + Outcome::failure(Status::InternalServerError) + } + Err(error) => { + info!("Couldn't open file, {:?}", error); + Outcome::failure(Status::InternalServerError) } - - info!("Failed copying."); - Outcome::failure(Status::InternalServerError) - } else { - info!("Couldn't open file: {:?}", file.unwrap_err()); - Outcome::failure(Status::InternalServerError) } }) } From 732a613733cc4490635caf072d86c1dedfd2fc80 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Wed, 11 Nov 2020 23:37:04 -0800 Subject: [PATCH 49/60] fix(test): missing semicolon --- core/lib/src/trace.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index f7c585cb21..513ae87259 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -329,7 +329,7 @@ impl<'de> Deserialize<'de> for LogLevel { /// // Use Rocket's default filter for the configured log level... /// let trace_filter = rocket::trace::filter_layer(config.log_level) /// // ...but always enable the `DEBUG` level for `my_crate`. -/// .add_directive("my_crate=debug".parse().unwrap()) +/// .add_directive("my_crate=debug".parse().unwrap()); /// /// // Build a custom `tracing` subscriber... /// rocket::trace::registry() From 8dba51d3eb18e49771f7cb878a7ad1b3e6cb8cb9 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Thu, 12 Nov 2020 22:00:39 -0800 Subject: [PATCH 50/60] feat: rework log output formatting * Multiple levels of indentation * Print context when the "root" span has changed or when "going deeper" into indentation than the previous message --- core/lib/src/server.rs | 2 +- core/lib/src/trace.rs | 83 +++++++++++++++++++++++------------------- 2 files changed, 47 insertions(+), 38 deletions(-) diff --git a/core/lib/src/server.rs b/core/lib/src/server.rs index 7672562f36..156fd964a3 100644 --- a/core/lib/src/server.rs +++ b/core/lib/src/server.rs @@ -432,4 +432,4 @@ impl, R> hyper::Service for InstrumentedService { fn call(&mut self, request: R) -> Self::Future { self.service.call(request).instrument(self.span.clone()) } -} \ No newline at end of file +} diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 513ae87259..6b332636ba 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -199,7 +199,7 @@ use tracing_subscriber::{ }; use std::fmt::{self, Write}; -use std::sync::atomic::{AtomicU64, Ordering::{Acquire, Release}}; +use std::sync::atomic::{AtomicU16, AtomicU64, Ordering::{Acquire, Release}}; use std::str::FromStr; use yansi::Paint; @@ -348,7 +348,7 @@ impl<'de> Deserialize<'de> for LogLevel { /// [dirs]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives pub fn filter_layer(level: LogLevel) -> Filter { let filter_str = match level { - LogLevel::Critical => "warn,rocket::launch=info,hyper=off,rustls=off", + LogLevel::Critical => "warn,hyper=off,rustls=off", LogLevel::Normal => "info,hyper=off,rustls=off", LogLevel::Debug => "trace", LogLevel::Off => "off", @@ -411,7 +411,7 @@ where // `stdout().write_str(...)`, so that logs are captured by libtest's test // capturing. .with_test_writer() - .event_format(EventFormat { last_id: AtomicU64::new(0) }) + .event_format(EventFormat { last_id: AtomicU64::new(0), last_depth: AtomicU16::new(0) }) } pub(crate) fn try_init(level: LogLevel, colors: bool) -> bool { @@ -459,6 +459,7 @@ impl PaintExt for Paint<&str> { struct EventFormat { last_id: AtomicU64, + last_depth: AtomicU16, } impl FormatEvent for EventFormat @@ -473,40 +474,47 @@ where writer: &mut dyn fmt::Write, event: &tracing::Event<'_>, ) -> fmt::Result { - let mut seen = false; - let id = if let Some(span) = cx.lookup_current() { - let id = span.id(); - if id.into_u64() != self.last_id.load(Acquire) { - cx.visit_spans(|span| { - if seen { - write!(writer, " {} ", Paint::default("=>").bold())?; - } - let meta = span.metadata(); - let exts = span.extensions(); - if let Some(fields) = exts.get::>() { - // If the span has a human-readable message, print that - // instead of the span's name (so that we can get nice emojis). - if meta.fields().iter().any(|field| field.name() == "message") { - with_meta(writer, meta, &fields.fields)?; - } else { - with_meta(writer, meta, format_args!("{} {}", Paint::new(span.name()).bold(), &fields.fields))?; - } + let mut indent = String::new(); + let mut root_id = None; + let mut root_changed = false; + let mut depth = 0; + + let prev_depth = self.last_depth.load(Acquire); + cx.visit_spans(|span| { + let is_root = root_id.is_none(); + if is_root { + root_id = Some(span.id()); + if span.id().into_u64() != self.last_id.load(Acquire) { + root_changed = true; + } + } + depth += 1; + + if root_changed || depth > prev_depth { + if !indent.is_empty() { + write!(writer, "{}{} ", indent, Paint::default("=>").bold())?; + } + let meta = span.metadata(); + let exts = span.extensions(); + if let Some(fields) = exts.get::>() { + // If the span has a human-readable message, print that + // instead of the span's name (so that we can get nice emojis). + if meta.fields().iter().any(|field| field.name() == "message") { + with_meta(writer, meta, &fields.fields)?; } else { - with_meta(writer, span.metadata(), Paint::new(span.name()).bold())?; + with_meta(writer, meta, format_args!("{} {}", Paint::new(span.name()).bold(), &fields.fields))?; } - seen = true; - Ok(()) - })?; - } else { - seen = true; + } else { + with_meta(writer, span.metadata(), Paint::new(span.name()).bold())?; + } } - Some(id) - } else { - None - }; - if seen { - write!(writer, " {} ", Paint::default("=>").bold())?; + indent.push_str(" "); + Ok(()) + })?; + + if !indent.is_empty() { + write!(writer, "{}{} ", indent, Paint::default("=>").bold())?; } with_meta( @@ -517,10 +525,11 @@ where event, }, )?; - if let Some(id) = id { - self.last_id.store(id.into_u64(), Release); + if root_changed { + self.last_id.store(root_id.unwrap().into_u64(), Release); } - Ok(()) + self.last_depth.store(depth, Release); + Ok(()) } } @@ -618,4 +627,4 @@ fn try_init_log(filter: LogLevel) -> Result<(), impl std::error::Error> { LogLevel::Off => return Ok(()), }; builder.init() -} \ No newline at end of file +} From 39ae1d581c6f197dc33398a5b710c6424884206c Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Thu, 12 Nov 2020 22:00:42 -0800 Subject: [PATCH 51/60] feat: print request IDs The hash algorithm is bespoke and weird; not a great final answer. --- core/lib/src/trace.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 6b332636ba..623699212c 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -480,6 +480,18 @@ where let mut depth = 0; let prev_depth = self.last_depth.load(Acquire); + + // FIXME: explain or replace this + fn hash_id(id: tracing::Id) -> u16 { + let id = id.into_u64(); + let [z, y, x, w, v, u, t, s] = id.to_le_bytes(); + + (z as u16) ^ (y as u16) ^ + (x as u16) ^ (w as u16) ^ + (v as u16) << 8 ^ (u as u16) << 8 ^ + (t as u16) << 8 ^ (s as u16) << 8 + } + cx.visit_spans(|span| { let is_root = root_id.is_none(); if is_root { @@ -500,7 +512,11 @@ where // If the span has a human-readable message, print that // instead of the span's name (so that we can get nice emojis). if meta.fields().iter().any(|field| field.name() == "message") { - with_meta(writer, meta, &fields.fields)?; + if span.name() == "request" { + with_meta(writer, meta, format_args!("[{:x}] {}", hash_id(span.id()), &fields.fields))?; + } else { + with_meta(writer, meta, &fields.fields)?; + } } else { with_meta(writer, meta, format_args!("{} {}", Paint::new(span.name()).bold(), &fields.fields))?; } From 910ad1c30d98f3013a0eb65177bfc16c3d79aa5f Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Thu, 12 Nov 2020 22:08:22 -0800 Subject: [PATCH 52/60] feat/experiment: remove connection span, making 'request' the top-level span instead --- core/lib/src/server.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/lib/src/server.rs b/core/lib/src/server.rs index 156fd964a3..d8ba95ea86 100644 --- a/core/lib/src/server.rs +++ b/core/lib/src/server.rs @@ -379,15 +379,10 @@ impl Rocket { let service = hyper::make_service_fn(move |conn: &::Connection| { let rocket = rocket.clone(); let remote = conn.remote_addr().unwrap_or_else(|| ([0, 0, 0, 0], 0).into()); - let span = info_span!("connection", from = %remote, "{}Connection", Paint::emoji("📡 ")); async move { let service = hyper::service_fn(move |req| { hyper_service_fn(rocket.clone(), remote, req) }); - let service = InstrumentedService { - span, - service, - }; Ok::<_, std::convert::Infallible>(service) } }); From 63e0ef3d8538624fa021e1ac48ada4c4db62de06 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Mon, 16 Nov 2020 11:29:01 -0800 Subject: [PATCH 53/60] handle `log` metadata properly Signed-off-by: Eliza Weisman --- core/lib/src/trace.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 623699212c..ec4903fda4 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -533,9 +533,18 @@ where write!(writer, "{}{} ", indent, Paint::default("=>").bold())?; } + // If the `log` compatibility feature is enabled, normalize metadata + // from `log` records that's emitted as fields. + #[cfg(feature = "log")] + let normalized_meta = tracing_log::NormalizeEvent::normalized_metadata(event); + #[cfg(feature = "log")] + let event_meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata()); + #[cfg(not(feature = "log"))] + let event_meta = event.metadata(); + with_meta( writer, - event.metadata(), + event_meta, DisplayFields { fmt: cx, event, From 8ac9625911621a028ca840f9c9960fa0a57a6e3f Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sun, 15 Nov 2020 14:00:46 -0800 Subject: [PATCH 54/60] use tracing instead of log in http --- core/http/Cargo.toml | 2 +- core/http/src/listener.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/http/Cargo.toml b/core/http/Cargo.toml index b4bd1c317f..c0a167bf27 100644 --- a/core/http/Cargo.toml +++ b/core/http/Cargo.toml @@ -31,7 +31,7 @@ state = "0.4" tokio-rustls = { version = "0.14.0", optional = true } tokio = { version = "0.2.9", features = ["sync", "tcp", "time"] } unicode-xid = "0.2" -log = "0.4" +tracing = "0.1.19" ref-cast = "1.0" uncased = "0.9" parking_lot = "0.11" diff --git a/core/http/src/listener.rs b/core/http/src/listener.rs index fe998abe14..6e2ba72065 100644 --- a/core/http/src/listener.rs +++ b/core/http/src/listener.rs @@ -8,7 +8,7 @@ use std::time::Duration; use hyper::server::accept::Accept; -use log::{debug, error}; +use tracing::{debug, error}; use tokio::time::Delay; use tokio::io::{AsyncRead, AsyncWrite}; From 2f098a7f06e73966d6b7b97a025c62153d622c6f Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Tue, 17 Nov 2020 07:43:29 -0800 Subject: [PATCH 55/60] call Paint::disable in more cases of 'tracing' initialization failure --- core/lib/src/trace.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index ec4903fda4..e3f1bdc699 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -416,6 +416,7 @@ where pub(crate) fn try_init(level: LogLevel, colors: bool) -> bool { if level == LogLevel::Off { + Paint::disable(); return false; } @@ -434,14 +435,23 @@ pub(crate) fn try_init(level: LogLevel, colors: bool) -> bool { // has already set a `log` logger. In that case, don't try to set up a // `tracing` subscriber as well --- instead, Rocket's `tracing` events // will be recorded as `log` records. + Paint::disable(); return false; } - tracing::subscriber::set_global_default(tracing_subscriber::registry() + let success = tracing::subscriber::set_global_default(tracing_subscriber::registry() .with(logging_layer()) .with(filter_layer(level)) ) - .is_ok() + .is_ok(); + + if !success { + // Something else already registered a tracing subscriber; + // don't assume we can use Paint. + Paint::disable(); + } + + success } pub trait PaintExt { From 2cfad83345e72fbb647ad06a16bc6257740455a1 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Tue, 17 Nov 2020 19:38:03 -0800 Subject: [PATCH 56/60] add new log level 'support' * The "Support" log level is like "Critical" ('warn' level or above), but additionally logs any events with the 'rocket::suport' target at 'info' level or above. --- core/lib/src/config/config.rs | 16 ++++++++-------- core/lib/src/trace.rs | 10 ++++++++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/core/lib/src/config/config.rs b/core/lib/src/config/config.rs index 02dc723980..8f362eb47d 100644 --- a/core/lib/src/config/config.rs +++ b/core/lib/src/config/config.rs @@ -275,16 +275,16 @@ impl Config { pub(crate) fn pretty_print(&self, profile: &Profile) { use crate::trace::PaintExt; - let span = info_span!("configured", "{}Configured for {}", Paint::emoji("🔧 "), profile); + let span = info_span!(target: "rocket::support", "configured", "{}Configured for {}", Paint::emoji("🔧 "), profile); let _e = span.enter(); - info!(address = %&self.address); - info!(port = %&self.port); - info!(workers = %self.workers); - info!(log_level = %self.log_level); - info!(secret_key = ?&self.secret_key); - info!(limits = %&self.limits); - info!(cli_colors = %&self.cli_colors); + info!(target: "rocket::support", address = %&self.address); + info!(target: "rocket::support", port = %&self.port); + info!(target: "rocket::support", workers = %self.workers); + info!(target: "rocket::support", log_level = %self.log_level); + info!(target: "rocket::support", secret_key = ?&self.secret_key); + info!(target: "rocket::support", limits = %&self.limits); + info!(target: "rocket::support", cli_colors = %&self.cli_colors); let ka = self.keep_alive; if ka > 0 { diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index e3f1bdc699..12fded2681 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -224,6 +224,9 @@ pub mod prelude { pub enum LogLevel { /// Only shows errors and warnings: `"critical"`. Critical, + /// Shows errors, warnings, and some informational messages that are likely + /// to be relevant when troubleshooting (such as configuration). + Support, /// Shows everything except debug and trace information: `"normal"`. Normal, /// Shows everything: `"debug"`. @@ -236,6 +239,7 @@ impl LogLevel { fn as_str(&self) -> &str { match self { LogLevel::Critical => "critical", + LogLevel::Support => "support", LogLevel::Normal => "normal", LogLevel::Debug => "debug", LogLevel::Off => "off", @@ -249,10 +253,11 @@ impl FromStr for LogLevel { fn from_str(s: &str) -> Result { let level = match &*s.to_ascii_lowercase() { "critical" => LogLevel::Critical, + "support" => LogLevel::Support, "normal" => LogLevel::Normal, "debug" => LogLevel::Debug, "off" => LogLevel::Off, - _ => return Err("a log level (off, debug, normal, critical)"), + _ => return Err("a log level (off, debug, normal, support, critical)"), }; Ok(level) @@ -349,6 +354,7 @@ impl<'de> Deserialize<'de> for LogLevel { pub fn filter_layer(level: LogLevel) -> Filter { let filter_str = match level { LogLevel::Critical => "warn,hyper=off,rustls=off", + LogLevel::Support => "warn,rocket::support=info,hyper=off,rustls=off", LogLevel::Normal => "info,hyper=off,rustls=off", LogLevel::Debug => "trace", LogLevel::Off => "off", @@ -650,7 +656,7 @@ fn try_init_log(filter: LogLevel) -> Result<(), impl std::error::Error> { // will already be collected. .ignore_all(vec!["rocket", "hyper", "tracing::span"]); let builder = match filter { - LogLevel::Critical => builder + LogLevel::Critical | LogLevel::Support => builder .ignore_crate("rustls") // Set the max level for all `log` records to Warn. Rocket's // `launch` events will be collected by the native `tracing` From d69340cc8bad1451516f3dfacc805f93a3d865e4 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Wed, 18 Nov 2020 20:06:30 -0800 Subject: [PATCH 57/60] fixup: add string form to description of 'support' to make consistent with other log levels --- core/lib/src/trace.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 12fded2681..53f55f1aaa 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -225,7 +225,7 @@ pub enum LogLevel { /// Only shows errors and warnings: `"critical"`. Critical, /// Shows errors, warnings, and some informational messages that are likely - /// to be relevant when troubleshooting (such as configuration). + /// to be relevant when troubleshooting such as configuration: `"support"`. Support, /// Shows everything except debug and trace information: `"normal"`. Normal, From 0d506c14f774a61ef9e961acebb512d64439b236 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Wed, 18 Nov 2020 20:31:27 -0800 Subject: [PATCH 58/60] Use 'in_scope' instead of 'enter' in most places. Calling 'enter()' directly inside an async function doesn't work properly, but that's easy to forget. The 'in_scope()' API can't be misused in this way, it automatically perfectly delineates which messages the span actually includes, and it saves several 'let' statements. --- contrib/lib/src/databases.rs | 16 ++-- contrib/lib/src/helmet/helmet.rs | 12 +-- contrib/lib/src/templates/context.rs | 10 +-- contrib/lib/src/templates/fairing.rs | 65 +++++++------- .../lib/src/templates/handlebars_templates.rs | 8 +- contrib/lib/src/templates/tera_templates.rs | 67 +++++++-------- core/lib/src/config/config.rs | 84 ++++++++++--------- core/lib/src/data/data_stream.rs | 20 ++--- core/lib/src/error.rs | 29 ++++--- core/lib/src/fairing/fairings.rs | 10 +-- core/lib/src/rocket.rs | 76 ++++++++--------- 11 files changed, 197 insertions(+), 200 deletions(-) diff --git a/contrib/lib/src/databases.rs b/contrib/lib/src/databases.rs index be3d4fdc4a..b4919a502c 100644 --- a/contrib/lib/src/databases.rs +++ b/contrib/lib/src/databases.rs @@ -740,11 +740,7 @@ async fn run_blocking(job: F) -> R where F: FnOnce() -> R + Send + 'static, R: Send + 'static, { let span = tracing::Span::current(); - let job = move || { - let _enter = span.enter(); - job() - }; - match tokio::task::spawn_blocking(job).await { + match tokio::task::spawn_blocking(move || span.in_scope(|| job())).await { Ok(ret) => ret, Err(e) => match e.try_into_panic() { Ok(panic) => std::panic::resume_unwind(panic), @@ -755,8 +751,9 @@ async fn run_blocking(job: F) -> R macro_rules! dberr { ($target:literal, $msg:literal, $db_name:expr, $efmt:literal, $error:expr, $rocket:expr) => ({ - let span = rocket::trace::error_span!($target, "database {} error for pool named `{}`", $msg, $db_name); - rocket::trace::error!(parent: &span, $efmt, $error); + rocket::trace::error_span!($target, "database {} error for pool named `{}`", $msg, $db_name).in_scope(|| { + rocket::trace::error!($efmt, $error); + }); return Err($rocket); }); } @@ -843,8 +840,7 @@ impl Drop for Connection { tokio::spawn(async move { let mut connection = connection.lock_owned().await; let span = tracing::Span::current(); - tokio::task::spawn_blocking(move || { - let _e = span.enter(); + tokio::task::spawn_blocking(move || span.in_scope(|| { if let Some(conn) = connection.take() { drop(conn); } @@ -852,7 +848,7 @@ impl Drop for Connection { // Explicitly dropping the permit here so that it's only // released after the connection is. drop(permit); - }) + })); }.in_current_span()); } } diff --git a/contrib/lib/src/helmet/helmet.rs b/contrib/lib/src/helmet/helmet.rs index 5dee537a9f..535806ded8 100644 --- a/contrib/lib/src/helmet/helmet.rs +++ b/contrib/lib/src/helmet/helmet.rs @@ -171,11 +171,10 @@ impl SpaceHelmet { for policy in self.policies.values() { let name = policy.name(); if response.headers().contains(name.as_str()) { - let span = warn_span!( + warn_span!( "Space Helmet: response contains already contains this header", header = %name, - ); - warn!(parent: &span, "Refusing to overwrite existing header."); + ).in_scope(|| warn!("Refusing to overwrite existing header.")); continue } @@ -209,9 +208,10 @@ impl Fairing for SpaceHelmet { && rocket.figment().profile() != rocket::Config::DEBUG_PROFILE && !self.is_enabled::() { - let span = warn_span!("Space Helmet: deploying with TLS without enabling HSTS."); - warn!(parent: &span, "Enabling default HSTS policy."); - info!(parent: &span, "To disable this warning, configure an HSTS policy."); + warn_span!("Space Helmet: deploying with TLS without enabling HSTS.").in_scope(|| { + warn!("Enabling default HSTS policy."); + info!("To disable this warning, configure an HSTS policy."); + }); self.force_hsts.store(true, Ordering::Relaxed); } } diff --git a/contrib/lib/src/templates/context.rs b/contrib/lib/src/templates/context.rs index fe96840ff4..6d2b8eba36 100644 --- a/contrib/lib/src/templates/context.rs +++ b/contrib/lib/src/templates/context.rs @@ -28,11 +28,11 @@ impl Context { for path in glob::glob(glob_path).unwrap().filter_map(Result::ok) { let (name, data_type_str) = split_path(&root, &path); if let Some(info) = templates.get(&*name) { - let span = warn_span!("invalid_template_path", "Template name '{}' does not have a unique path.", name); - let _enter = span.enter(); - info!(path = ?info.path, "Existing"); - info!(path = ?path, "Additiona"); - warn!("Using existing path for template '{}'.", name); + warn_span!("invalid_template_path", "Template name '{}' does not have a unique path.", name).in_scope(|| { + info!(path = ?info.path, "Existing"); + info!(path = ?path, "Additiona"); + warn!("Using existing path for template '{}'.", name); + }); continue; } diff --git a/contrib/lib/src/templates/fairing.rs b/contrib/lib/src/templates/fairing.rs index f2c925d6ba..44a8dd32f1 100644 --- a/contrib/lib/src/templates/fairing.rs +++ b/contrib/lib/src/templates/fairing.rs @@ -63,9 +63,10 @@ mod context { let watcher = match watcher { Ok(watcher) => Some(Mutex::new((watcher, rx))), Err(error) => { - let span = warn_span!("Failed to enable live template reloading", %error); - debug!(parent: &span, reload_error = ?error); - warn!(parent: &span, "Live template reloading is unavailable."); + warn_span!("Failed to enable live template reloading", %error).in_scope(|| { + debug!(reload_error = ?error); + warn!("Live template reloading is unavailable."); + }); None } }; @@ -101,24 +102,24 @@ mod context { } if changed { - let span = info_span!("Change detected: reloading templates."); - let _entered = span.enter(); - let mut ctxt = self.context_mut(); - if let Some(mut new_ctxt) = Context::initialize(ctxt.root.clone()) { - match callback(&mut new_ctxt.engines) { - Ok(()) => { - *ctxt = new_ctxt; - debug!("reloaded!"); + info_span!("Change detected: reloading templates.").in_scope(|| { + let mut ctxt = self.context_mut(); + if let Some(mut new_ctxt) = Context::initialize(ctxt.root.clone()) { + match callback(&mut new_ctxt.engines) { + Ok(()) => { + *ctxt = new_ctxt; + debug!("reloaded!"); + } + Err(error) => { + warn!(%error, "The template customization callback returned an error"); + warn!("The existing templates will remain active."); + } } - Err(error) => { - warn!(%error, "The template customization callback returned an error"); - warn!("The existing templates will remain active."); - } - } - } else { - warn!("An error occurred while reloading templates."); - warn!("The existing templates will remain active."); - }; + } else { + warn!("An error occurred while reloading templates."); + warn!("The existing templates will remain active."); + }; + }); } }); } @@ -173,19 +174,19 @@ impl Fairing for TemplateFairing { use rocket::{trace::PaintExt, yansi::Paint}; use crate::templates::Engines; - let span = info_span!("templating", "{}{}", Paint::emoji("📐 "), Paint::magenta("Templating:")); - let _enter = span.enter(); - match (self.callback)(&mut ctxt.engines) { - Ok(()) => { - info!(directory = %Paint::white(&root)); - info!(engines = ?Paint::white(Engines::ENABLED_EXTENSIONS)); - Ok(rocket.manage(ContextManager::new(ctxt))) - } - Err(error) => { - error!(%error, "The template customization callback returned an error"); - Err(rocket) + info_span!("templating", "{}{}", Paint::emoji("📐 "), Paint::magenta("Templating:")).in_scope(|| { + match (self.callback)(&mut ctxt.engines) { + Ok(()) => { + info!(directory = %Paint::white(&root)); + info!(engines = ?Paint::white(Engines::ENABLED_EXTENSIONS)); + Ok(rocket.manage(ContextManager::new(ctxt))) + } + Err(error) => { + error!(%error, "The template customization callback returned an error"); + Err(rocket) + } } - } + }) } None => Err(rocket), } diff --git a/contrib/lib/src/templates/handlebars_templates.rs b/contrib/lib/src/templates/handlebars_templates.rs index c2fde92782..af17e191b0 100644 --- a/contrib/lib/src/templates/handlebars_templates.rs +++ b/contrib/lib/src/templates/handlebars_templates.rs @@ -12,10 +12,10 @@ impl Engine for Handlebars<'static> { for &(name, info) in templates { let path = &info.path; if let Err(e) = hb.register_template_file(name, path) { - let span = error_span!("template_error", template = %name, "Error in Handlebars template",); - let _enter = span.enter(); - info!(template.error = %e); - info!(template.path = %path.to_string_lossy()); + error_span!("template_error", template = %name, "Error in Handlebars template",).in_scope(|| { + info!(template.error = %e); + info!(template.path = %path.to_string_lossy()); + }); return None; } } diff --git a/contrib/lib/src/templates/tera_templates.rs b/contrib/lib/src/templates/tera_templates.rs index 011ea805b5..88eb2c0f75 100644 --- a/contrib/lib/src/templates/tera_templates.rs +++ b/contrib/lib/src/templates/tera_templates.rs @@ -21,13 +21,13 @@ impl Engine for Tera { // Finally try to tell Tera about all of the templates. if let Err(e) = tera.add_template_files(tera_templates) { - let span = error_span!("Failed to initialize Tera templating."); - - let mut error = Some(&e as &dyn Error); - while let Some(err) = error { - error!(parent: &span, error = %err); - error = err.source(); - } + error_span!("Failed to initialize Tera templating.").in_scope(|| { + let mut error = Some(&e as &dyn Error); + while let Some(err) = error { + error!(error = %err); + error = err.source(); + } + }); None } else { @@ -36,36 +36,37 @@ impl Engine for Tera { } fn render(&self, name: &str, context: C) -> Option { - let span = info_span!("Rendering Tera template", template = %name); - if self.get_template(name).is_err() { - error!(parent: &span, "Tera template '{}' does not exist.", name); - return None; - }; - - let tera_ctx = match Context::from_serialize(context) { - Ok(ctx) => ctx, - Err(_) => { - error!( - parent: &span, - "Error generating context when rendering Tera template '{}'.", - name, - ); + info_span!("Rendering Tera template", template = %name).in_scope(|| { + if self.get_template(name).is_err() { + error!("Tera template '{}' does not exist.", name); return None; - } - }; + }; - match Tera::render(self, name, &tera_ctx) { - Ok(string) => Some(string), - Err(e) => { - let span = error_span!("Error rendering Tera template", template = %name); - let mut error = Some(&e as &dyn Error); - while let Some(err) = error { - error!(parent: &span, error = %err); - error = err.source(); + let tera_ctx = match Context::from_serialize(context) { + Ok(ctx) => ctx, + Err(_) => { + error!( + "Error generating context when rendering Tera template '{}'.", + name, + ); + return None; } + }; - None + match Tera::render(self, name, &tera_ctx) { + Ok(string) => Some(string), + Err(e) => { + error_span!("Error rendering Tera template", template = %name).in_scope(|| { + let mut error = Some(&e as &dyn Error); + while let Some(err) = error { + error!(error = %err); + error = err.source(); + } + }); + + None + } } - } + }) } } diff --git a/core/lib/src/config/config.rs b/core/lib/src/config/config.rs index 8f362eb47d..bd85784b43 100644 --- a/core/lib/src/config/config.rs +++ b/core/lib/src/config/config.rs @@ -219,10 +219,11 @@ impl Config { let figment = Figment::from(&provider); for (key, replacement) in Self::DEPRECATED_KEYS { if figment.find_value(key).is_ok() { - let span = warn_span!("deprecated_key", "found value for deprecated config key {}", Paint::white(key)); - if let Some(new_key) = replacement { - info!(parent: &span, "key has been by replaced by {}", Paint::white(new_key)); - } + warn_span!("deprecated_key", "found value for deprecated config key {}", Paint::white(key)).in_scope(|| { + if let Some(new_key) = replacement { + info!("key has been by replaced by {}", Paint::white(new_key)); + } + }); } } @@ -236,13 +237,15 @@ impl Config { if config.secret_key.is_zero() { if figment.profile() != Self::DEBUG_PROFILE { crate::trace::try_init(LogLevel::Debug, true); - let span = error_span!("secrets enabled in `release` without `secret_key`"); - info!(parent: &span, "disable `secrets` feature or configure a `secret_key`"); - panic!("aborting due to configuration error(s)") + error_span!("secrets enabled in `release` without `secret_key`").in_scope(|| { + info!("disable `secrets` feature or configure a `secret_key`"); + panic!("aborting due to configuration error(s)") + }); } else { - let span = warn_span!("secrets enabled in `debug` without `secret_key`"); - info!(parent: &span, "disable `secrets` feature or configure a `secret_key`"); - info!(parent: &span, "this becomes a hard error in `release`"); + warn_span!("secrets enabled in `debug` without `secret_key`").in_scope(|| { + info!("disable `secrets` feature or configure a `secret_key`"); + info!("this becomes a hard error in `release`"); + }); } // in debug, generate a key for a bit more security @@ -275,28 +278,27 @@ impl Config { pub(crate) fn pretty_print(&self, profile: &Profile) { use crate::trace::PaintExt; - let span = info_span!(target: "rocket::support", "configured", "{}Configured for {}", Paint::emoji("🔧 "), profile); - let _e = span.enter(); - - info!(target: "rocket::support", address = %&self.address); - info!(target: "rocket::support", port = %&self.port); - info!(target: "rocket::support", workers = %self.workers); - info!(target: "rocket::support", log_level = %self.log_level); - info!(target: "rocket::support", secret_key = ?&self.secret_key); - info!(target: "rocket::support", limits = %&self.limits); - info!(target: "rocket::support", cli_colors = %&self.cli_colors); + info_span!(target: "rocket::support", "configured", "{}Configured for {}", Paint::emoji("🔧 "), profile).in_scope(|| { + info!(target: "rocket::support", address = %&self.address); + info!(target: "rocket::support", port = %&self.port); + info!(target: "rocket::support", workers = %self.workers); + info!(target: "rocket::support", log_level = %self.log_level); + info!(target: "rocket::support", secret_key = ?&self.secret_key); + info!(target: "rocket::support", limits = %&self.limits); + info!(target: "rocket::support", cli_colors = %&self.cli_colors); - let ka = self.keep_alive; - if ka > 0 { - info!(keep_alive = %Paint::default(format!("{}s", ka)).bold()); - } else { - info!(keep_alive = %Paint::default("disabled").bold()); - } + let ka = self.keep_alive; + if ka > 0 { + info!(target: "rocket::support", keep_alive = %Paint::default(format!("{}s", ka)).bold()); + } else { + info!(target: "rocket::support", keep_alive = %Paint::default("disabled").bold()); + } - match self.tls_enabled() { - true => info!(tls = %Paint::default("enabled").bold()), - false => info!(tls = %Paint::default("disabled").bold()), - } + match self.tls_enabled() { + true => info!(target: "rocket::support", tls = %Paint::default("enabled").bold()), + false => info!(target: "rocket::support", tls = %Paint::default("disabled").bold()), + } + }); } } @@ -320,19 +322,19 @@ pub fn pretty_print_error(error: figment::Error) { crate::trace::try_init(LogLevel::Debug, true); for e in error { - let span = error_span!("config_error", "{}", e.kind); - - if let (Some(ref profile), Some(ref md)) = (&e.profile, &e.metadata) { - if !e.path.is_empty() { - let key = md.interpolate(profile, &e.path); - info!(parent: &span, key = %Paint::white(&key)); + error_span!("config_error", "{}", e.kind).in_scope(|| { + if let (Some(ref profile), Some(ref md)) = (&e.profile, &e.metadata) { + if !e.path.is_empty() { + let key = md.interpolate(profile, &e.path); + info!(key = %Paint::white(&key)); + } } - } - if let Some(ref md) = e.metadata { - if let Some(ref source) = md.source { - info!(parent: &span, "in {} {}", Paint::white(source), md.name); + if let Some(ref md) = e.metadata { + if let Some(ref source) = md.source { + info!("in {} {}", Paint::white(source), md.name); + } } - } + }); } } diff --git a/core/lib/src/data/data_stream.rs b/core/lib/src/data/data_stream.rs index 9406b12377..a6773c2c5a 100644 --- a/core/lib/src/data/data_stream.rs +++ b/core/lib/src/data/data_stream.rs @@ -118,17 +118,17 @@ impl AsyncRead for DataStream { cx: &mut Context<'_>, buf: &mut [u8] ) -> Poll> { - let span = trace_span!("DataStream::poll_read()"); - - if self.buffer.limit() > 0 { - trace!(parent: &span, "DataStream::buffer_read()"); - match Pin::new(&mut self.buffer).poll_read(cx, buf) { - Poll::Ready(Ok(0)) => { /* fall through */ }, - poll => return poll, + trace_span!("DataStream::poll_read()").in_scope(|| { + if self.buffer.limit() > 0 { + trace!("DataStream::buffer_read()"); + match Pin::new(&mut self.buffer).poll_read(cx, buf) { + Poll::Ready(Ok(0)) => { /* fall through */ }, + poll => return poll, + } } - } - trace!(parent: &span, "DataStream::stream_read()"); - Pin::new(&mut self.stream).poll_read(cx, buf) + trace!("DataStream::stream_read()"); + Pin::new(&mut self.stream).poll_read(cx, buf) + }) } } diff --git a/core/lib/src/error.rs b/core/lib/src/error.rs index b2e2d996f0..3fe82a7fb4 100644 --- a/core/lib/src/error.rs +++ b/core/lib/src/error.rs @@ -170,39 +170,38 @@ impl Drop for Error { ErrorKind::Bind(ref error) => { error_span!("bind_error", "Rocket failed to bind network socket to given address/port.").in_scope(|| { info!(%error); - panic!("aborting due to binding o error"); }); + panic!("aborting due to binding o error"); } ErrorKind::Io(ref error) => { error_span!("io_error", "Rocket failed to launch due to an I/O error.").in_scope(|| { info!(%error); - panic!("aborting due to i/o error"); }); + panic!("aborting due to i/o error"); } ErrorKind::Collision(ref collisions) => { - let span = error_span!("collisions", "Rocket failed to launch due to the following routing collisions:"); - let _enter = span.enter(); - for &(ref a, ref b) in collisions { - info!("{} {} {}", a, Paint::red("collides with").italic(), b); - } + error_span!("collisions", "Rocket failed to launch due to the following routing collisions:").in_scope(|| { + for &(ref a, ref b) in collisions { + info!("{} {} {}", a, Paint::red("collides with").italic(), b); + } - info!("Note: Collisions can usually be resolved by ranking routes."); + info!("Note: Collisions can usually be resolved by ranking routes."); + }); panic!("route collisions detected"); } ErrorKind::FailedFairings(ref failures) => { - let span = error_span!("fairing_error", "Rocket failed to launch due to failing fairings:"); - let _enter = span.enter(); - for fairing in failures { - info!(%fairing); - } - + error_span!("fairing_error", "Rocket failed to launch due to failing fairings:").in_scope(|| { + for fairing in failures { + info!(%fairing); + } + }); panic!("aborting due to launch fairing failure"); } ErrorKind::Runtime(ref error) => { error_span!("runtime_error", "An error occured in the runtime:").in_scope(|| { info!(%error); - panic!("aborting due to runtime failure"); }); + panic!("aborting due to runtime failure"); } } } diff --git a/core/lib/src/fairing/fairings.rs b/core/lib/src/fairing/fairings.rs index b3276b4dfc..fc58d9ced0 100644 --- a/core/lib/src/fairing/fairings.rs +++ b/core/lib/src/fairing/fairings.rs @@ -94,11 +94,11 @@ impl Fairings { pub fn pretty_print_counts(&self) { if !self.all_fairings.is_empty() { - let span = info_span!("fairings", "{}{}:", Paint::emoji("📡 "), Paint::magenta("Fairings")); - let _e = span.enter(); - self.info_for("launch", &self.launch); - self.info_for("request", &self.request); - self.info_for("response", &self.response); + info_span!("fairings", "{}{}:", Paint::emoji("📡 "), Paint::magenta("Fairings")).in_scope(|| { + self.info_for("launch", &self.launch); + self.info_for("request", &self.request); + self.info_for("response", &self.response); + }); } } } diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 5b6e0768c7..e00abb3304 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -151,33 +151,32 @@ impl Rocket { /// ``` #[inline] pub fn mount>>(mut self, base: &str, routes: R) -> Self { - let span = info_span!("mounting", at = %Paint::blue(&base), "{} Mounting", Paint::emoji("🛰 ")); - let _e = span.enter(); - - let base_uri = Origin::parse_owned(base.to_string()) - .unwrap_or_else(|e| { - error!(uri = %Paint::white(base), "Invalid mount point URI"); - panic!("Error: {}", e); - }); - - if base_uri.query().is_some() { - error!("Mount point '{}' contains query string.", base); - panic!("Invalid mount point."); - } - - for route in routes.into() { - let old_route = route.clone(); - let route = route.map_base(|old| format!("{}{}", base, old)) - .unwrap_or_else(|error| { - error!(route = %old_route, %error, "Route has a malformed URI."); - panic!("Invalid route URI."); + info_span!("mounting", at = %Paint::blue(&base), "{} Mounting", Paint::emoji("🛰 ")).in_scope(|| { + let base_uri = Origin::parse_owned(base.to_string()) + .unwrap_or_else(|e| { + error!(uri = %Paint::white(base), "Invalid mount point URI"); + panic!("Error: {}", e); }); - info!(%route); - self.router.add(route); - } + if base_uri.query().is_some() { + error!("Mount point '{}' contains query string.", base); + panic!("Invalid mount point."); + } - self + for route in routes.into() { + let old_route = route.clone(); + let route = route.map_base(|old| format!("{}{}", base, old)) + .unwrap_or_else(|error| { + error!(route = %old_route, %error, "Route has a malformed URI."); + panic!("Invalid route URI."); + }); + + info!(%route); + self.router.add(route); + } + + self + }) } /// Registers all of the catchers in the supplied vector. @@ -205,23 +204,22 @@ impl Rocket { /// ``` #[inline] pub fn register(mut self, catchers: Vec) -> Self { - let span = info_span!("catchers", "{}{}", Paint::emoji("👾 "), Paint::magenta("Catchers:")); - let _enter = span.enter(); - - for catcher in catchers { - info!(%catcher); - - let existing = match catcher.code { - Some(code) => self.catchers.insert(code, catcher), - None => self.default_catcher.replace(catcher) - }; - - if let Some(existing) = existing { - warn!("Replacing existing '{}' catcher.", existing); + info_span!("catchers", "{}{}", Paint::emoji("👾 "), Paint::magenta("Catchers:")).in_scope(|| { + for catcher in catchers { + info!(%catcher); + + let existing = match catcher.code { + Some(code) => self.catchers.insert(code, catcher), + None => self.default_catcher.replace(catcher) + }; + + if let Some(existing) = existing { + warn!("Replacing existing '{}' catcher.", existing); + } } - } - self + self + }) } /// Add `state` to the state managed by this instance of Rocket. From c19799b9668debcd504bbd0f9c493fbf4a9e16e3 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Fri, 20 Nov 2020 23:51:51 -0800 Subject: [PATCH 59/60] fix: under feature="log", skip formatting of fields whose names start with "log." --- core/lib/src/trace.rs | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 53f55f1aaa..3d0f97aa60 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -411,6 +411,10 @@ where }) .delimited(", ") .display_messages(); + + #[cfg(feature = "log")] + let field_format = skip_log::SkipLogFields(field_format); + tracing_subscriber::fmt::layer() .fmt_fields(field_format) // Configure the formatter to use `print!` rather than @@ -669,3 +673,62 @@ fn try_init_log(filter: LogLevel) -> Result<(), impl std::error::Error> { }; builder.init() } + +#[cfg(feature = "log")] +mod skip_log { + use tracing::field::Field; + use tracing_subscriber::field::{MakeVisitor, RecordFields, Visit, VisitFmt, VisitOutput}; + + use super::*; + + // This struct along with SkipLogVisitor suppress the output of any fields + // whose names start with "log."; it's used under cfg(feature="log") to clean + // up the output. This complements NormalizeEvent, which converts log fields + // into event metadata but does not remove the fields it used. + pub(crate) struct SkipLogFields(pub V); + + impl<'a, V: MakeVisitor<&'a mut (dyn Write + 'a)>> MakeVisitor<&'a mut (dyn Write + 'a)> for SkipLogFields { + type Visitor = SkipLogFields; + + fn make_visitor(&self, target: &'a mut dyn Write) -> Self::Visitor { + SkipLogFields(self.0.make_visitor(target)) + } + } + + macro_rules! forward_record_fns { + ($($fn_name:ident ( $type:ty ) ),*) => { + $( + fn $fn_name (&mut self, field: &Field, value: $type) { + if field.name().starts_with("log.") { return; } + self.0.$fn_name(field, value) + } + )* + }; + } + + impl Visit for SkipLogFields { + forward_record_fns!( + record_debug(&dyn std::fmt::Debug), + record_i64(i64), + record_u64(u64), + record_str(&str), + record_bool(bool) + ); + } + + impl VisitFmt for SkipLogFields { + fn writer(&mut self) -> &mut dyn Write { + self.0.writer() + } + } + + impl, O> VisitOutput for SkipLogFields { + fn visit(self, fields: &R) -> O where R: RecordFields, Self: Sized { + self.0.visit(fields) + } + + fn finish(self) -> O { + self.0.finish() + } + } +} From 85f2e5b16cbcc27c1ec78218e869dc48ad2c0771 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sat, 21 Nov 2020 00:07:02 -0800 Subject: [PATCH 60/60] fix: test the 'log' feature individually --- scripts/test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/test.sh b/scripts/test.sh index d5849bb2d4..4cc0470e68 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -112,6 +112,7 @@ elif [ "$1" = "--core" ]; then FEATURES=( secrets tls + log ) pushd "${CORE_LIB_ROOT}" > /dev/null 2>&1