From 1a38988cf63d7295abf10ef08b753f1f82787914 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Fri, 3 May 2024 17:12:16 -0700 Subject: [PATCH] wip: send all logs unedited to trace --- core/lib/Cargo.toml | 13 +++++- core/lib/src/config/config.rs | 2 +- core/lib/src/lib.rs | 1 + core/lib/src/rocket.rs | 6 +-- core/lib/src/trace.rs | 79 ++++++++++++++++++++++++++++++++++- 5 files changed, 95 insertions(+), 6 deletions(-) diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 094754b334..d4914ef57a 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -32,7 +32,7 @@ multiple_bound_locations = "allow" manual_range_contains = "allow" [features] -default = ["http2", "tokio-macros"] +default = ["http2", "tokio-macros", "trace"] http2 = ["hyper/http2", "hyper-util/http2"] http3-preview = ["s2n-quic", "s2n-quic-h3", "tls"] secrets = ["cookie/private", "cookie/key-expansion"] @@ -42,6 +42,7 @@ uuid = ["uuid_", "rocket_http/uuid"] tls = ["rustls", "tokio-rustls", "rustls-pemfile"] mtls = ["tls", "x509-parser"] tokio-macros = ["tokio/macros"] +trace = ["tracing-subscriber"] [dependencies] # Optional serialization dependencies. @@ -83,6 +84,16 @@ cookie = { version = "0.18", features = ["percent-encode"] } futures = { version = "0.3.30", default-features = false, features = ["std"] } state = "0.6" +# tracing +tracing = "0.1.40" +tracing-futures = { version = "0.2", default-features = false, features = ["std-future"] } + +[dependencies.tracing-subscriber] +version = "0.3.18" +optional = true +default-features = false +features = ["fmt", "env-filter", "tracing-log"] + [dependencies.rocket_codegen] version = "0.6.0-dev" path = "../codegen" diff --git a/core/lib/src/config/config.rs b/core/lib/src/config/config.rs index d234a89b29..b22bddde1e 100644 --- a/core/lib/src/config/config.rs +++ b/core/lib/src/config/config.rs @@ -374,7 +374,7 @@ impl Config { } launch_meta_!("shutdown: {}", self.shutdown.paint(VAL)); - launch_meta_!("log level: {}", self.log_level.paint(VAL)); + // launch_meta_!("log level: {}", self.log_level.paint(VAL)); launch_meta_!("cli colors: {}", self.cli_colors.paint(VAL)); // Check for now deprecated config values. diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index 8e56ddc604..b42203dcb1 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -120,6 +120,7 @@ pub use futures; pub use tokio; pub use figment; pub use time; +pub use tracing; #[doc(hidden)] #[macro_use] diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 8c46a9d2d0..5140c8237a 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -187,7 +187,7 @@ impl Rocket { pub fn custom(provider: T) -> Self { // We initialize the logger here so that logging from fairings and so on // are visible; we use the final config to set a max log-level in ignite - // crate::log::init_default(); + crate::trace::init(&Config::debug_default()); let rocket: Rocket = Rocket(Building { figment: Figment::from(provider), @@ -538,10 +538,10 @@ impl Rocket { self = Fairings::handle_ignite(self).await; self.fairings.audit().map_err(|f| ErrorKind::FailedFairings(f.to_vec()))?; - // Extract the configuration; initialize the logger. + // Extract the configuration; initialize default trace subscriber. #[allow(unused_mut)] let mut config = Config::try_from(&self.figment).map_err(ErrorKind::Config)?; - // crate::log::init(&config); + crate::trace::init(&config); // Check for safely configured secrets. #[cfg(feature = "secrets")] diff --git a/core/lib/src/trace.rs b/core/lib/src/trace.rs index 846a26a2ec..fce2e0914c 100644 --- a/core/lib/src/trace.rs +++ b/core/lib/src/trace.rs @@ -1,3 +1,14 @@ +use std::sync::OnceLock; + +use tracing::{Level, Subscriber}; +use tracing_subscriber::prelude::*; +use tracing_subscriber::filter::EnvFilter; +use tracing_subscriber::registry::LookupSpan; +use tracing_subscriber::{reload, Layer, Registry}; +use yansi::{Condition, Paint, Painted}; + +use crate::Config; + macro_rules! declare_macro { ($($name:ident),*) => ( $(declare_macro!([$] $name);)* @@ -6,10 +17,76 @@ macro_rules! declare_macro { ([$d:tt] $name:ident) => ( #[macro_export] macro_rules! $name { - ($d ($token:tt)*) => ({}) + ($d ($t:tt)*) => ({ + #[allow(unused_imports)] + use $crate::trace::PaintExt as _; + + $crate::tracing::event!($crate::tracing::Level::INFO, $d ($t)*); + }) } ); } declare_macro!(log, log_, launch_info, launch_info_, launch_meta, launch_meta_, error, error_, info, info_, trace, trace_, debug, debug_, warn, warn_); + +pub trait PaintExt: Sized { + fn emoji(self) -> Painted; +} + +impl PaintExt for &str { + /// Paint::masked(), but hidden on Windows due to broken output. See #1122. + fn emoji(self) -> Painted { + #[cfg(windows)] { Paint::new("").mask() } + #[cfg(not(windows))] { Paint::new(self).mask() } + } +} + +pub fn filter_layer(level: Level) -> EnvFilter { + let filter_str = match level { + Level::ERROR => "error,hyper=off,rustls=off", + Level::WARN => "warn,hyper=off,rustls=off", + Level::INFO => "info,hyper=off,rustls=off", + Level::DEBUG => "debug", + Level::TRACE => "trace", + }; + + EnvFilter::try_new(filter_str).expect("filter string must parse") +} + +pub fn fmt_layer LookupSpan<'span>>() -> impl Layer { + let layer = tracing_subscriber::fmt::layer(); + + #[cfg(not(test))] { layer } + #[cfg(test)] { layer.with_test_writer() } +} + +pub(crate) fn init(config: &Config) { + static HANDLE: OnceLock> = OnceLock::new(); + + // FIXME: Read the true level from `config`. + let level = Level::INFO; + + // Always disable colors if requested or if the stdout/err aren't TTYs. + let should_color = match config.cli_colors { + crate::config::CliColors::Always => Condition::ALWAYS, + crate::config::CliColors::Auto => Condition::DEFAULT, + crate::config::CliColors::Never => Condition::NEVER, + }; + + let (filter, reload_handle) = reload::Layer::new(filter_layer(level)); + let result = tracing_subscriber::registry() + .with(filter) + .with(fmt_layer()) + .try_init(); + + if result.is_ok() { + assert!(HANDLE.set(reload_handle).is_ok()); + yansi::whenever(should_color); + } else if let Some(handle) = HANDLE.get() { + assert!(handle.modify(|filter| *filter = filter_layer(level)).is_ok()); + yansi::whenever(should_color); + } else { + yansi::disable() + } +}