diff --git a/Cargo.lock b/Cargo.lock index e9c8dc80..d5e146da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1178,6 +1178,7 @@ dependencies = [ "isahc", "libc", "libloading", + "log", "mailin-embedded", "native-tls", "nix", diff --git a/docs/meli.conf.5 b/docs/meli.conf.5 index 7e1f2fb8..7a95e9e8 100644 --- a/docs/meli.conf.5 +++ b/docs/meli.conf.5 @@ -1448,8 +1448,6 @@ Available levels are, in partial order: .It .Em OFF .It -.Em FATAL -.It .Em ERROR .It .Em WARN diff --git a/melib/Cargo.toml b/melib/Cargo.toml index 9fd0b4a5..44d6f99b 100644 --- a/melib/Cargo.toml +++ b/melib/Cargo.toml @@ -20,6 +20,7 @@ name = "melib" path = "src/lib.rs" [dependencies] +log = { version = "0.4", features = ["std"]} async-stream = "^0.3" base64 = { version = "^0.13", optional = true } bincode = { version = "^1.3.0", default-features = false } diff --git a/melib/src/addressbook.rs b/melib/src/addressbook.rs index 64374e60..7817da32 100644 --- a/melib/src/addressbook.rs +++ b/melib/src/addressbook.rs @@ -120,12 +120,10 @@ impl AddressBook { } } Err(err) => { - crate::log( - format!( - "Could not load mutt alias file {:?}: {}", - mutt_alias_file, err - ), - crate::WARN, + log::warn!( + "Could not load mutt alias file {:?}: {}", + mutt_alias_file, + err ); } } @@ -139,10 +137,7 @@ impl AddressBook { } } Err(err) => { - crate::log( - format!("Could not load vcards from {:?}: {}", vcard_path, err), - crate::WARN, - ); + log::warn!("Could not load vcards from {:?}: {}", vcard_path, err); } } } diff --git a/melib/src/addressbook/vcard.rs b/melib/src/addressbook/vcard.rs index 90c19a2f..33de6324 100644 --- a/melib/src/addressbook/vcard.rs +++ b/melib/src/addressbook/vcard.rs @@ -305,10 +305,7 @@ pub fn load_cards(p: &std::path::Path) -> Result> { } } Err(err) => { - crate::log( - format!("Could not parse vcard from {}: {}", f.display(), err), - crate::WARN, - ); + log::warn!("Could not parse vcard from {}: {}", f.display(), err); } } } diff --git a/melib/src/backends.rs b/melib/src/backends.rs index dde57ed2..89533e00 100644 --- a/melib/src/backends.rs +++ b/melib/src/backends.rs @@ -62,6 +62,7 @@ use super::email::{Envelope, EnvelopeHash, Flag}; use crate::{ conf::AccountSettings, error::{Error, ErrorKind, Result}, + LogLevel, }; #[macro_export] @@ -261,7 +262,7 @@ pub enum BackendEvent { Notice { description: String, content: Option, - level: crate::LoggingLevel, + level: LogLevel, }, Refresh(RefreshEvent), AccountStateChange { @@ -275,7 +276,7 @@ impl From for BackendEvent { BackendEvent::Notice { description: val.summary.to_string(), content: Some(val.to_string()), - level: crate::LoggingLevel::ERROR, + level: LogLevel::ERROR, } } } diff --git a/melib/src/backends/imap.rs b/melib/src/backends/imap.rs index b356f4b4..72f8b47a 100644 --- a/melib/src/backends/imap.rs +++ b/melib/src/backends/imap.rs @@ -753,13 +753,10 @@ impl MailBackend for ImapType { cmd.push_str("\\Draft "); } Ok(_) => { - crate::log( - format!( - "Application error: more than one flag bit set in \ - set_flags: {:?}", - flags - ), - crate::ERROR, + log::error!( + "Application error: more than one flag bit set in set_flags: \ + {:?}", + flags ); return Err(Error::new(format!( "Application error: more than one flag bit set in set_flags: \ @@ -826,13 +823,10 @@ impl MailBackend for ImapType { cmd.push_str("\\Draft "); } Ok(_) => { - crate::log( - format!( - "Application error: more than one flag bit set in \ - set_flags: {:?}", - flags - ), - crate::ERROR, + log::error!( + "Application error: more than one flag bit set in set_flags: \ + {:?}", + flags ); return Err(Error::new(format!( "Application error: more than one flag bit set in set_flags: \ @@ -1716,23 +1710,18 @@ async fn fetch_hlpr(state: &mut FetchState) -> Result> { FetchStage::InitialCache => { match cache::fetch_cached_envs(state).await { Err(err) => { - crate::log( - format!( - "IMAP cache error: could not fetch cache for {}. Reason: {}", - state.uid_store.account_name, err - ), - crate::ERROR, + log::error!( + "IMAP cache error: could not fetch cache for {}. Reason: {}", + state.uid_store.account_name, + err ); /* Try resetting the database */ if let Some(ref mut cache_handle) = state.cache_handle { if let Err(err) = cache_handle.reset() { - crate::log( - format!( - "IMAP cache error: could not reset cache for {}. Reason: \ - {}", - state.uid_store.account_name, err - ), - crate::ERROR, + log::error!( + "IMAP cache error: could not reset cache for {}. Reason: {}", + state.uid_store.account_name, + err ); } } diff --git a/melib/src/backends/imap/connection.rs b/melib/src/backends/imap/connection.rs index 1b9c76cc..0ce3e1ee 100644 --- a/melib/src/backends/imap/connection.rs +++ b/melib/src/backends/imap/connection.rs @@ -25,6 +25,7 @@ use crate::{ connections::{lookup_ipv4, timeout, Connection}, email::parser::BytesExt, error::*, + LogLevel, }; extern crate native_tls; use std::{ @@ -260,10 +261,7 @@ impl ImapStream { .get_ref() .set_keepalive(Some(Duration::new(60 * 9, 0))) { - crate::log( - format!("Could not set TCP keepalive in IMAP connection: {}", err), - crate::LoggingLevel::WARN, - ); + log::warn!("Could not set TCP keepalive in IMAP connection: {}", err); } let mut res = Vec::with_capacity(8 * 1024); let mut ret = ImapStream { @@ -658,13 +656,11 @@ impl ImapConnection { | ImapResponse::Bad(code) | ImapResponse::Preauth(code) | ImapResponse::Bye(code) => { - crate::log( - format!( - "Could not use COMPRESS=DEFLATE in account `{}`: server \ - replied with `{}`", - self.uid_store.account_name, code - ), - crate::LoggingLevel::WARN, + log::warn!( + "Could not use COMPRESS=DEFLATE in account `{}`: server \ + replied with `{}`", + self.uid_store.account_name, + code ); } ImapResponse::Ok(_) => { @@ -737,7 +733,7 @@ impl ImapConnection { crate::backends::BackendEvent::Notice { description: response_code.to_string(), content: None, - level: crate::logging::LoggingLevel::ERROR, + level: LogLevel::ERROR, }, ); ret.extend_from_slice(&response); @@ -754,7 +750,7 @@ impl ImapConnection { crate::backends::BackendEvent::Notice { description: response_code.to_string(), content: None, - level: crate::logging::LoggingLevel::ERROR, + level: LogLevel::ERROR, }, ); ret.extend_from_slice(&response); diff --git a/melib/src/backends/imap/untagged.rs b/melib/src/backends/imap/untagged.rs index 89b4df5b..ac058b72 100644 --- a/melib/src/backends/imap/untagged.rs +++ b/melib/src/backends/imap/untagged.rs @@ -287,7 +287,7 @@ impl ImapConnection { ) }) { - crate::log(err.to_string(), crate::INFO); + log::info!("{err}"); } } for response in v { @@ -391,7 +391,7 @@ impl ImapConnection { ) }) { - crate::log(err.to_string(), crate::INFO); + log::info!("{err}"); } } for response in v { diff --git a/melib/src/backends/nntp/connection.rs b/melib/src/backends/nntp/connection.rs index 6eafefe5..f823969e 100644 --- a/melib/src/backends/nntp/connection.rs +++ b/melib/src/backends/nntp/connection.rs @@ -24,6 +24,7 @@ use crate::{ connections::{lookup_ipv4, Connection}, email::parser::BytesExt, error::*, + log, }; extern crate native_tls; use std::{collections::HashSet, future::Future, pin::Pin, sync::Arc, time::Instant}; @@ -199,10 +200,7 @@ impl NntpStream { .get_ref() .set_keepalive(Some(std::time::Duration::new(60 * 9, 0))) { - crate::log( - format!("Could not set TCP keepalive in NNTP connection: {}", err), - crate::LoggingLevel::WARN, - ); + log::warn!("Could not set TCP keepalive in NNTP connection: {}", err); } ret.read_response(&mut res, false, &["200 ", "201 "]) diff --git a/melib/src/lib.rs b/melib/src/lib.rs index 8902d27d..853ef0e6 100644 --- a/melib/src/lib.rs +++ b/melib/src/lib.rs @@ -43,41 +43,20 @@ //! - A `ShellExpandTrait` to expand paths like a shell. //! - A `debug` macro that works like `std::dbg` but for multiple threads. (see //! [`debug` macro](./macro.debug.html)) + #[macro_use] pub mod dbg { - #[macro_export] - macro_rules! log_tag { - () => { - eprint!( - "[{}][{:?}] {}:{}_{}: ", - $crate::datetime::timestamp_to_string( - $crate::datetime::now(), - Some("%Y-%m-%d %T"), - false - ), - std::thread::current() - .name() - .map(std::string::ToString::to_string) - .unwrap_or_else(|| format!("{:?}", std::thread::current().id())), - file!(), - line!(), - column!() - ); - }; - } - #[allow(clippy::redundant_closure)] #[macro_export] macro_rules! debug { ($val:literal) => { { - if cfg!(feature="debug-tracing") { - log_tag!(); - eprintln!($val); + if cfg!(feature="debug-tracing") { + $crate::log::debug!($val); + } + $val } - $val - } }; ($val:expr) => { if cfg!(feature="debug-tracing") { @@ -86,8 +65,7 @@ pub mod dbg { // of temporaries - https://stackoverflow.com/a/48732525/1063961 match $val { tmp => { - log_tag!(); - eprintln!("{} = {:?}", stringify, tmp); + $crate::log::debug!("{} = {:?}", stringify, tmp); tmp } } @@ -97,8 +75,7 @@ pub mod dbg { }; ($fmt:literal, $($arg:tt)*) => { if cfg!(feature="debug-tracing") { - log_tag!(); - eprintln!($fmt, $($arg)*); + $crate::log::debug!($fmt, $($arg)*); } }; } @@ -112,7 +89,7 @@ pub use datetime::UnixTimestamp; #[macro_use] mod logging; -pub use self::logging::{LoggingLevel::*, *}; +pub use self::logging::{LogLevel, StderrLogger}; pub mod addressbook; pub use addressbook::*; @@ -142,6 +119,7 @@ pub mod sqlite3; #[macro_use] extern crate serde_derive; +pub extern crate log; /* parser */ extern crate data_encoding; extern crate encoding; diff --git a/melib/src/logging.rs b/melib/src/logging.rs index 36f9c165..28ab6e26 100644 --- a/melib/src/logging.rs +++ b/melib/src/logging.rs @@ -23,30 +23,98 @@ use std::{ fs::OpenOptions, io::{BufWriter, Write}, path::PathBuf, - sync::{Arc, Mutex}, + sync::{ + atomic::{AtomicU8, Ordering}, + Arc, Mutex, + }, }; +use log::{Level, LevelFilter, Log, Metadata, Record}; + use crate::shellexpand::ShellExpandTrait; -#[derive(Copy, Clone, PartialEq, PartialOrd, Hash, Debug, Serialize, Deserialize)] -pub enum LoggingLevel { - OFF, - FATAL, +#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Hash, Debug, Serialize, Deserialize)] +#[repr(u8)] +pub enum LogLevel { + OFF = 0, ERROR, WARN, + #[default] INFO, DEBUG, TRACE, } -impl std::fmt::Display for LoggingLevel { +impl From for LogLevel { + fn from(verbosity: u8) -> Self { + match verbosity { + 0 => LogLevel::OFF, + 1 => LogLevel::ERROR, + 2 => LogLevel::WARN, + 3 => LogLevel::INFO, + 4 => LogLevel::DEBUG, + _ => LogLevel::TRACE, + } + } +} + +impl From for LogLevel { + fn from(l: Level) -> Self { + match l { + Level::Error => Self::ERROR, + Level::Warn => Self::WARN, + Level::Info => Self::INFO, + Level::Debug => Self::DEBUG, + Level::Trace => Self::TRACE, + } + } +} + +impl From for Level { + fn from(l: LogLevel) -> Self { + match l { + LogLevel::ERROR => Self::Error, + LogLevel::WARN => Self::Warn, + LogLevel::OFF | LogLevel::INFO => Self::Info, + LogLevel::DEBUG => Self::Debug, + LogLevel::TRACE => Self::Trace, + } + } +} + +impl From for LogLevel { + fn from(l: LevelFilter) -> Self { + match l { + LevelFilter::Off => Self::OFF, + LevelFilter::Error => Self::ERROR, + LevelFilter::Warn => Self::WARN, + LevelFilter::Info => Self::INFO, + LevelFilter::Debug => Self::DEBUG, + LevelFilter::Trace => Self::TRACE, + } + } +} + +impl From for LevelFilter { + fn from(l: LogLevel) -> Self { + match l { + LogLevel::OFF => Self::Off, + LogLevel::ERROR => Self::Error, + LogLevel::WARN => Self::Warn, + LogLevel::INFO => Self::Info, + LogLevel::DEBUG => Self::Debug, + LogLevel::TRACE => Self::Trace, + } + } +} + +impl std::fmt::Display for LogLevel { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "{}", match self { OFF => "OFF", - FATAL => "FATAL", ERROR => "ERROR", WARN => "WARN", INFO => "INFO", @@ -57,73 +125,173 @@ impl std::fmt::Display for LoggingLevel { } } -impl Default for LoggingLevel { - fn default() -> Self { - LoggingLevel::INFO +use LogLevel::*; + +#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Hash, Debug, Serialize, Deserialize)] +pub enum Destination { + File, + #[default] + Stderr, + None, +} + +#[derive(Clone)] +pub struct StderrLogger { + dest: Arc>>, + level: Arc, + print_level: bool, + print_module_names: bool, + debug_dest: Destination, +} + +impl std::fmt::Debug for StderrLogger { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_struct(stringify!(StderrLogger)) + .field("level", &LogLevel::from(self.level.load(Ordering::SeqCst))) + .field("print_level", &self.print_level) + .field("print_module_names", &self.print_module_names) + .field("debug_dest", &self.debug_dest) + .finish() } } -use LoggingLevel::*; - -struct LoggingBackend { - dest: BufWriter, - level: LoggingLevel, +impl Default for StderrLogger { + fn default() -> Self { + Self::new(LogLevel::default()) + } } -thread_local!(static LOG: Arc> = Arc::new(Mutex::new({ - let data_dir = xdg::BaseDirectories::with_prefix("meli").unwrap(); - let log_file = OpenOptions::new().append(true) /* writes will append to a file instead of overwriting previous contents */ - .create(true) /* a new file will be created if the file does not yet already exist.*/ - .read(true) - .open(data_dir.place_data_file("meli.log").unwrap()).unwrap(); - LoggingBackend { - dest: BufWriter::new(log_file), - level: LoggingLevel::default(), - }})) -); +impl StderrLogger { + pub fn new(level: LogLevel) -> Self { + let logger = { + let data_dir = xdg::BaseDirectories::with_prefix("meli").unwrap(); + let log_file = OpenOptions::new().append(true) /* writes will append to a file instead of overwriting previous contents */ + .create(true) /* a new file will be created if the file does not yet already exist.*/ + .read(true) + .open(data_dir.place_data_file("meli.log").unwrap()).unwrap(); + StderrLogger { + dest: Arc::new(Mutex::new(BufWriter::new(log_file).into())), + level: Arc::new(AtomicU8::new(level as u8)), + print_level: true, + print_module_names: true, + #[cfg(feature = "debug-tracing")] + debug_dest: Destination::Stderr, + #[cfg(not(feature = "debug-tracing"))] + debug_dest: Destination::None, + } + }; -pub fn log>(val: S, level: LoggingLevel) { - LOG.with(|f| { - let mut b = f.lock().unwrap(); - if level <= b.level { - b.dest + #[cfg(feature = "debug-tracing")] + log::set_max_level( + if matches!(LevelFilter::from(logger.log_level()), LevelFilter::Off) { + LevelFilter::Off + } else { + LevelFilter::Trace + }, + ); + #[cfg(not(feature = "debug-tracing"))] + log::set_max_level(LevelFilter::from(logger.log_level())); + log::set_boxed_logger(Box::new(logger.clone())).unwrap(); + logger + } + + pub fn log_level(&self) -> LogLevel { + self.level.load(Ordering::SeqCst).into() + } + + pub fn change_log_dest(&mut self, path: PathBuf) { + let path = path.expand(); // expand shell stuff + let mut dest = self.dest.lock().unwrap(); + *dest = BufWriter::new(OpenOptions::new().append(true) /* writes will append to a file instead of overwriting previous contents */ + .create(true) /* a new file will be created if the file does not yet already exist.*/ + .read(true) + .open(path).unwrap()).into(); + } +} + +impl Log for StderrLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + !["polling", "async_io"] + .iter() + .any(|t| metadata.target().starts_with(t)) + && (metadata.level() <= Level::from(self.log_level()) + || !matches!(self.debug_dest, Destination::None)) + //metadata.level() <= self.log_level_filter() && + // self.includes_module(metadata.target()) + } + + fn log(&self, record: &Record) { + if !self.enabled(record.metadata()) { + return; + } + + fn write( + writer: &mut impl Write, + record: &Record, + (print_level, print_module_names): (bool, bool), + ) -> Option<()> { + writer .write_all( crate::datetime::timestamp_to_string(crate::datetime::now(), None, false) .as_bytes(), ) - .unwrap(); - b.dest.write_all(b" [").unwrap(); - b.dest.write_all(level.to_string().as_bytes()).unwrap(); - b.dest.write_all(b"]: ").unwrap(); - b.dest.write_all(val.as_ref().as_bytes()).unwrap(); - b.dest.write_all(b"\n").unwrap(); - b.dest.flush().unwrap(); + .ok()?; + writer.write_all(b" [").ok()?; + if print_level { + writer + .write_all(record.level().to_string().as_bytes()) + .ok()?; + } + write!(writer, "]: ").ok()?; + if print_module_names { + write!(writer, "{}: ", record.metadata().target()).ok()?; + } + write!(writer, "{}", record.args()).ok()?; + writer.write_all(b"\n").ok()?; + writer.flush().ok()?; + Some(()) } - }); -} -pub fn get_log_level() -> LoggingLevel { - let mut level = INFO; - LOG.with(|f| { - level = f.lock().unwrap().level; - }); - level -} + // if logging isn't enabled for this level do a quick out + match ( + self.debug_dest, + record.metadata().level() <= Level::from(self.log_level()), + ) { + (Destination::None, false) => return, + (Destination::None | Destination::File, _) => { + _ = self.dest.lock().ok().and_then(|mut d| { + write( + &mut (*d), + record, + (self.print_level, self.print_module_names), + ) + }); + } + (Destination::Stderr, true) => { + _ = self.dest.lock().ok().and_then(|mut d| { + write( + &mut (*d), + record, + (self.print_level, self.print_module_names), + ) + }); + _ = write( + &mut std::io::stderr(), + record, + (self.print_level, self.print_module_names), + ); + } + (Destination::Stderr, false) => { + _ = write( + &mut std::io::stderr(), + record, + (self.print_level, self.print_module_names), + ); + } + } + } -pub fn change_log_dest(path: PathBuf) { - LOG.with(|f| { - let path = path.expand(); // expand shell stuff - let mut backend = f.lock().unwrap(); - backend.dest = BufWriter::new(OpenOptions::new().append(true) /* writes will append to a file instead of overwriting previous contents */ - .create(true) /* a new file will be created if the file does not yet already exist.*/ - .read(true) - .open(path).unwrap()); - }); -} - -pub fn change_log_level(new_val: LoggingLevel) { - LOG.with(|f| { - let mut backend = f.lock().unwrap(); - backend.level = new_val; - }); + fn flush(&self) { + self.dest.lock().ok().and_then(|mut w| w.flush().ok()); + } } diff --git a/melib/src/sqlite3.rs b/melib/src/sqlite3.rs index 9c6995b0..0ff0195e 100644 --- a/melib/src/sqlite3.rs +++ b/melib/src/sqlite3.rs @@ -24,7 +24,7 @@ use std::path::PathBuf; use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput}; pub use rusqlite::{self, params, Connection}; -use crate::{error::*, logging::log, Envelope}; +use crate::{error::*, log, Envelope}; #[derive(Copy, Clone, Debug)] pub struct DatabaseDescription { @@ -61,13 +61,10 @@ pub fn open_or_create_db( }?; let mut set_mode = false; if !db_path.exists() { - log( - format!( - "Creating {} database in {}", - description.name, - db_path.display() - ), - crate::INFO, + log::info!( + "Creating {} database in {}", + description.name, + db_path.display() ); set_mode = true; } @@ -83,12 +80,10 @@ pub fn open_or_create_db( } let version: i32 = conn.pragma_query_value(None, "user_version", |row| row.get(0))?; if version != 0_i32 && version as u32 != description.version { - log( - format!( - "Database version mismatch, is {} but expected {}", - version, description.version - ), - crate::INFO, + log::info!( + "Database version mismatch, is {} but expected {}", + version, + description.version ); if second_try { return Err(Error::new(format!( @@ -124,13 +119,10 @@ pub fn reset_db(description: &DatabaseDescription, identifier: Option<&str>) -> if !db_path.exists() { return Ok(()); } - log( - format!( - "Resetting {} database in {}", - description.name, - db_path.display() - ), - crate::INFO, + log::info!( + "Resetting {} database in {}", + description.name, + db_path.display() ); std::fs::remove_file(&db_path)?; Ok(()) diff --git a/src/components/mail/compose.rs b/src/components/mail/compose.rs index a8f91d98..16300ff5 100644 --- a/src/components/mail/compose.rs +++ b/src/components/mail/compose.rs @@ -1748,12 +1748,9 @@ impl Component for Composer { } let editor_command = format!("{} {}", editor, f.path().display()); - log( - format!( - "Executing: sh -c \"{}\"", - editor_command.replace('"', "\\\"") - ), - DEBUG, + log::debug!( + "Executing: sh -c \"{}\"", + editor_command.replace('"', "\\\"") ); match Command::new("sh") .args(["-c", &editor_command]) @@ -1893,10 +1890,7 @@ impl Component for Composer { context.input_kill(); } - log( - format!("Executing: sh -c \"{}\"", command.replace('"', "\\\"")), - DEBUG, - ); + log::debug!("Executing: sh -c \"{}\"", command.replace('"', "\\\"")); match Command::new("sh") .args(["-c", command]) .stdin(Stdio::inherit()) @@ -2170,13 +2164,9 @@ pub fn send_draft( match output { Err(err) => { debug!("{:?} could not sign draft msg", err); - log( - format!( - "Could not sign draft in account `{}`: {}.", + log::error!( + "Could not sign draft in account `{}`: {err}.", context.accounts[&account_hash].name(), - err.to_string() - ), - ERROR, ); context.replies.push_back(UIEvent::Notification( Some(format!( @@ -2358,12 +2348,9 @@ pub fn send_draft_async( .unwrap(); } else if !store_sent_mail && is_ok { let f = create_temp_file(message.as_bytes(), None, None, false); - log( - format!( - "store_sent_mail is false; stored sent mail to {}", - f.path().display() - ), - INFO, + log::info!( + "store_sent_mail is false; stored sent mail to {}", + f.path().display() ); } ret diff --git a/src/components/mail/listing.rs b/src/components/mail/listing.rs index c99fef29..dd2af9c1 100644 --- a/src/components/mail/listing.rs +++ b/src/components/mail/listing.rs @@ -557,7 +557,7 @@ pub trait MailListingTrait: ListingTrait { name: "message copying".into(), handle, on_finish: None, - logging_level: melib::LoggingLevel::INFO, + log_level: LogLevel::INFO, }, ); } @@ -594,7 +594,7 @@ pub trait MailListingTrait: ListingTrait { name: "message moving".into(), handle, on_finish: None, - logging_level: melib::LoggingLevel::INFO, + log_level: LogLevel::INFO, }, ); } @@ -692,7 +692,7 @@ pub trait MailListingTrait: ListingTrait { ), }); }))), - logging_level: melib::LoggingLevel::INFO, + log_level: LogLevel::INFO, }, ); } diff --git a/src/components/mail/listing/compact.rs b/src/components/mail/listing/compact.rs index 0466c413..127f4404 100644 --- a/src/components/mail/listing/compact.rs +++ b/src/components/mail/listing/compact.rs @@ -1426,7 +1426,7 @@ impl CompactListing { "Encountered an error while searching for `{}`: {}.", search_term, &err ); - log(message.clone(), ERROR); + log::error!("{}", message); context.replies.push_back(UIEvent::Notification( Some("Could not perform search".to_string()), message, diff --git a/src/components/mail/view.rs b/src/components/mail/view.rs index 16617154..b2da0b64 100644 --- a/src/components/mail/view.rs +++ b/src/components/mail/view.rs @@ -1501,7 +1501,7 @@ impl Component for MailView { err.to_string(), Some(NotificationType::Error(err.kind)), )); - log(format!("Failed to open envelope: {}", err), ERROR); + log::error!("Failed to open envelope: {err}"); self.init_futures(context); return; } else { @@ -2140,7 +2140,7 @@ impl Component for MailView { .unwrap_or_else(|| "Not found".into()), err ); - log(&err_string, ERROR); + log::error!("{err_string}"); context.replies.push_back(UIEvent::Notification( Some("Failed to open e-mail".to_string()), err_string, @@ -2151,7 +2151,7 @@ impl Component for MailView { } } }))), - logging_level: melib::LoggingLevel::DEBUG, + log_level: LogLevel::DEBUG, }, ); return true; @@ -2503,7 +2503,7 @@ impl Component for MailView { err.to_string(), Some(NotificationType::Error(err.kind)), )); - log(format!("Failed to open envelope: {}", err), ERROR); + log::error!("Failed to open envelope: {err}"); self.init_futures(context); return true; } else { @@ -2523,10 +2523,7 @@ impl Component for MailView { err.to_string(), Some(NotificationType::Error(melib::ErrorKind::External)), )); - log( - format!("Failed to create file at {}: {}", path.display(), err), - ERROR, - ); + log::error!("Failed to create file at {}: {err}", path.display()); return true; } Ok(()) => { @@ -2557,7 +2554,7 @@ impl Component for MailView { err.to_string(), Some(NotificationType::Error(err.kind)), )); - log(format!("Failed to open envelope: {}", err), ERROR); + log::error!("Failed to open envelope: {err}"); self.init_futures(context); return true; } else { @@ -2582,10 +2579,7 @@ impl Component for MailView { err.to_string(), Some(NotificationType::Error(melib::ErrorKind::External)), )); - log( - format!("Failed to create file at {}: {}", path.display(), err), - ERROR, - ); + log::error!("Failed to create file at {}: {err}", path.display()); } Ok(()) => { context.replies.push_back(UIEvent::Notification( @@ -2609,10 +2603,7 @@ impl Component for MailView { err.to_string(), Some(NotificationType::Error(melib::ErrorKind::External)), )); - log( - format!("Failed to create file at {}: {}", path.display(), err), - ERROR, - ); + log::error!("Failed to create file at {}: {err}", path.display()); return true; } Ok(()) => { diff --git a/src/components/mail/view/thread.rs b/src/components/mail/view/thread.rs index ea74269d..7540888b 100644 --- a/src/components/mail/view/thread.rs +++ b/src/components/mail/view/thread.rs @@ -100,6 +100,7 @@ impl ThreadView { view.new_cursor_pos = view.new_expanded_pos; view } + pub fn update(&mut self, context: &Context) { if self.entries.is_empty() { return; @@ -439,7 +440,6 @@ impl ThreadView { copy_area(grid, &self.content, dest_area, src_area); } - /// draw the list fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { let (upper_left, bottom_right) = area; let (width, height) = self.content.size(); @@ -492,8 +492,7 @@ impl ThreadView { let page_no = (self.new_cursor_pos).wrapping_div(rows); let top_idx = page_no * rows; - /* This closure (written for code clarity, should be inlined by the compiler) - * returns the **line** of an entry in the ThreadView grid. */ + /* returns the **line** of an entry in the ThreadView grid. */ let get_entry_area = |idx: usize, entries: &[ThreadEntry]| { let entries = &entries; let visual_indentation = entries[idx].index.0 * 4; @@ -991,6 +990,7 @@ impl Component for ThreadView { } self.dirty = false; } + fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { if let UIEvent::Action(Listing(OpenInNewTab)) = event { /* Handle this before self.mailview does */ diff --git a/src/components/notifications.rs b/src/components/notifications.rs index c03f15da..a64c8e28 100644 --- a/src/components/notifications.rs +++ b/src/components/notifications.rs @@ -117,11 +117,8 @@ mod dbus { } if let Err(err) = notification.show() { - debug!("Could not show dbus notification: {:?}", &err); - melib::log( - format!("Could not show dbus notification: {}", err), - melib::ERROR, - ); + log::debug!("Could not show dbus notification: {:?}", &err); + log::error!("Could not show dbus notification: {err}"); } } false @@ -194,8 +191,8 @@ impl Component for NotificationCommand { if *kind == Some(NotificationType::NewMail) { if let Some(ref path) = context.settings.notifications.xbiff_file_path { if let Err(err) = update_xbiff(path) { - debug!("Could not update xbiff file: {:?}", &err); - melib::log(format!("Could not update xbiff file: {}.", err), ERROR); + log::debug!("Could not update xbiff file: {:?}", &err); + log::error!("Could not update xbiff file: {err}."); } } } @@ -220,11 +217,8 @@ impl Component for NotificationCommand { context.children.push(child); } Err(err) => { - log( - format!("Could not run notification script: {}.", err), - ERROR, - ); - debug!("Could not run notification script: {:?}", err); + log::error!("Could not run notification script: {err}."); + log::debug!("Could not run notification script: {:?}", err); } } } else { @@ -256,11 +250,8 @@ impl Component for NotificationCommand { return false; } Err(err) => { - log( - format!("Could not run notification script: {}.", err), - ERROR, - ); - debug!("Could not run notification script: {:?}", err); + log::error!("Could not run notification script: {err}."); + log::debug!("Could not run notification script: {:?}", err); } } } diff --git a/src/conf.rs b/src/conf.rs index 196718ed..83e5c5c6 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -32,7 +32,7 @@ use std::{ process::{Command, Stdio}, }; -use melib::{backends::TagHash, search::Query}; +use melib::{backends::TagHash, search::Query, StderrLogger}; use crate::{conf::deserializers::non_empty_string, terminal::Color}; @@ -610,6 +610,8 @@ pub struct Settings { pub pgp: PGPSettings, pub terminal: TerminalSettings, pub log: LogSettings, + #[serde(skip)] + _logger: StderrLogger, } impl Settings { @@ -624,11 +626,10 @@ impl Settings { s.insert(id, ac); } + let mut _logger = StderrLogger::new(fs.log.maximum_level); + if let Some(ref log_path) = fs.log.log_file { - melib::change_log_dest(log_path.into()); - } - if fs.log.maximum_level != melib::LoggingLevel::default() { - melib::change_log_level(fs.log.maximum_level); + _logger.change_log_dest(log_path.into()); } Ok(Settings { @@ -642,16 +643,16 @@ impl Settings { pgp: fs.pgp, terminal: fs.terminal, log: fs.log, + _logger, }) } pub fn without_accounts() -> Result { let fs = FileSettings::new()?; + let mut _logger = StderrLogger::new(fs.log.maximum_level); + if let Some(ref log_path) = fs.log.log_file { - melib::change_log_dest(log_path.into()); - } - if fs.log.maximum_level != melib::LoggingLevel::default() { - melib::change_log_level(fs.log.maximum_level); + _logger.change_log_dest(log_path.into()); } Ok(Settings { @@ -665,6 +666,7 @@ impl Settings { pgp: fs.pgp, terminal: fs.terminal, log: fs.log, + _logger, }) } } @@ -1015,7 +1017,7 @@ pub struct LogSettings { #[serde(default)] pub log_file: Option, #[serde(default)] - pub maximum_level: melib::LoggingLevel, + pub maximum_level: melib::LogLevel, } pub use dotaddressable::*; @@ -1047,7 +1049,7 @@ mod dotaddressable { impl DotAddressable for crate::terminal::Key {} impl DotAddressable for usize {} impl DotAddressable for Query {} - impl DotAddressable for melib::LoggingLevel {} + impl DotAddressable for melib::LogLevel {} impl DotAddressable for PathBuf {} impl DotAddressable for ToggleFlag {} impl DotAddressable for SearchBackend {} diff --git a/src/conf/accounts.rs b/src/conf/accounts.rs index 05b24e05..2571ae99 100644 --- a/src/conf/accounts.rs +++ b/src/conf/accounts.rs @@ -47,9 +47,10 @@ use melib::{ backends::*, email::*, error::{Error, ErrorKind, Result}, + log, text_processing::GlobMatch, thread::{SortField, SortOrder, Threads}, - AddressBook, Collection, + AddressBook, Collection, LogLevel, }; use smallvec::SmallVec; @@ -127,12 +128,10 @@ impl MailboxEntry { ret.path = melib::backends::utf7::decode_utf7_imap(&ret.path); } Some(other) => { - melib::log( - format!( - "mailbox `{}`: unrecognized mailbox name charset: {}", - &ret.name, other - ), - melib::WARN, + log::warn!( + "mailbox `{}`: unrecognized mailbox name charset: {}", + &ret.name, + other ); } } @@ -199,7 +198,7 @@ pub enum JobRequest { }, Generic { name: Cow<'static, str>, - logging_level: melib::LoggingLevel, + log_level: LogLevel, handle: JoinHandle>, on_finish: Option, }, @@ -636,13 +635,11 @@ impl Account { } for missing_mailbox in &mailbox_conf_hash_set { - melib::log( - format!( - "Account `{}` mailbox `{}` configured but not present in account's mailboxes. \ - Is it misspelled?", - &self.name, missing_mailbox, - ), - melib::WARN, + log::warn!( + "Account `{}` mailbox `{}` configured but not present in account's mailboxes. Is \ + it misspelled?", + &self.name, + missing_mailbox, ); self.sender .send(ThreadEvent::UIEvent(UIEvent::StatusEvent( @@ -667,12 +664,10 @@ impl Account { }); mailbox_comma_sep_list_string .drain(mailbox_comma_sep_list_string.len().saturating_sub(2)..); - melib::log( - format!( - "Account `{}` has the following mailboxes: [{}]", - &self.name, mailbox_comma_sep_list_string, - ), - melib::WARN, + log::warn!( + "Account `{}` has the following mailboxes: [{}]", + &self.name, + mailbox_comma_sep_list_string, ); self.sender .send(ThreadEvent::UIEvent(UIEvent::StatusEvent( @@ -766,13 +761,10 @@ impl Account { ) }) { Err(err) => { - melib::log( - format!( - "Failed to update envelope {} in cache: {}", - envelope.message_id_display(), - err - ), - melib::ERROR, + log::error!( + "Failed to update envelope {} in cache: {}", + envelope.message_id_display(), + err ); } Ok(job) => { @@ -786,7 +778,7 @@ impl Account { ) .into(), handle, - logging_level: melib::LoggingLevel::TRACE, + log_level: LogLevel::TRACE, on_finish: None, }, ); @@ -833,20 +825,17 @@ impl Account { ) .into(), handle, - logging_level: melib::LoggingLevel::TRACE, + log_level: LogLevel::TRACE, on_finish: None, }, ); } Err(err) => { - melib::log( - format!( - "Failed to update envelope {} in cache: {}", - self.collection.envelopes.read().unwrap()[&env_hash] - .message_id_display(), - err - ), - melib::ERROR, + log::error!( + "Failed to update envelope {} in cache: {}", + self.collection.envelopes.read().unwrap()[&env_hash] + .message_id_display(), + err ); } } @@ -869,14 +858,11 @@ impl Account { ) }) { Err(err) => { - melib::log( - format!( - "Failed to update envelope {} in cache: {}", - &self.collection.envelopes.read().unwrap()[&new_hash] - .message_id_display(), - err - ), - melib::ERROR, + log::error!( + "Failed to update envelope {} in cache: {}", + &self.collection.envelopes.read().unwrap()[&new_hash] + .message_id_display(), + err ); } Ok(job) => { @@ -891,7 +877,7 @@ impl Account { ) .into(), handle, - logging_level: melib::LoggingLevel::TRACE, + log_level: LogLevel::TRACE, on_finish: None, }, ); @@ -934,7 +920,7 @@ impl Account { ) .into(), handle, - logging_level: melib::LoggingLevel::TRACE, + log_level: LogLevel::TRACE, on_finish: None, }, ); @@ -996,14 +982,11 @@ impl Account { if self.settings.conf.search_backend == crate::conf::SearchBackend::Sqlite3 { if let Err(err) = crate::sqlite3::remove(env_hash) { let envelopes = self.collection.envelopes.read().unwrap(); - melib::log( - format!( - "Failed to remove envelope {} [{}] in cache: {}", - &envelopes[&env_hash].message_id_display(), - env_hash, - err - ), - melib::ERROR, + log::error!( + "Failed to remove envelope {} [{}] in cache: {}", + &envelopes[&env_hash].message_id_display(), + env_hash, + err ); } } @@ -1235,10 +1218,7 @@ impl Account { if let Some(mailbox_hash) = mailbox { if let Err(err) = self.save(bytes, *mailbox_hash, Some(flags)) { debug!("{:?} could not save msg", err); - melib::log( - format!("Could not save in '{}' mailbox: {}.", *mailbox_hash, err), - melib::ERROR, - ); + log::error!("Could not save in '{}' mailbox: {}.", *mailbox_hash, err); } else { saved_at = Some(*mailbox_hash); break; @@ -1253,12 +1233,9 @@ impl Account { } else { let file = crate::types::create_temp_file(bytes, None, None, false); debug!("message saved in {}", file.path.display()); - melib::log( - format!( - "Message was stored in {} so that you can restore it manually.", - file.path.display() - ), - melib::INFO, + log::info!( + "Message was stored in {} so that you can restore it manually.", + file.path.display() ); Err(Error::new(format!( "Message was stored in {} so that you can restore it manually.", @@ -1336,7 +1313,7 @@ impl Account { } let output = msmtp.wait().expect("Failed to wait on mailer"); if output.success() { - melib::log("Message sent.", melib::LoggingLevel::TRACE); + log::trace!("Message sent."); } else { let error_message = if let Some(exit_code) = output.code() { format!( @@ -1349,7 +1326,7 @@ impl Account { command ) }; - melib::log(&error_message, melib::LoggingLevel::ERROR); + log::error!("{}", error_message); return Err(Error::new(error_message).set_summary("Message not sent.")); } Ok(None) @@ -1426,7 +1403,7 @@ impl Account { } let output = msmtp.wait().expect("Failed to wait on mailer"); if output.success() { - melib::log("Message sent.", melib::LoggingLevel::TRACE); + log::trace!("Message sent."); } else { let error_message = if let Some(exit_code) = output.code() { format!( @@ -1440,7 +1417,7 @@ impl Account { command ) }; - melib::log(&error_message, melib::LoggingLevel::ERROR); + log::error!("{}", error_message); return Err(Error::new(error_message).set_summary("Message not sent.")); } Ok(()) @@ -1895,15 +1872,12 @@ impl Account { .. } => { if let Ok(Some(Err(err))) = handle.chan.try_recv() { - melib::log(format!("Could not save message: {}", err), melib::ERROR); + log::error!("Could not save message: {err}"); let file = crate::types::create_temp_file(bytes, None, None, false); - debug!("message saved in {}", file.path.display()); - melib::log( - format!( - "Message was stored in {} so that you can restore it manually.", - file.path.display() - ), - melib::INFO, + log::debug!("message saved in {}", file.path.display()); + log::info!( + "Message was stored in {} so that you can restore it manually.", + file.path.display() ); self.sender .send(ThreadEvent::UIEvent(UIEvent::Notification( @@ -2199,7 +2173,7 @@ impl Account { ref name, ref mut handle, ref mut on_finish, - logging_level, + log_level, } => { match handle.chan.try_recv() { Ok(Some(Err(err))) => { @@ -2212,7 +2186,7 @@ impl Account { .expect("Could not send event on main channel"); } Ok(Some(Ok(()))) if on_finish.is_none() => { - if logging_level <= melib::LoggingLevel::INFO { + if log_level <= LogLevel::INFO { self.sender .send(ThreadEvent::UIEvent(UIEvent::Notification( Some(format!("{}: {} succeeded", &self.name, name,)), diff --git a/src/mailcap.rs b/src/mailcap.rs index 1c8a1e0a..31955128 100644 --- a/src/mailcap.rs +++ b/src/mailcap.rs @@ -30,7 +30,7 @@ use std::{ process::{Command, Stdio}, }; -use melib::{email::Attachment, text_processing::GlobMatch, Error, Result}; +use melib::{email::Attachment, log, text_processing::GlobMatch, Error, Result}; use crate::{ state::Context, @@ -186,10 +186,7 @@ impl MailcapEntry { }) .collect::>(); let cmd_string = format!("{} {}", cmd, args.join(" ")); - melib::log( - format!("Executing: sh -c \"{}\"", cmd_string.replace('"', "\\\"")), - melib::DEBUG, - ); + log::debug!("Executing: sh -c \"{}\"", cmd_string.replace('"', "\\\"")); if copiousoutput { let out = if needs_stdin { let mut child = Command::new("sh") diff --git a/src/sqlite3.rs b/src/sqlite3.rs index be15ded7..521e1c8d 100644 --- a/src/sqlite3.rs +++ b/src/sqlite3.rs @@ -36,7 +36,7 @@ use melib::{ }, sqlite3::{self as melib_sqlite3, rusqlite::params, DatabaseDescription}, thread::{SortField, SortOrder}, - Error, Result, ERROR, + Error, Result, }; use smallvec::SmallVec; @@ -174,13 +174,9 @@ pub async fn insert( err ) ); - log( - format!( - "Failed to open envelope {}: {}", - envelope.message_id_display(), - err - ), - ERROR, + log::error!( + "Failed to open envelope {}: {err}", + envelope.message_id_display(), ); return Err(err); } @@ -195,13 +191,9 @@ pub async fn insert( envelope.message_id_display(), err ); - log( - format!( - "Failed to insert envelope {}: {}", - envelope.message_id_display(), - err - ), - ERROR, + log::error!( + "Failed to insert envelope {}: {err}", + envelope.message_id_display(), ); return Err(Error::new(err.to_string())); } @@ -246,18 +238,13 @@ pub async fn insert( ) .map_err(|e| Error::new(e.to_string())) { - debug!( - "Failed to insert envelope {}: {}", + log::debug!( + "Failed to insert envelope {}: {err}", envelope.message_id_display(), - err ); - log( - format!( - "Failed to insert envelope {}: {}", - envelope.message_id_display(), - err - ), - ERROR, + log::error!( + "Failed to insert envelope {}: {err}", + envelope.message_id_display(), ); } Ok(()) @@ -279,11 +266,8 @@ pub fn remove(env_hash: EnvelopeHash) -> Result<()> { ) .map_err(|e| Error::new(e.to_string())) { - debug!("Failed to remove envelope {}: {}", env_hash, err); - log( - format!("Failed to remove envelope {}: {}", env_hash, err), - ERROR, - ); + log::debug!("Failed to remove envelope {env_hash}: {err}"); + log::error!("Failed to remove envelope {env_hash}: {err}"); return Err(err); } Ok(()) diff --git a/src/state.rs b/src/state.rs index f917911b..89a6e204 100644 --- a/src/state.rs +++ b/src/state.rs @@ -881,7 +881,7 @@ impl State { name: "Message index rebuild".into(), handle, on_finish: None, - logging_level: melib::LoggingLevel::INFO, + log_level: LogLevel::INFO, }, ); self.context.replies.push_back(UIEvent::Notification( @@ -1080,15 +1080,13 @@ impl State { level, }, ) => { - log( - format!( - "{}: {}{}{}", - self.context.accounts[&account_hash].name(), - description.as_str(), - if content.is_some() { ": " } else { "" }, - content.as_ref().map(|s| s.as_str()).unwrap_or("") - ), - level, + log::log!( + level.into(), + "{}: {}{}{}", + self.context.accounts[&account_hash].name(), + description.as_str(), + if content.is_some() { ": " } else { "" }, + content.as_ref().map(|s| s.as_str()).unwrap_or("") ); self.rcv_event(UIEvent::StatusEvent(StatusEvent::DisplayMessage( description.to_string(), @@ -1208,7 +1206,7 @@ impl State { Ok(Some(_)) => true, Ok(None) => false, Err(err) => { - log(format!("Failed to wait on editor process: {}", err), ERROR); + log::error!("Failed to wait on editor process: {err}"); return None; } } @@ -1219,7 +1217,7 @@ impl State { Ok(Some(_)) => true, Ok(None) => false, Err(err) => { - log(format!("Failed to wait on child process: {}", err), ERROR); + log::error!("Failed to wait on child process: {err}"); return None; } } diff --git a/src/terminal/embed.rs b/src/terminal/embed.rs index 2e4b2b16..9c2ec299 100644 --- a/src/terminal/embed.rs +++ b/src/terminal/embed.rs @@ -27,7 +27,7 @@ use std::{ }, }; -use melib::{error::*, log, ERROR}; +use melib::{error::*, log}; #[cfg(not(target_os = "macos"))] use nix::{ fcntl::{open, OFlag}, @@ -123,23 +123,15 @@ pub fn create_pty( nix::unistd::setsid().unwrap(); match unsafe { set_controlling_terminal(slave_fd) } { Ok(c) if c < 0 => { - log( - format!( - "Could not execute `{}`: ioctl(fd, TIOCSCTTY, NULL) returned {}", - command, c, - ), - ERROR, + log::error!( + "Could not execute `{command}`: ioctl(fd, TIOCSCTTY, NULL) returned {c}", ); std::process::exit(c); } Ok(_) => {} Err(err) => { - log( - format!( - "Could not execute `{}`: ioctl(fd, TIOCSCTTY, NULL) returned {}", - command, err, - ), - ERROR, + log::error!( + "Could not execute `{command}`: ioctl(fd, TIOCSCTTY, NULL) returned {err}", ); std::process::exit(-1); } @@ -160,18 +152,15 @@ pub fn create_pty( &CString::new(command.as_bytes()).unwrap(), ], ) { - log(format!("Could not execute `{}`: {}", command, e,), ERROR); + log::error!("Could not execute `{command}`: {e}"); std::process::exit(-1); } } } - log( - format!( - "Could not execute `{}`: did not find the standard POSIX sh shell in PATH = {}", - command, - String::from_utf8_lossy(&path_var), - ), - ERROR, + log::error!( + "Could not execute `{command}`: did not find the standard POSIX sh shell in PATH \ + = {}", + String::from_utf8_lossy(&path_var), ); std::process::exit(-1); }