The Logger system can now be expanded with external logging libraries.
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.
This commit is contained in:
parent
c655eca358
commit
13a2cee826
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1,4 +1,4 @@
|
|||||||
[root]
|
[root]
|
||||||
name = "scribe"
|
name = "scribe"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
|
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "scribe"
|
name = "scribe"
|
||||||
version = "0.1.0"
|
version = "0.5.0"
|
||||||
authors = ["Jason Travis Smith <Jason@CyberMagesLLC.com>"]
|
authors = ["Jason Travis Smith <Jason@CyberMagesLLC.com>"]
|
||||||
description = "Handles logging."
|
description = "Handles logging."
|
||||||
license = ""
|
license = ""
|
||||||
repository = "https://gitlab.com/CyberMages/scribe.git"
|
repository = "https://gitlab.com/CyberMages/scribe.git"
|
||||||
documentation = ""
|
documentation = ""
|
||||||
keywords = ["scribe", "logging"]
|
keywords = ["scribe", "logging"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
use_std = []
|
||||||
|
110
examples/logger.rs
Normal file
110
examples/logger.rs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate scribe;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
use scribe::{Levels, Logger, Record};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub struct TestLogger
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestLogger
|
||||||
|
{
|
||||||
|
pub fn start()
|
||||||
|
{
|
||||||
|
match scribe::set_logger(TestLogger::set_logger)
|
||||||
|
{
|
||||||
|
Ok(_) =>
|
||||||
|
{
|
||||||
|
println!("Logger sucessfully set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(error) =>
|
||||||
|
{
|
||||||
|
println!("ERROR: {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop()
|
||||||
|
{
|
||||||
|
match scribe::clear_logger()
|
||||||
|
{
|
||||||
|
Ok(_) =>
|
||||||
|
{
|
||||||
|
println!("Logger cleared.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(error) =>
|
||||||
|
{
|
||||||
|
println!("ERROR: {}", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_logger() -> Box<Logger>
|
||||||
|
{
|
||||||
|
let logger: TestLogger;
|
||||||
|
|
||||||
|
logger =
|
||||||
|
TestLogger
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
Box::new(logger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Logger for TestLogger
|
||||||
|
{
|
||||||
|
fn is_domain_enabled(&self, domain_name: &'static str) -> bool
|
||||||
|
{
|
||||||
|
println!("Domain '{}': {}", domain_name, true);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_level_enabled(&self, lvl: Levels) -> bool
|
||||||
|
{
|
||||||
|
println!("Level '{}': {}", lvl, true);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, record: &Record)
|
||||||
|
{
|
||||||
|
println!("Record: {}", record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mod temp_mod
|
||||||
|
{
|
||||||
|
pub fn print()
|
||||||
|
{
|
||||||
|
info!("Mod test.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main()
|
||||||
|
{
|
||||||
|
TestLogger::start();
|
||||||
|
|
||||||
|
temp_mod::print();
|
||||||
|
|
||||||
|
debug!("This seemed to work alright.");
|
||||||
|
debug!(domain: "Temp Domain", "This seemed to work alright.");
|
||||||
|
|
||||||
|
info!("Jason is awesome.");
|
||||||
|
info!(domain: "Temp Domain", "Jason is awesome.");
|
||||||
|
|
||||||
|
warn!("Danger Will Robinson, danger!");
|
||||||
|
warn!(domain: "Temp Domain", "Danger Will Robinson, danger!");
|
||||||
|
|
||||||
|
//error!("There was an error!");
|
||||||
|
//error!(domain: "Temp Domain", "There was an error!");
|
||||||
|
|
||||||
|
|
||||||
|
TestLogger::stop();
|
||||||
|
}
|
@ -6,7 +6,14 @@ extern crate scribe;
|
|||||||
pub fn main()
|
pub fn main()
|
||||||
{
|
{
|
||||||
debug!("This seemed to work alright.");
|
debug!("This seemed to work alright.");
|
||||||
|
debug!(domain: "Temp Domain", "This seemed to work alright.");
|
||||||
|
|
||||||
info!("Jason is awesome.");
|
info!("Jason is awesome.");
|
||||||
|
info!(domain: "Temp Domain", "Jason is awesome.");
|
||||||
|
|
||||||
warn!("Danger Will Robinson, danger!");
|
warn!("Danger Will Robinson, danger!");
|
||||||
error!("There was an error!");
|
warn!(domain: "Temp Domain", "Danger Will Robinson, danger!");
|
||||||
|
|
||||||
|
//error!("There was an error!");
|
||||||
|
//error!(domain: "Temp Domain", "There was an error!");
|
||||||
}
|
}
|
||||||
|
54
src/faux_logger.rs
Normal file
54
src/faux_logger.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use ::levels::Levels;
|
||||||
|
use ::logger::Logger;
|
||||||
|
use ::record::Record;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// This is a fake Logger.
|
||||||
|
///
|
||||||
|
/// No domains or severity Levels are enabled,
|
||||||
|
/// and all logging calls are ignored.
|
||||||
|
pub struct FauxLogger;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// The fake Logger will do nothing if this is built
|
||||||
|
// to just use the core library.
|
||||||
|
#[cfg(not(feature="use_std"))]
|
||||||
|
impl Logger for FauxLogger
|
||||||
|
{
|
||||||
|
fn is_domain_enabled(&self, _domain_name: &'static str) -> bool
|
||||||
|
{
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_level_enabled(&self, _lvl: Levels) -> bool
|
||||||
|
{
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, _record: &Record)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When built using the STD library, the fake Logger will
|
||||||
|
// act as a simple console printer. This is to make development easier.
|
||||||
|
#[cfg(feature="use_std")]
|
||||||
|
impl Logger for FauxLogger
|
||||||
|
{
|
||||||
|
fn is_domain_enabled(&self, _domain_name: &'static str) -> bool
|
||||||
|
{
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_level_enabled(&self, _lvl: Levels) -> bool
|
||||||
|
{
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, record: &Record)
|
||||||
|
{
|
||||||
|
println!("{}", record);
|
||||||
|
}
|
||||||
|
}
|
66
src/guard.rs
Normal file
66
src/guard.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use std::ops::Deref;
|
||||||
|
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||||
|
|
||||||
|
use ::logger::Logger;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// The amount of references that have currently been created.
|
||||||
|
static REF_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// This is an access guard for the Logger.
|
||||||
|
/// It helps keep track of REF_COUNTS. Since there can be
|
||||||
|
/// only one GLOBAL_LOGGER that needs guarded, this is done
|
||||||
|
/// with static variables.
|
||||||
|
pub struct Guard
|
||||||
|
{
|
||||||
|
logger: &'static Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Guard
|
||||||
|
{
|
||||||
|
/// Create a new reference counted Guard.
|
||||||
|
pub fn new(global_logger: *const Logger) -> Guard
|
||||||
|
{
|
||||||
|
REF_COUNT.fetch_add(1, Ordering::SeqCst);
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
Guard
|
||||||
|
{
|
||||||
|
logger: &*global_logger
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Do a spin lock while waiting for the desired REF_COUNT to be hit.
|
||||||
|
/// Normally, this is waiting for REF_COUNT to be zero.
|
||||||
|
pub fn spin_until(desired_refcount: usize)
|
||||||
|
{
|
||||||
|
while REF_COUNT.load(Ordering::SeqCst) != desired_refcount
|
||||||
|
{
|
||||||
|
// It would be nice to sleep this thread here,
|
||||||
|
// but I'm not sure how to do that without the STD.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Guard
|
||||||
|
{
|
||||||
|
fn drop(&mut self)
|
||||||
|
{
|
||||||
|
REF_COUNT.fetch_sub(1, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Guard
|
||||||
|
{
|
||||||
|
type Target = Logger;
|
||||||
|
|
||||||
|
fn deref(&self) -> &(Logger + 'static)
|
||||||
|
{
|
||||||
|
self.logger
|
||||||
|
}
|
||||||
|
}
|
@ -3,8 +3,9 @@ use std::fmt::Display;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// The different available logging levels.
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[derive(Clone, Copy, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
pub enum LogLevel
|
pub enum LogLevel
|
||||||
{
|
{
|
||||||
/// Only errors will be written to the logs.
|
/// Only errors will be written to the logs.
|
90
src/levels.rs
Normal file
90
src/levels.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/// The different available logging levels.
|
||||||
|
// These only need to be represented as an unsigned
|
||||||
|
// integer as there won't be that many of these.
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
|
pub enum Levels
|
||||||
|
{
|
||||||
|
/// Designates a serious, program
|
||||||
|
/// stopping, error.
|
||||||
|
Error,
|
||||||
|
|
||||||
|
/// Designates a problem, but it is not
|
||||||
|
/// something that will stop the program
|
||||||
|
/// from running.
|
||||||
|
Warn,
|
||||||
|
|
||||||
|
/// Designates useful information.
|
||||||
|
Info,
|
||||||
|
|
||||||
|
/// Designates debugging only information.
|
||||||
|
Debug
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl Levels
|
||||||
|
{
|
||||||
|
/// Get a str representation of this variant.
|
||||||
|
pub fn to_str(&self) -> &'static str
|
||||||
|
{
|
||||||
|
match *self
|
||||||
|
{
|
||||||
|
Levels::Error => "Error",
|
||||||
|
Levels::Warn => "Warn",
|
||||||
|
Levels::Info => "Info",
|
||||||
|
Levels::Debug => "Debug"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the byte(u8) value of this variant.
|
||||||
|
pub fn get_val(&self) -> u8
|
||||||
|
{
|
||||||
|
match *self
|
||||||
|
{
|
||||||
|
Levels::Error => 0,
|
||||||
|
Levels::Warn => 1,
|
||||||
|
Levels::Info => 2,
|
||||||
|
Levels::Debug => 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::convert::From<u8> for Levels
|
||||||
|
{
|
||||||
|
fn from(lvl: u8) -> Levels
|
||||||
|
{
|
||||||
|
match lvl
|
||||||
|
{
|
||||||
|
0 => Levels::Error,
|
||||||
|
1 => Levels::Warn,
|
||||||
|
2 => Levels::Info,
|
||||||
|
3 => Levels::Debug,
|
||||||
|
_ => Levels::Debug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::convert::From<Levels> for u8
|
||||||
|
{
|
||||||
|
fn from(lvl: Levels) -> u8
|
||||||
|
{
|
||||||
|
lvl.get_val()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Debug for Levels
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result
|
||||||
|
{
|
||||||
|
write!(f, "{}", self.to_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Display for Levels
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result
|
||||||
|
{
|
||||||
|
write!(f, "{}", self.to_str())
|
||||||
|
}
|
||||||
|
}
|
208
src/lib.rs
208
src/lib.rs
@ -1,12 +1,210 @@
|
|||||||
///
|
//! 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
|
// Define the modules that are a
|
||||||
// part of this library.
|
// part of this library.
|
||||||
mod log_level;
|
mod guard;
|
||||||
|
mod levels;
|
||||||
|
mod location;
|
||||||
|
mod logger;
|
||||||
|
mod logger_states;
|
||||||
mod macros;
|
mod macros;
|
||||||
|
mod faux_logger;
|
||||||
|
mod record;
|
||||||
|
|
||||||
|
|
||||||
pub use self::log_level::LogLevel;
|
|
||||||
|
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 =>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
69
src/location.rs
Normal file
69
src/location.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/// The Location where a log message was generated.
|
||||||
|
pub struct Location<'a>
|
||||||
|
{
|
||||||
|
/// The module path where the message was genereated.
|
||||||
|
/// <crate_name>[::<module>]*
|
||||||
|
module_path: &'a str,
|
||||||
|
|
||||||
|
/// The name of the file where the message was generated.
|
||||||
|
file_name: &'a str,
|
||||||
|
|
||||||
|
/// The line of the file where the message was generated.
|
||||||
|
line: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl<'a> Location<'a>
|
||||||
|
{
|
||||||
|
/// Create a new Location.
|
||||||
|
///
|
||||||
|
/// This can easily be done using 'module_path!', 'file!',
|
||||||
|
/// 'line!'.
|
||||||
|
pub fn new(module: &'a str, file: &'a str, line_num: u32)
|
||||||
|
-> Location<'a>
|
||||||
|
{
|
||||||
|
Location
|
||||||
|
{
|
||||||
|
module_path: module,
|
||||||
|
file_name: file,
|
||||||
|
line: line_num
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the module path where the message was generated.
|
||||||
|
pub fn get_module_path(&self) -> &str
|
||||||
|
{
|
||||||
|
self.module_path
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the name of the file where the message was generated.
|
||||||
|
pub fn get_file_name(&self) -> &str
|
||||||
|
{
|
||||||
|
self.file_name
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the line number of the file where the
|
||||||
|
/// message was generated.
|
||||||
|
pub fn get_line_number(&self) -> u32
|
||||||
|
{
|
||||||
|
self.line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ::std::fmt::Debug for Location<'a>
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result
|
||||||
|
{
|
||||||
|
write!(f, "{}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ::std::fmt::Display for Location<'a>
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result
|
||||||
|
{
|
||||||
|
write!(f, "{}::{}::{}", self.get_module_path(),
|
||||||
|
self.get_file_name(), self.get_line_number())
|
||||||
|
}
|
||||||
|
}
|
17
src/logger.rs
Normal file
17
src/logger.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use ::levels::Levels;
|
||||||
|
use ::record::Record;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Defines the minimum requirements for a Logger.
|
||||||
|
pub trait Logger: Send + Sync
|
||||||
|
{
|
||||||
|
/// Query if the given domain has logging enabled or disabled.
|
||||||
|
fn is_domain_enabled(&self, domain_name: &'static str) -> bool;
|
||||||
|
|
||||||
|
/// Query if the given severity Level has logging enabled or disabled.
|
||||||
|
fn is_level_enabled(&self, lvl: Levels) -> bool;
|
||||||
|
|
||||||
|
/// Log the given Record.
|
||||||
|
fn log(&self, record: &Record);
|
||||||
|
}
|
79
src/logger_states.rs
Normal file
79
src/logger_states.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/// The different states the the GLOBAL_LOGGER can be in.
|
||||||
|
#[repr(usize)]
|
||||||
|
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
|
pub enum LoggerStates
|
||||||
|
{
|
||||||
|
/// The GLOBAL_LOGGER has not been initialized yet.
|
||||||
|
Uninitialized,
|
||||||
|
|
||||||
|
/// The GLOBAL_LOGGER is currently being initialized.
|
||||||
|
Initializing,
|
||||||
|
|
||||||
|
/// The GLOBAL_LOGGER is initialized and ready to be used.
|
||||||
|
Initialized
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl LoggerStates
|
||||||
|
{
|
||||||
|
/// Get a str representation of this variant.
|
||||||
|
pub fn to_str(&self) -> &'static str
|
||||||
|
{
|
||||||
|
match *self
|
||||||
|
{
|
||||||
|
LoggerStates::Uninitialized => "Uninitialized",
|
||||||
|
LoggerStates::Initializing => "Initializing",
|
||||||
|
LoggerStates::Initialized => "Initialized"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the value of this variant.
|
||||||
|
pub fn get_val(&self) -> usize
|
||||||
|
{
|
||||||
|
match *self
|
||||||
|
{
|
||||||
|
LoggerStates::Uninitialized => 0,
|
||||||
|
LoggerStates::Initializing => 1,
|
||||||
|
LoggerStates::Initialized => 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::convert::From<usize> for LoggerStates
|
||||||
|
{
|
||||||
|
fn from(lvl: usize) -> LoggerStates
|
||||||
|
{
|
||||||
|
match lvl
|
||||||
|
{
|
||||||
|
0 => LoggerStates::Uninitialized,
|
||||||
|
1 => LoggerStates::Initializing,
|
||||||
|
2 => LoggerStates::Initialized,
|
||||||
|
_ => LoggerStates::Uninitialized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::convert::From<LoggerStates> for usize
|
||||||
|
{
|
||||||
|
fn from(state: LoggerStates) -> usize
|
||||||
|
{
|
||||||
|
state.get_val()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Debug for LoggerStates
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result
|
||||||
|
{
|
||||||
|
write!(f, "{}", self.to_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Display for LoggerStates
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result
|
||||||
|
{
|
||||||
|
write!(f, "{}", self.to_str())
|
||||||
|
}
|
||||||
|
}
|
121
src/macros.rs
121
src/macros.rs
@ -1,103 +1,130 @@
|
|||||||
|
/// This will force a panic to occur with the given arguments.
|
||||||
|
/// This is mostly used from the error! macro.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! generate_panic
|
macro_rules! generate_panic
|
||||||
{
|
{
|
||||||
($($arg: tt)+) =>
|
($($arg: tt)+) =>
|
||||||
({
|
({
|
||||||
use std::fmt;
|
panic!(::std::fmt::format(format_args!($($arg)+)));
|
||||||
|
|
||||||
panic!(fmt::format(format_args!($($arg)+)));
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This handles turning a module path into a domain name.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! log
|
macro_rules! determine_domain
|
||||||
{
|
{
|
||||||
($log_level: expr, $($arg: tt)+) =>
|
() =>
|
||||||
({
|
({
|
||||||
// Specifically use the Path from std in this block.
|
match module_path!().find(":")
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
let file_path: &Path;
|
|
||||||
let mut file_name: &str;
|
|
||||||
|
|
||||||
file_name = file!();
|
|
||||||
file_path = Path::new(file!());
|
|
||||||
match file_path.file_name()
|
|
||||||
{
|
{
|
||||||
Some(name) =>
|
Some(pos) =>
|
||||||
{
|
{
|
||||||
match name.to_str()
|
&module_path!()[0..pos]
|
||||||
{
|
|
||||||
Some(string) =>
|
|
||||||
{
|
|
||||||
file_name = string;
|
|
||||||
}
|
|
||||||
|
|
||||||
None =>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
None =>
|
None =>
|
||||||
{
|
{
|
||||||
|
module_path!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
println!("{0}: {1}::{2}::{3} -- {4}", $log_level,
|
|
||||||
module_path!(), file_name, line!(),
|
|
||||||
format_args!($($arg)+));
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This handles calling the actual logging function.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log
|
||||||
|
{
|
||||||
|
(domain: $domain_name: expr, $log_level: expr, $($arg: tt)+) =>
|
||||||
|
({
|
||||||
|
$crate::log_record($domain_name, $log_level, module_path!(),
|
||||||
|
file!(), line!(), format_args!($($arg)+));
|
||||||
|
});
|
||||||
|
|
||||||
|
($log_level: expr, $($arg: tt)+) =>
|
||||||
|
({
|
||||||
|
log!(domain: determine_domain!(), $log_level, $($arg)+);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Logs a message at the Error severity Level.
|
||||||
|
///
|
||||||
|
/// This is for a serious, program stopping, problem.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! error
|
macro_rules! error
|
||||||
{
|
{
|
||||||
|
(domain: $domain_name: expr, $($arg: tt)*) =>
|
||||||
|
({
|
||||||
|
log!(domain: $domain_name, $crate::Levels::Error, $($arg)*);
|
||||||
|
generate_panic!($($arg)*);
|
||||||
|
});
|
||||||
|
|
||||||
($($arg: tt)*) =>
|
($($arg: tt)*) =>
|
||||||
({
|
({
|
||||||
use scribe::LogLevel;
|
log!($crate::Levels::Error, $($arg)*);
|
||||||
|
|
||||||
|
|
||||||
log!(LogLevel::ERROR, $($arg)*);
|
|
||||||
generate_panic!($($arg)*);
|
generate_panic!($($arg)*);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Logs a message at the Warn severity Level.
|
||||||
|
///
|
||||||
|
/// This is for a problem that will not stop the
|
||||||
|
/// program from running.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! warn
|
macro_rules! warn
|
||||||
{
|
{
|
||||||
|
(domain: $domain_name: expr, $($arg: tt)*) =>
|
||||||
|
({
|
||||||
|
log!(domain: $domain_name, $crate::Levels::Warn, $($arg)*);
|
||||||
|
});
|
||||||
|
|
||||||
($($arg: tt)*) =>
|
($($arg: tt)*) =>
|
||||||
({
|
({
|
||||||
use scribe::LogLevel;
|
log!($crate::Levels::Warn, $($arg)*);
|
||||||
|
});
|
||||||
log!(LogLevel::WARN, $($arg)*);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Logs a message at the Info severity Level.
|
||||||
|
///
|
||||||
|
/// This is for general useful information.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! info
|
macro_rules! info
|
||||||
{
|
{
|
||||||
|
(domain: $domain_name: expr, $($arg: tt)*) =>
|
||||||
|
({
|
||||||
|
log!(domain: $domain_name, $crate::Levels::Info, $($arg)*);
|
||||||
|
});
|
||||||
|
|
||||||
($($arg: tt)*) =>
|
($($arg: tt)*) =>
|
||||||
({
|
({
|
||||||
use scribe::LogLevel;
|
log!($crate::Levels::Info, $($arg)*);
|
||||||
|
|
||||||
log!(LogLevel::INFO, $($arg)*);
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Logs a message at the Debug severity Level.
|
||||||
|
///
|
||||||
|
/// This is for debugging only information.
|
||||||
|
///
|
||||||
|
/// Logging at this level is disabled when in release mode.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! debug
|
macro_rules! debug
|
||||||
{
|
{
|
||||||
|
(domain: $domain_name: expr, $($arg: tt)*) =>
|
||||||
|
({
|
||||||
|
if cfg!(debug_assertions) == true
|
||||||
|
{
|
||||||
|
log!(domain: $domain_name, $crate::Levels::Debug, $($arg)*);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
($($arg: tt)*) =>
|
($($arg: tt)*) =>
|
||||||
({
|
({
|
||||||
use scribe::LogLevel;
|
|
||||||
|
|
||||||
// Only log this if we are currently in a debug build.
|
// Only log this if we are currently in a debug build.
|
||||||
if cfg!(debug_assertions) == true
|
if cfg!(debug_assertions) == true
|
||||||
{
|
{
|
||||||
log!(LogLevel::DEBUG, $($arg)*);
|
log!($crate::Levels::Debug, $($arg)*);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
106
src/record.rs
Normal file
106
src/record.rs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
use ::levels::Levels;
|
||||||
|
use ::location::Location;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// A single message to be logged.
|
||||||
|
pub struct Record<'a>
|
||||||
|
{
|
||||||
|
/// The domain that the message is supposed to be
|
||||||
|
/// logged to.
|
||||||
|
domain: &'a str,
|
||||||
|
|
||||||
|
/// The severity Level for this message.
|
||||||
|
level: Levels,
|
||||||
|
|
||||||
|
/// The Location that the message was generated at.
|
||||||
|
location: Location<'a>,
|
||||||
|
|
||||||
|
/// The message to be logged.
|
||||||
|
message: ::std::fmt::Arguments<'a>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl<'a> Record<'a>
|
||||||
|
{
|
||||||
|
/// Create a new Record.
|
||||||
|
pub fn new(domain_name: &'a str, lvl: Levels,
|
||||||
|
message_data: ::std::fmt::Arguments<'a>)
|
||||||
|
-> Record<'a>
|
||||||
|
{
|
||||||
|
Record
|
||||||
|
{
|
||||||
|
domain: domain_name,
|
||||||
|
level: lvl,
|
||||||
|
location: Location::new("", "", 0),
|
||||||
|
message: message_data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the domain of this Record.
|
||||||
|
pub fn get_domain(&self) -> &str
|
||||||
|
{
|
||||||
|
self.domain
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the Level of this Record.
|
||||||
|
pub fn get_level(&self) -> Levels
|
||||||
|
{
|
||||||
|
self.level
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the Location where this Record was generated.
|
||||||
|
pub fn get_location(&self) -> &Location
|
||||||
|
{
|
||||||
|
&self.location
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the message that this Record holds.
|
||||||
|
pub fn get_message(&self) -> ::std::fmt::Arguments<'a>
|
||||||
|
{
|
||||||
|
self.message
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the domain that this Record is to be logged to.
|
||||||
|
pub fn set_domain(&mut self, domain_name: &'a str)
|
||||||
|
{
|
||||||
|
self.domain = domain_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the severity Level of the Record.
|
||||||
|
pub fn set_level(&mut self, lvl: Levels)
|
||||||
|
{
|
||||||
|
self.level = lvl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the Location where this Record was generated.
|
||||||
|
pub fn set_location(&mut self, module: &'a str, file: &'a str,
|
||||||
|
line: u32)
|
||||||
|
{
|
||||||
|
self.location = Location::new(module, file, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the message for this Record.
|
||||||
|
pub fn set_message(&mut self, message_data: ::std::fmt::Arguments<'a>)
|
||||||
|
{
|
||||||
|
self.message = message_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ::std::fmt::Debug for Record<'a>
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result
|
||||||
|
{
|
||||||
|
write!(f, "{}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ::std::fmt::Display for Record<'a>
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result
|
||||||
|
{
|
||||||
|
write!(f, "{}-{}: {} -- {}", self.get_domain(), self.get_level(),
|
||||||
|
self.get_location(), self.get_message())
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user