scribe/src/lib.rs

211 lines
4.8 KiB
Rust
Raw Normal View History

//! 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;
2015-12-09 17:51:31 -05:00
// Define the modules that are a
// part of this library.
mod guard;
mod levels;
mod location;
mod logger;
mod logger_states;
2015-12-09 17:51:31 -05:00
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);
2015-12-09 17:51:31 -05:00
logger.log(&record)
}
2015-12-09 17:51:31 -05:00
None =>
{
}
}
}