Skip to content

Commit

Permalink
wip: parse and propagate log level
Browse files Browse the repository at this point in the history
  • Loading branch information
SergioBenitez committed May 4, 2024
1 parent 109ae68 commit 2c9e01d
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 36 deletions.
2 changes: 1 addition & 1 deletion core/lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ futures = { version = "0.3.30", default-features = false, features = ["std"] }
state = "0.6"

# tracing
tracing = { version = "0.1.40", default-features = false, features = ["std"] }
tracing = { version = "0.1.40", default-features = false, features = ["std", "attributes"] }
# tracing-futures = { version = "0.2", default-features = false, features = ["std-future"] }

[dependencies.tracing-subscriber]
Expand Down
12 changes: 7 additions & 5 deletions core/lib/src/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use figment::providers::{Serialized, Env, Toml, Format};
use figment::value::{Map, Dict, magic::RelativePathBuf};
use serde::{Deserialize, Serialize};
use yansi::{Paint, Style, Color::Primary};
use tracing::{level_filters::LevelFilter, Level};

// use crate::log::PaintExt;
// use crate::config::{LogLevel, ShutdownConfig, Ident, CliColors};
Expand Down Expand Up @@ -123,8 +124,9 @@ pub struct Config {
pub secret_key: SecretKey,
/// Graceful shutdown configuration. **(default: [`ShutdownConfig::default()`])**
pub shutdown: ShutdownConfig,
// /// Max level to log. **(default: _debug_ `normal` / _release_ `critical`)**
// pub log_level: LogLevel,
/// Max level to log. **(default: _debug_ `info` / _release_ `error`)**
#[serde(with = "crate::trace::level")]
pub log_level: Option<Level>,
/// Whether to use colors and emoji when logging. **(default:
/// [`CliColors::Auto`])**
pub cli_colors: CliColors,
Expand Down Expand Up @@ -202,7 +204,7 @@ impl Config {
#[cfg(feature = "secrets")]
secret_key: SecretKey::zero(),
shutdown: ShutdownConfig::default(),
// log_level: LogLevel::Normal,
log_level: Some(Level::INFO),
cli_colors: CliColors::Auto,
__non_exhaustive: (),
}
Expand All @@ -226,7 +228,7 @@ impl Config {
pub fn release_default() -> Config {
Config {
profile: Self::RELEASE_PROFILE,
// log_level: LogLevel::Critical,
log_level: Some(Level::ERROR),
..Config::debug_default()
}
}
Expand Down Expand Up @@ -374,7 +376,7 @@ impl Config {
}

launch_meta_!("shutdown: {}", self.shutdown.paint(VAL));
// launch_meta_!("log level: {}", self.log_level.paint(VAL));
launch_meta_!("log level: {}", LevelFilter::from(self.log_level).paint(VAL));
launch_meta_!("cli colors: {}", self.cli_colors.paint(VAL));

// Check for now deprecated config values.
Expand Down
11 changes: 6 additions & 5 deletions core/lib/src/config/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use figment::{Figment, Profile};
use pretty_assertions::assert_eq;
use tracing::Level;

// use crate::log::LogLevel;
use crate::data::{Limits, ToByteUnit};
Expand Down Expand Up @@ -65,7 +66,7 @@ fn test_toml_file() {
workers: 20,
ident: ident!("Something Cool"),
keep_alive: 10,
// log_level: LogLevel::Off,
log_level: None,
cli_colors: CliColors::Never,
..Config::default()
});
Expand All @@ -75,7 +76,7 @@ fn test_toml_file() {
ident = "Something Else Cool"
workers = 20
keep_alive = 10
log_level = "off"
log_level = "trace"
cli_colors = 0
"#)?;

Expand All @@ -84,7 +85,7 @@ fn test_toml_file() {
workers: 20,
ident: ident!("Something Else Cool"),
keep_alive: 10,
// log_level: LogLevel::Off,
log_level: Some(Level::TRACE),
cli_colors: CliColors::Never,
..Config::default()
});
Expand All @@ -94,15 +95,15 @@ fn test_toml_file() {
[default]
workers = 20
keep_alive = 10
log_level = "off"
log_level = 2
cli_colors = 0
"#)?;

let config = Config::from(Config::figment());
assert_eq!(config, Config {
workers: 20,
keep_alive: 10,
// log_level: LogLevel::Off,
log_level: Some(Level::WARN),
cli_colors: CliColors::Never,
..Config::default()
});
Expand Down
3 changes: 2 additions & 1 deletion core/lib/src/rocket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ impl Rocket<Build> {
pub fn custom<T: Provider>(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::trace::init(&Config::debug_default());
crate::trace::init(None);

let rocket: Rocket<Build> = Rocket(Building {
figment: Figment::from(provider),
Expand Down Expand Up @@ -534,6 +534,7 @@ impl Rocket<Build> {
/// Ok(())
/// }
/// ```
#[tracing::instrument(target = "rocket", skip_all)]
pub async fn ignite(mut self) -> Result<Rocket<Ignite>, Error> {
self = Fairings::handle_ignite(self).await;
self.fairings.audit().map_err(|f| ErrorKind::FailedFairings(f.to_vec()))?;
Expand Down
50 changes: 50 additions & 0 deletions core/lib/src/trace/level.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use std::fmt;

use serde::{de, Serialize, Deserializer, Serializer};
use tracing::{level_filters::LevelFilter, Level};

pub fn serialize<S: Serializer>(level: &Option<Level>, s: S) -> Result<S::Ok, S::Error> {
LevelFilter::from(*level).to_string().serialize(s)
}

pub fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result<Option<Level>, D::Error> {
struct Visitor;

const E: &str = r#"one of "off", "error", "warn", "info", "debug", "trace", or a number 0-5"#;

impl<'de> de::Visitor<'de> for Visitor {
type Value = Option<Level>;

fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "expected {E}")
}

fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
v.try_into()
.map_err(|_| E::invalid_value(de::Unexpected::Signed(v), &E))
.and_then(|v| self.visit_u64(v))
}

fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
let filter = match v {
0 => LevelFilter::OFF,
1 => LevelFilter::ERROR,
2 => LevelFilter::WARN,
3 => LevelFilter::INFO,
4 => LevelFilter::DEBUG,
5 => LevelFilter::TRACE,
_ => return Err(E::invalid_value(de::Unexpected::Unsigned(v), &E)),
};

Ok(filter.into_level())
}

fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
v.parse::<LevelFilter>()
.map(|f| f.into_level())
.map_err(|_| E::invalid_value(de::Unexpected::Str(v), &E))
}
}

de.deserialize_map(Visitor)
}
26 changes: 17 additions & 9 deletions core/lib/src/trace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use rocket::Config;

#[cfg(feature = "trace")]
pub mod subscriber;
pub mod level;

pub trait PaintExt: Sized {
fn emoji(self) -> yansi::Painted<Self>;
Expand All @@ -16,27 +17,34 @@ impl PaintExt for &str {
}

macro_rules! declare_macro {
($($name:ident),*) => (
$(declare_macro!([$] $name);)*
($($name:ident $level:ident),* $(,)?) => (
$(declare_macro!([$] $name $level);)*
);

([$d:tt] $name:ident) => (
([$d:tt] $name:ident $level:ident) => (
#[macro_export]
macro_rules! $name {
($d ($t:tt)*) => ({
#[allow(unused_imports)]
use $crate::trace::PaintExt as _;

$crate::tracing::event!($crate::tracing::Level::INFO, $d ($t)*);
$crate::tracing::event!($crate::tracing::Level::$level, $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 fn init(_config: &Config) {
declare_macro!(
launch_info INFO, launch_info_ INFO,
launch_meta INFO, launch_meta_ INFO,
error ERROR, error_ ERROR,
info INFO, info_ INFO,
trace TRACE, trace_ TRACE,
debug DEBUG, debug_ DEBUG,
warn WARN, warn_ WARN,
);

pub fn init<'a, T: Into<Option<&'a Config>>>(_config: T) {
#[cfg(feature = "trace")]
subscriber::init(_config);
subscriber::init(_config.into());
}
35 changes: 20 additions & 15 deletions core/lib/src/trace/subscriber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::{reload, filter, Layer, Registry};
use yansi::{Condition, Paint, Painted};

use crate::config::CliColors;
use crate::Config;

pub trait PaintExt: Sized {
Expand All @@ -21,34 +22,38 @@ impl PaintExt for &str {
}
}

pub fn filter_layer(level: Level) -> filter::Targets {
pub fn filter_layer(level: impl Into<LevelFilter>) -> filter::Targets {
filter::Targets::new()
.with_default(level)
.with_default(level.into())
.with_target("rustls", LevelFilter::OFF)
.with_target("hyper", LevelFilter::OFF)
}

pub fn fmt_layer<S: Subscriber + for<'span> LookupSpan<'span>>() -> impl Layer<S> {
let layer = tracing_subscriber::fmt::layer();
tracing_subscriber::fmt::layer().with_test_writer()

#[cfg(not(test))] { layer }
#[cfg(test)] { layer.with_test_writer() }
// #[cfg(not(test))] { layer }
// #[cfg(test)] { layer.with_test_writer() }
}

pub(crate) fn init(config: &Config) {
pub(crate) fn init(config: Option<&Config>) {
static HANDLE: OnceLock<reload::Handle<filter::Targets, Registry>> = OnceLock::new();

// FIXME: Read the true level from `config`.
let level = Level::INFO;
// Do nothing if there's no config and we've already initialized.
if config.is_none() && HANDLE.get().is_some() {
return;
}

// 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 cli_colors = config.map(|c| c.cli_colors).unwrap_or(CliColors::Auto);
let should_color = match cli_colors {
CliColors::Always => Condition::ALWAYS,
CliColors::Auto => Condition::DEFAULT,
CliColors::Never => Condition::NEVER,
};

let (filter, reload_handle) = reload::Layer::new(filter_layer(level));
let log_level = config.map(|c| c.log_level).unwrap_or(Some(Level::INFO));
let (filter, reload_handle) = reload::Layer::new(filter_layer(log_level));
let result = tracing_subscriber::registry()
.with(filter)
.with(fmt_layer())
Expand All @@ -57,8 +62,8 @@ pub(crate) fn init(config: &Config) {
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());
} if let Some(handle) = HANDLE.get() {
assert!(handle.modify(|filter| *filter = filter_layer(log_level)).is_ok());
yansi::whenever(should_color);
} else {
yansi::disable()
Expand Down

0 comments on commit 2c9e01d

Please sign in to comment.