While external loggers can be used, if you are compiling with --features use_std, then it will default to just printing everything to the stdout unless a logger is created. This library can be used without the STD library, however, you will have to write an external logger.
211 lines
4.8 KiB
Rust
211 lines
4.8 KiB
Rust
//! This is the logging system used by different CyberMages projects.
|
|
//! This is meant to work with both std and non std Rust programs.
|
|
|
|
|
|
// 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<F>(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<Guard>
|
|
{
|
|
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<F>(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<F>(create_logger: F)
|
|
-> Result<(), u8>
|
|
where F: FnOnce() -> Box<Logger>
|
|
{
|
|
// 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<Box<Logger>, 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 =>
|
|
{
|
|
}
|
|
}
|
|
}
|