diff --git a/TODO b/TODO new file mode 100644 index 0000000..e77f9b0 --- /dev/null +++ b/TODO @@ -0,0 +1 @@ +Log filtering rules in config file. For `Trace`s, `Level`s, sublevels, etc. \ No newline at end of file diff --git a/src/log.rs b/src/log.rs index e00ff67..5f90ef8 100644 --- a/src/log.rs +++ b/src/log.rs @@ -1,4 +1,5 @@ //! Global logging state +use super::util::*; use std::{ fmt::{ @@ -10,6 +11,7 @@ use std::{ Write, }, borrow::Borrow, + sync::{Arc,RwLock}, }; use once_cell::sync::OnceCell; @@ -24,6 +26,136 @@ pub enum Level Debug, } +/// Contains backtrace info from where the log macro was invoked +#[derive(Debug)] +pub struct Trace +{ + line: Option, + file: Option<&'static str>, + column: Option, + + display: Opaque>>, +} + +impl Clone for Trace +{ + #[inline] + fn clone(&self) -> Self + { + Self { + line: self.line.clone(), + file: self.file.clone(), + column: self.column.clone(), + + display: Opaque::new(RwLock::new(None)), + } + } +} + +impl std::hash::Hash for Trace +{ + fn hash(&self, state: &mut H) + { + self.line.hash(state); + self.file.hash(state); + self.column.hash(state); + } +} + +impl std::cmp::PartialEq for Trace +{ + fn eq(&self, other: &Self)->bool + { + self.line == other.line && + self.file == other.file && + self.column == other.column + } +} + +impl Trace +{ + /// Create a new `Trace` manually. Generally not desireable, use `get_trace!()` instead. + pub fn new(file: Option<&'static str>, line: Option, column: Option) -> Self + { + use std::convert::TryInto; + let this = Self{ + file, + line: line.and_then(|x| x.try_into().ok()), + column: column.and_then(|x| x.try_into().ok()), + display: Opaque::new(RwLock::new(None)), + }; + this + } + + fn genstring(&self) -> Result + { + let mut out = String::new(); + use std::fmt::Write as FmtWrite; + + write!(out, "{}", self.file.unwrap_or("(unbound)"))?; + if let Some(line) = self.line { + write!(&mut out, ":{}", line)?; + } else { + write!(&mut out, ":?")?; + } + if let Some(column) = self.column { + write!(&mut out, ":{}>", column)?; + } else { + write!(&mut out, ":?")?; + } + + Ok(out) + } +} + +impl std::fmt::Display for Trace +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + { + let read = self.display.read().expect("Poisoned"); + if let Some(opt) = read.as_ref() + { + write!(f, "{}", opt)?; + return Ok(()); + } + } + + let out = self.genstring()?; + + write!(f, "{}", out)?; + + let mut write = self.display.write().expect("Poisoned"); + *write = Some(out); + Ok(()) + } +} + + +impl Default for Trace +{ + #[inline] + fn default() -> Self + { + Self{ + line:None, + file:None, + column:None, + + display: Opaque::new(RwLock::new(None)), + } + } +} + +/// Get a source code trace of the current file, line and column. +#[macro_export] macro_rules! get_trace +{ + () => { + $crate::log::Trace::new(Some(file!()), Some(line!()), Some(column!())) + } +} + + /// Append this trait to allow you to pub trait AsLevel: fmt::Display + Borrow{ /// Used for derived levels @@ -105,10 +237,11 @@ impl Logger Logger::global() } - pub fn println(&self, mut to: W, level: L, what: D) -> io::Result<()> + pub fn println(&self, mut to: W, level: L, what: D, trace: T) -> io::Result<()> where W: Write, L: AsLevel, D: Display, + T: Borrow { //lol enum Date { @@ -152,7 +285,13 @@ impl Logger chrono::offset::Utc::now().into() }; - write!(to, "{} [", now)?; + write!(to, "{} ", now)?; + if self.title.len() > 0 { + write!(to, " <{}>\t", self.title)?; + } else { + write!(to, " <{}>\t", trace.borrow())?; + } + write!(to," [")?; struct Prefix<'a,L: AsLevel>(&'a L); @@ -167,12 +306,7 @@ impl Logger write!(to, "{}", Prefix(&level))?; //what a mess... write!(to, "{}", level)?; - write!(to, "]")?; - if self.title.len() > 0 { - write!(to, " <{}>: ", self.title)?; - } else { - write!(to, ": ")?; - } + write!(to, "]: ")?; writeln!(to, "{}", what)?; } Ok(()) @@ -184,7 +318,7 @@ impl Logger { let stdout = std::io::stdout(); let stdout = stdout.lock(); - $crate::log::Logger::global().println(stdout, $crate::log::Level::Debug, $obj).expect("i/o error"); + $crate::log::Logger::global().println(stdout, $crate::log::Level::Debug, $obj, get_trace!()).expect("i/o error"); } }; ($fmt:literal, $($args:expr),*) => { @@ -221,7 +355,7 @@ impl Logger let stdout = std::io::stdout(); let stdout = stdout.lock(); - $crate::log::Logger::global().println(stdout, Status, $obj).expect("i/o error"); + $crate::log::Logger::global().println(stdout, Status, $obj, get_trace!()).expect("i/o error"); } }; ($fmt:literal, $($args:expr),*) => { @@ -235,7 +369,7 @@ impl Logger { let stdout = std::io::stdout(); let stdout = stdout.lock(); - $crate::log::Logger::global().println(stdout, $crate::log::Level::Info, $obj).expect("i/o error"); + $crate::log::Logger::global().println(stdout, $crate::log::Level::Info, $obj, get_trace!()).expect("i/o error"); } }; ($fmt:literal, $($args:expr),*) => { @@ -270,7 +404,7 @@ impl Logger let stdout = std::io::stdout(); let stdout = stdout.lock(); - $crate::log::Logger::global().println(stdout, Important, $obj).expect("i/o error"); + $crate::log::Logger::global().println(stdout, Important, $obj, get_trace!()).expect("i/o error"); } }; ($fmt:literal, $($args:expr),*) => { @@ -284,7 +418,7 @@ impl Logger { let stderr = std::io::stderr(); let stderr = stderr.lock(); - $crate::log::Logger::global().println(stderr, $crate::log::Level::Warn, $obj).expect("i/o error"); + $crate::log::Logger::global().println(stderr, $crate::log::Level::Warn, $obj, get_trace!()).expect("i/o error"); } }; ($fmt:literal, $($args:expr),*) => { @@ -320,7 +454,7 @@ impl Logger let stderr = std::io::stderr(); let stderr = stderr.lock(); - $crate::log::Logger::global().println(stderr, Dangerous, $obj).expect("i/o error"); + $crate::log::Logger::global().println(stderr, Dangerous, $obj, get_trace!()).expect("i/o error"); } }; ($fmt:literal, $($args:expr),*) => { @@ -333,7 +467,7 @@ impl Logger { let stderr = std::io::stderr(); let stderr = stderr.lock(); - $crate::log::Logger::global().println(stderr, $crate::log::Level::Error, $obj).expect("i/o error"); + $crate::log::Logger::global().println(stderr, $crate::log::Level::Error, $obj, get_trace!()).expect("i/o error"); } }; ($fmt:literal, $($args:expr),*) => { @@ -346,13 +480,13 @@ impl Logger { let stdout = std::io::stdout(); let stdout = stdout.lock(); - $crate::log::Logger::global().println(stdout, $crate::log::Level::Silent, $obj).expect("i/o error"); + $crate::log::Logger::global().println(stdout, $crate::log::Level::Silent, $obj, get_trace!()).expect("i/o error"); std::process::exit(-1) } }; ($fmt:literal, $($args:expr),*) => { - error!(format!($fmt, $($args,)*)); + fatal!(format!($fmt, $($args,)*)); }; } diff --git a/src/main.rs b/src/main.rs index 55d49cd..29d42b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,6 +29,7 @@ mod live; mod context; mod job; +//test async fn do_thing_every() -> Result<(mpsc::Sender<()>, task::JoinHandle<()>), Box> { let mut interval = time::interval(Duration::from_secs(10)); diff --git a/src/util.rs b/src/util.rs index 06ba526..aae07d7 100644 --- a/src/util.rs +++ b/src/util.rs @@ -165,3 +165,61 @@ where F:FnOnce(T) } } + +/// Enabled `#[derive(Debug)]` on types with fields that are not `Debug`. +pub struct Opaque(T); + +impl Opaque +{ + /// Create a new `Opaque` wrapper around `value`. + #[inline] + pub const fn new(value: T) -> Self + { + Self(value) + } + + /// Consume `Opaque` and get back the inner value. + #[inline] + pub fn into(self) -> T + { + self.0 + } +} + +impl From for Opaque +{ + fn from(from: T) -> Self + { + Self(from) + } +} + +impl std::fmt::Debug for Opaque +{ + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "") + } +} + +impl Deref for Opaque +{ + type Target = T; + #[inline] + fn deref(&self) -> &Self::Target + { + &self.0 + } +} + + +impl DerefMut for Opaque +{ + #[inline] + fn deref_mut(&mut self) -> &mut ::Target + { + &mut self.0 + } +} +