diff --git a/maid/client/cli/dispatch.rs b/maid/client/cli/dispatch.rs index 3e2710ea2c676289f8bf46e127976213ef681869..b908b31c9e70a7b70a591ba00b288105d138b6b2 100644 --- a/maid/client/cli/dispatch.rs +++ b/maid/client/cli/dispatch.rs @@ -9,12 +9,12 @@ use std::{fs::File, io::Write, path::Path, time::Duration}; pub(crate) fn clean() { if let Ok(_) = std::fs::remove_dir_all(".maid/temp") { - info!("{}", "Purged temp archives".green()) + info!("Purged temp archives") } match std::fs::remove_dir_all(".maid/cache") { - Ok(_) => info!("{}", "Emptied build cache".green()), - Err(_) => warn!("{}", "Build cache does not exist, cannot remove".yellow()), + Ok(_) => info!("Emptied build cache"), + Err(_) => warn!("Build cache does not exist, cannot remove"), }; } diff --git a/maid/client/cli/tasks.rs b/maid/client/cli/tasks.rs index 572d8f564cfc29f7f20d56b2da3512c26a1d633c..49d64ac4a2986593f348542979024aad91790388 100644 --- a/maid/client/cli/tasks.rs +++ b/maid/client/cli/tasks.rs @@ -36,7 +36,7 @@ impl List { }; let verbose = match log_level.unwrap() { - tracing::Level::ERROR => string!(), + tracing::Level::INFO => string!(), _ => string!(task.script), }; @@ -85,7 +85,7 @@ impl List { }; let verbose = match log_level.unwrap() { - tracing::Level::ERROR => string!(), + tracing::Level::INFO => string!(), _ => string!(task.script), }; diff --git a/maid/client/main.rs b/maid/client/main.rs index 58eebc453c688cd42d67a545a9b08e10784e62b7..588217d50c5d5840c25ac70a4d723086ae8147a2 100644 --- a/maid/client/main.rs +++ b/maid/client/main.rs @@ -8,12 +8,14 @@ mod structs; mod table; mod task; -use maid::log::{InfoLevel, Verbosity}; +use maid::log::{ + layer::prelude::*, + verbose::{InfoLevel, Verbosity}, +}; use clap::{Parser, ValueEnum}; use macros_rs::str; use std::path::Path; -use tracing_subscriber::{fmt, prelude::*}; macro_rules! dispatch { ($cli:expr, { $($flag:ident => $func:expr),+ $(,)? }) => {$( @@ -99,12 +101,12 @@ enum Project { fn main() { let cli = Cli::parse(); - let fmt_layer = fmt::layer().without_time(); - - tracing_subscriber::registry().with(cli.verbose.log_level_filter()).with(fmt_layer).init(); + let log_layer = MaidFormatLayer::new(); globals::init(); + tracing_subscriber::registry().with(cli.verbose.log_level_filter()).with(log_layer).init(); + dispatch!(cli, { init => cli::dispatch::init(), health => server::cli::connect(&cli.path), diff --git a/maid/client/table.rs b/maid/client/table.rs index ec03a8b1079c8c55b8ecb19edec948fb3ea96cc0..bc5e705e055eff5ec5f4c05914bc8da5993cd0be 100644 --- a/maid/client/table.rs +++ b/maid/client/table.rs @@ -12,33 +12,33 @@ pub fn create(values: Maidfile, args: &Vec, project: PathBuf) -> HashMap let mut table = HashMap::new(); let empty_env: BTreeMap = BTreeMap::new(); + trace!(value = env::consts::OS, "os.platform"); + trace!(value = env::consts::ARCH, "os.arch"); + table.insert("os.platform", env::consts::OS); table.insert("os.arch", env::consts::ARCH); - trace!(os_platform = env::consts::OS); - trace!(os_arch = env::consts::ARCH); - match env::current_dir() { Ok(path) => { + trace!(value = path.display().to_string(), "dir.current"); table.insert("dir.current", helpers::string::path_to_str(&path)); - trace!(dir_current = path.display().to_string()); } Err(err) => error!(%err, "Current directory could not be added as script variable."), } match home::home_dir() { Some(path) => { + trace!(value = path.display().to_string(), "dir.home"); table.insert("dir.home", helpers::string::path_to_str(&path)); - trace!(dir_home = path.display().to_string()); } None => error!("Home directory could not be added as script variable."), } + trace!(value = project.display().to_string(), "dir.project"); table.insert("dir.project", helpers::string::path_to_str(&project)); - trace!(dir_project = project.display().to_string()); for (pos, arg) in args.iter().enumerate() { - trace!("arg_{pos} = \"{arg}\""); + trace!(value = arg, "arg.{pos}"); table.insert(str!(format!("arg.{pos}")), arg); } @@ -54,8 +54,8 @@ pub fn create(values: Maidfile, args: &Vec, project: PathBuf) -> HashMap str!(Template::new_with_placeholder(&value.to_string(), "%{", "}").fill_with_hashmap(&table)).replace("\"", "\\\"") ); + trace!(value = value_formatted, "env.{key}"); env::set_var(key, value_formatted.clone()); - trace!("env_{key} = \"{value_formatted}\""); table.insert(str!(format!("env.{}", key.clone())), str!(value_formatted)); } diff --git a/maid/server/table.rs b/maid/server/table.rs index 120a1f125ee92cbf0b417641eb8820c28e113967..10f21939072608fd35d9b752b5b771bf263ae525 100644 --- a/maid/server/table.rs +++ b/maid/server/table.rs @@ -21,7 +21,7 @@ pub fn create(values: Maidfile, args: &Vec, project: PathBuf) -> HashMap match env::current_dir() { Ok(path) => { table.insert("dir.current", helpers::string::path_to_str(&path)); - trace!(dir_current = path.display().to_string()); + trace!("dir.current = \"{}\"", path.display()); } Err(err) => error!(%err, "Current directory could not be added as script variable."), } @@ -29,16 +29,16 @@ pub fn create(values: Maidfile, args: &Vec, project: PathBuf) -> HashMap match home::home_dir() { Some(path) => { table.insert("dir.home", helpers::string::path_to_str(&path)); - trace!(dir_home = path.display().to_string()); + trace!("dir.home = \"{}\"", path.display()); } None => error!("Home directory could not be added as script variable."), } table.insert("dir.project", helpers::string::path_to_str(&project)); - trace!(dir_project = project.display().to_string()); + trace!("dir.project = \"{}\"", project.display()); for (pos, arg) in args.iter().enumerate() { - trace!("arg_{pos} = \"{arg}\""); + trace!("arg.{pos} = \"{arg}\""); table.insert(str!(format!("arg.{pos}")), arg); } @@ -55,7 +55,7 @@ pub fn create(values: Maidfile, args: &Vec, project: PathBuf) -> HashMap ); env::set_var(key, value_formatted.clone()); - trace!("env_{key} = \"{value_formatted}\""); + trace!("env.{key} = \"{value_formatted}\""); table.insert(str!(format!("env.{}", key.clone())), str!(value_formatted)); } diff --git a/maid/shared/log.rs b/maid/shared/log.rs index 032621368d0d4448772fa2349be0a1cb93c387d8..fe2aa4261a4bf1e8f12f901142cfa197a40551de 100644 --- a/maid/shared/log.rs +++ b/maid/shared/log.rs @@ -1,5 +1,5 @@ -use std::fmt; -use tracing::{level_filters::LevelFilter, Level}; +pub mod layer; +pub mod verbose; pub mod prelude { pub use crate::{debug, error, info, trace, warn}; @@ -8,143 +8,36 @@ pub mod prelude { #[macro_export] macro_rules! info { - ($($arg:tt)*) => { - tracing::info!($($arg)*) - } + ($($field:tt)*) => { + tracing::info!($($field)*) + }; } #[macro_export] macro_rules! warn { - ($($arg:tt)*) => { - tracing::warn!($($arg)*) - } + ($($field:tt)*) => { + tracing::warn!($($field)*) + }; } #[macro_export] macro_rules! error { - ($($arg:tt)*) => {{ - tracing::error!($($arg)*); + ($($field:tt)*) => {{ + tracing::error!($($field)*); std::process::exit(1); - }} + }}; } #[macro_export] macro_rules! debug { - ($($arg:tt)*) => { - tracing::debug!($($arg)*) - } + ($($field:tt)*) => { + tracing::debug!($($field)*) + }; } #[macro_export] macro_rules! trace { - ($($arg:tt)*) => { - tracing::trace!($($arg)*) - } -} - -#[derive(clap::Args, Debug, Clone, Default)] -pub struct Verbosity { - #[arg( - long, - short = 'v', - action = clap::ArgAction::Count, - global = true, - help = L::verbose_help(), - long_help = L::verbose_long_help(), - )] - verbose: u8, - - #[arg( - long, - short = 'q', - action = clap::ArgAction::Count, - global = true, - help = L::quiet_help(), - long_help = L::quiet_long_help(), - conflicts_with = "verbose", - )] - quiet: u8, - - #[arg(skip)] - phantom: std::marker::PhantomData, -} - -#[allow(dead_code)] -impl Verbosity { - pub fn new(verbose: u8, quiet: u8) -> Self { - Verbosity { - verbose, - quiet, - phantom: std::marker::PhantomData, - } - } - - pub fn is_present(&self) -> bool { self.verbose != 0 || self.quiet != 0 } - - pub fn log_level(&self) -> Option { level_enum(self.verbosity()) } - - pub fn log_level_filter(&self) -> LevelFilter { return level_enum(self.verbosity()).map(LevelFilter::from_level).unwrap_or(LevelFilter::OFF); } - - pub fn is_silent(&self) -> bool { self.log_level().is_none() } - - fn verbosity(&self) -> i8 { level_value(L::default()) - (self.quiet as i8) + (self.verbose as i8) } -} - -fn level_value(level: Option) -> i8 { - match level { - None => -1, - Some(Level::ERROR) => 0, - Some(Level::WARN) => 1, - Some(Level::INFO) => 2, - Some(Level::DEBUG) => 3, - Some(Level::TRACE) => 4, - } -} - -fn level_enum(verbosity: i8) -> Option { - match verbosity { - i8::MIN..=-1 => None, - 0 => Some(Level::ERROR), - 1 => Some(Level::WARN), - 2 => Some(Level::INFO), - 3 => Some(Level::DEBUG), - 4..=i8::MAX => Some(Level::TRACE), - } -} - -impl fmt::Display for Verbosity { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.verbosity()) } -} - -pub trait LogLevel { - fn default() -> Option; - - fn verbose_help() -> Option<&'static str> { Some("Increase logging verbosity") } - - fn verbose_long_help() -> Option<&'static str> { None } - - fn quiet_help() -> Option<&'static str> { Some("Decrease logging verbosity") } - - fn quiet_long_help() -> Option<&'static str> { None } -} - -#[derive(Copy, Clone, Debug, Default)] -pub struct ErrorLevel; - -impl LogLevel for ErrorLevel { - fn default() -> Option { return Some(Level::ERROR); } -} - -#[derive(Copy, Clone, Debug, Default)] -pub struct WarnLevel; - -impl LogLevel for WarnLevel { - fn default() -> Option { return Some(Level::WARN); } -} - -#[derive(Copy, Clone, Debug, Default)] -pub struct InfoLevel; - -impl LogLevel for InfoLevel { - fn default() -> Option { return Some(Level::INFO); } + ($($field:tt)*) => { + tracing::trace!($($field)*) + }; } diff --git a/maid/shared/log/layer.rs b/maid/shared/log/layer.rs new file mode 100644 index 0000000000000000000000000000000000000000..5773f8a62b250735ec7623bcec8b9a96c42f2de5 --- /dev/null +++ b/maid/shared/log/layer.rs @@ -0,0 +1,76 @@ +use colored::*; +use std::fmt::{self, Write}; +use tracing::{Event, Level, Subscriber}; +use tracing_subscriber::Layer; + +pub mod prelude { + pub use super::MaidFormatLayer; + pub use tracing_subscriber::prelude::*; +} + +pub struct MaidFormatLayer; + +impl MaidFormatLayer { + pub fn new() -> Self { Self } + + fn format_level(&self, level: &Level) -> ColoredString { + match *level { + Level::TRACE => "TRACE".magenta(), + Level::DEBUG => "DEBUG".cyan(), + Level::INFO => "INFO".green(), + Level::WARN => "WARN".yellow(), + Level::ERROR => "FATAL".red(), + } + } + + fn get_bright_color(&self, level: &Level) -> Box ColoredString> { + match *level { + Level::TRACE => Box::new(|s| s.bright_magenta()), + Level::DEBUG => Box::new(|s| s.bright_cyan()), + Level::INFO => Box::new(|s| s.bright_green()), + Level::WARN => Box::new(|s| s.bright_yellow()), + Level::ERROR => Box::new(|s| s.bright_red()), + } + } +} + +impl Layer for MaidFormatLayer +where + S: Subscriber, +{ + fn on_event(&self, event: &Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) { + let metadata = event.metadata(); + let mut output = String::new(); + + output.push_str(&format!("{} ", self.format_level(metadata.level()))); + + if let Some(path) = metadata.module_path() { + let without_first = path.split("::").skip(1).collect::>().join("::"); + output.push_str(&format!("{} ", without_first.white())); + } + + event.record(&mut CommandVisitor { + output: &mut output, + colorizer: self.get_bright_color(metadata.level()), + }); + + println!("{}", output); + } +} + +struct CommandVisitor<'a> { + output: &'a mut String, + colorizer: Box ColoredString>, +} + +impl<'a> tracing::field::Visit for CommandVisitor<'a> { + fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn fmt::Debug) { + if field.name() == "message" { + let msg = format!("{:?}", value); + let clean_msg = msg.trim_matches('"'); + write!(self.output, "{}", (self.colorizer)(clean_msg)).unwrap(); + } else { + write!(self.output, " {}={:?}", field.name(), value).unwrap(); + } + } +} diff --git a/maid/shared/log/verbose.rs b/maid/shared/log/verbose.rs new file mode 100644 index 0000000000000000000000000000000000000000..1b1eff8dc04600c55d2a4f19194ef3a2d3d29af7 --- /dev/null +++ b/maid/shared/log/verbose.rs @@ -0,0 +1,109 @@ +use std::fmt; +use tracing::{level_filters::LevelFilter, Level}; + +#[derive(clap::Args, Debug, Clone, Default)] +pub struct Verbosity { + #[arg( + long, + short = 'v', + action = clap::ArgAction::Count, + global = true, + help = L::verbose_help(), + long_help = L::verbose_long_help(), + )] + verbose: u8, + + #[arg( + long, + short = 'q', + action = clap::ArgAction::Count, + global = true, + help = L::quiet_help(), + long_help = L::quiet_long_help(), + conflicts_with = "verbose", + )] + quiet: u8, + + #[arg(skip)] + phantom: std::marker::PhantomData, +} + +#[allow(dead_code)] +impl Verbosity { + pub fn new(verbose: u8, quiet: u8) -> Self { + Verbosity { + verbose, + quiet, + phantom: std::marker::PhantomData, + } + } + + pub fn is_present(&self) -> bool { self.verbose != 0 || self.quiet != 0 } + + pub fn log_level(&self) -> Option { level_enum(self.verbosity()) } + + pub fn log_level_filter(&self) -> LevelFilter { return level_enum(self.verbosity()).map(LevelFilter::from_level).unwrap_or(LevelFilter::OFF); } + + pub fn is_silent(&self) -> bool { self.log_level().is_none() } + + fn verbosity(&self) -> i8 { level_value(L::default()) - (self.quiet as i8) + (self.verbose as i8) } +} + +fn level_value(level: Option) -> i8 { + match level { + None => -1, + Some(Level::ERROR) => 0, + Some(Level::WARN) => 1, + Some(Level::INFO) => 2, + Some(Level::DEBUG) => 3, + Some(Level::TRACE) => 4, + } +} + +fn level_enum(verbosity: i8) -> Option { + match verbosity { + i8::MIN..=-1 => None, + 0 => Some(Level::ERROR), + 1 => Some(Level::WARN), + 2 => Some(Level::INFO), + 3 => Some(Level::DEBUG), + 4..=i8::MAX => Some(Level::TRACE), + } +} + +impl fmt::Display for Verbosity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.verbosity()) } +} + +pub trait LogLevel { + fn default() -> Option; + + fn verbose_help() -> Option<&'static str> { Some("Increase logging verbosity") } + + fn verbose_long_help() -> Option<&'static str> { None } + + fn quiet_help() -> Option<&'static str> { Some("Decrease logging verbosity") } + + fn quiet_long_help() -> Option<&'static str> { None } +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct ErrorLevel; + +impl LogLevel for ErrorLevel { + fn default() -> Option { return Some(Level::ERROR); } +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct WarnLevel; + +impl LogLevel for WarnLevel { + fn default() -> Option { return Some(Level::WARN); } +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct InfoLevel; + +impl LogLevel for InfoLevel { + fn default() -> Option { return Some(Level::INFO); } +}