//! This is the logging system used by different CyberMages projects. //! This is meant to work with both std and non std Rust programs. #![doc(html_logo_url="", html_favicon_url="http://cybermagesllc.com/favicon.ico", html_root_url="http://cybermagesllc.com")] #![warn(missing_docs)] // Handle using the core or the std of Rust depending on the chosen feature. #![cfg_attr(not(feature="use_std"), no_std)] // This crate can only use parts of Rust that are in both the core // and the standard library. This way the logging system will work for // libraries that use either. // // This is handled by coding using the std library and referencing the core // library as the std library if the core library is desired. #[cfg(not(feature="use_std"))] extern crate core as std; // Define the modules that are a // part of this library. mod guard; mod levels; mod location; mod logger; mod logger_states; mod macros; mod faux_logger; mod record; use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; use self::faux_logger::FauxLogger; use self::guard::Guard; use self::logger_states::LoggerStates; pub use self::levels::Levels; pub use self::location::Location; pub use self::logger::Logger; pub use self::record::Record; /// This is the currently set global Logger. /// It will default to a fake Logger that does nothing. static mut GLOBAL_LOGGER: *const Logger = &FauxLogger; /// The STATE of the GLOBAL_LOGGER. static STATE: AtomicUsize = ATOMIC_USIZE_INIT; /// Handles setting the GLOBAL_LOGGER and getting /// it ready to log new Records. fn set_logger_base(create_logger: F) -> Result<(), u8> where F: FnOnce() -> *const Logger { if STATE.compare_and_swap(LoggerStates::Uninitialized.into(), LoggerStates::Initializing.into(), Ordering::SeqCst) != LoggerStates::Uninitialized.into() { return Err(1); } unsafe { GLOBAL_LOGGER = create_logger(); } STATE.store(LoggerStates::Initialized.into(), Ordering::SeqCst); Ok(()) } /// Handles clearing the GLOBAL_LOGGER. /// This will return a pointer to the Logger that /// was previously set. fn clear_logger_base() -> Result<*const Logger, u8> { let logger: *const Logger; // Set to INITIALIZING to prevent re-initialization after if STATE.compare_and_swap(LoggerStates::Initialized.into(), LoggerStates::Initializing.into(), Ordering::SeqCst) != LoggerStates::Initialized.into() { return Err(2); } // Wait until there are no references alive to the // current GLOBAL_LOGGER. Guard::spin_until(0); // Change the GLOBAL_LOGGER to the fake one to unset it. unsafe { logger = GLOBAL_LOGGER; GLOBAL_LOGGER = &FauxLogger; Ok(logger) } } /// Get the current logger. /// /// This should be the FauxLogger if no logger has been set, /// or whatever logger has been set. If a logger has been /// cleared then this will return None. fn get_logger() -> Option { let guard: Guard; if STATE.load(Ordering::SeqCst) == LoggerStates::Initializing.into() { None } else { unsafe { guard = Guard::new(GLOBAL_LOGGER); } Some(guard) } } /// Set the Logger to use for logging. #[cfg(not(feature="use_std"))] pub fn set_logger(create_logger: F) -> Result<(), u8> where F: FnOnce() -> *const Logger { set_logger_base(create_logger) } /// Clear the logger currently being used. /// /// This will return a pointer to the Logger that /// was previously set. #[cfg(not(feature="use_std"))] pub fn clear_logger() -> Result<*const Logger, u8> { clear_logger_base() } /// Set the Logger to use for logging. #[cfg(feature="use_std")] pub fn set_logger(create_logger: F) -> Result<(), u8> where F: FnOnce() -> Box { // I hate using closures, but it is the only way I // can think of to handle this right now. unsafe { set_logger_base(|| ::std::mem::transmute(create_logger())) } } /// Clear the logger currently being used. /// /// This will return a pointer to the Logger that /// was previously set. #[cfg(feature="use_std")] pub fn clear_logger() -> Result, u8> { match clear_logger_base() { Ok(logger) => { unsafe { Ok(::std::mem::transmute(logger)) } } Err(error) => { Err(error) } } } /// Do NOT use outside of this crate. /// /// This records a log message. #[doc(hidden)] pub fn log_record(domain: &str, lvl: Levels, module_path: &str, file_name: &str, line_num: u32, message: ::std::fmt::Arguments) { let mut record: Record; match get_logger() { Some(logger) => { record = Record::new(domain, lvl, message); record.set_location(module_path, file_name, line_num); logger.log(&record) } None => { } } }