From 13a2cee8263052f98e866e2ea34e8797090600c4 Mon Sep 17 00:00:00 2001 From: Jason Travis Smith Date: Wed, 24 Aug 2016 11:41:48 -0400 Subject: [PATCH] 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. --- Cargo.lock | 2 +- Cargo.toml | 6 +- examples/logger.rs | 110 ++++++++++++++ examples/logging.rs | 9 +- src/faux_logger.rs | 54 +++++++ src/guard.rs | 66 ++++++++ src/{log_level.rs => level_filter.rs} | 3 +- src/levels.rs | 90 +++++++++++ src/lib.rs | 208 +++++++++++++++++++++++++- src/location.rs | 69 +++++++++ src/logger.rs | 17 +++ src/logger_states.rs | 79 ++++++++++ src/macros.rs | 121 +++++++++------ src/record.rs | 106 +++++++++++++ 14 files changed, 884 insertions(+), 56 deletions(-) create mode 100644 examples/logger.rs create mode 100644 src/faux_logger.rs create mode 100644 src/guard.rs rename src/{log_level.rs => level_filter.rs} (93%) create mode 100644 src/levels.rs create mode 100644 src/location.rs create mode 100644 src/logger.rs create mode 100644 src/logger_states.rs create mode 100644 src/record.rs diff --git a/Cargo.lock b/Cargo.lock index f248f0d..66cad88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,4 +1,4 @@ [root] name = "scribe" -version = "0.1.0" +version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 441b488..ceb9e0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,13 @@ [package] name = "scribe" -version = "0.1.0" +version = "0.5.0" authors = ["Jason Travis Smith "] description = "Handles logging." license = "" repository = "https://gitlab.com/CyberMages/scribe.git" documentation = "" keywords = ["scribe", "logging"] + +[features] +default = [] +use_std = [] diff --git a/examples/logger.rs b/examples/logger.rs new file mode 100644 index 0000000..f332882 --- /dev/null +++ b/examples/logger.rs @@ -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 + { + 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(); +} diff --git a/examples/logging.rs b/examples/logging.rs index bd1e00c..5e97d20 100644 --- a/examples/logging.rs +++ b/examples/logging.rs @@ -6,7 +6,14 @@ extern crate scribe; pub fn main() { 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!"); - 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!"); } diff --git a/src/faux_logger.rs b/src/faux_logger.rs new file mode 100644 index 0000000..d56fbb8 --- /dev/null +++ b/src/faux_logger.rs @@ -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); + } +} diff --git a/src/guard.rs b/src/guard.rs new file mode 100644 index 0000000..6dad20c --- /dev/null +++ b/src/guard.rs @@ -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 + } +} diff --git a/src/log_level.rs b/src/level_filter.rs similarity index 93% rename from src/log_level.rs rename to src/level_filter.rs index 4e5168b..26f5324 100644 --- a/src/log_level.rs +++ b/src/level_filter.rs @@ -3,8 +3,9 @@ use std::fmt::Display; +/// The different available logging levels. #[repr(u8)] -#[derive(Clone, Copy, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] pub enum LogLevel { /// Only errors will be written to the logs. diff --git a/src/levels.rs b/src/levels.rs new file mode 100644 index 0000000..dd5d38f --- /dev/null +++ b/src/levels.rs @@ -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 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 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()) + } +} diff --git a/src/lib.rs b/src/lib.rs index fb79352..df8967f 100644 --- a/src/lib.rs +++ b/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 // part of this library. -mod log_level; +mod guard; +mod levels; +mod location; +mod logger; +mod logger_states; 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(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 => + { + } + } +} diff --git a/src/location.rs b/src/location.rs new file mode 100644 index 0000000..0823ef0 --- /dev/null +++ b/src/location.rs @@ -0,0 +1,69 @@ +/// The Location where a log message was generated. +pub struct Location<'a> +{ + /// The module path where the message was genereated. + /// [::]* + 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()) + } +} diff --git a/src/logger.rs b/src/logger.rs new file mode 100644 index 0000000..6c3d6d3 --- /dev/null +++ b/src/logger.rs @@ -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); +} diff --git a/src/logger_states.rs b/src/logger_states.rs new file mode 100644 index 0000000..f3b37fc --- /dev/null +++ b/src/logger_states.rs @@ -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 for LoggerStates +{ + fn from(lvl: usize) -> LoggerStates + { + match lvl + { + 0 => LoggerStates::Uninitialized, + 1 => LoggerStates::Initializing, + 2 => LoggerStates::Initialized, + _ => LoggerStates::Uninitialized + } + } +} + +impl ::std::convert::From 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()) + } +} diff --git a/src/macros.rs b/src/macros.rs index f5fe8be..5adba6c 100644 --- a/src/macros.rs +++ b/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)] #[macro_export] macro_rules! generate_panic { ($($arg: tt)+) => ({ - use std::fmt; - - panic!(fmt::format(format_args!($($arg)+))); + panic!(::std::fmt::format(format_args!($($arg)+))); }) } +/// This handles turning a module path into a domain name. #[doc(hidden)] #[macro_export] -macro_rules! log +macro_rules! determine_domain { - ($log_level: expr, $($arg: tt)+) => + () => ({ - // Specifically use the Path from std in this block. - 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() + match module_path!().find(":") { - Some(name) => + Some(pos) => { - match name.to_str() - { - Some(string) => - { - file_name = string; - } - - None => - { - } - } + &module_path!()[0..pos] } 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_rules! error { + (domain: $domain_name: expr, $($arg: tt)*) => + ({ + log!(domain: $domain_name, $crate::Levels::Error, $($arg)*); + generate_panic!($($arg)*); + }); + ($($arg: tt)*) => ({ - use scribe::LogLevel; - - - log!(LogLevel::ERROR, $($arg)*); + log!($crate::Levels::Error, $($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_rules! warn { + (domain: $domain_name: expr, $($arg: tt)*) => + ({ + log!(domain: $domain_name, $crate::Levels::Warn, $($arg)*); + }); + ($($arg: tt)*) => ({ - use scribe::LogLevel; - - log!(LogLevel::WARN, $($arg)*); - }) + log!($crate::Levels::Warn, $($arg)*); + }); } +/// Logs a message at the Info severity Level. +/// +/// This is for general useful information. #[macro_export] macro_rules! info { + (domain: $domain_name: expr, $($arg: tt)*) => + ({ + log!(domain: $domain_name, $crate::Levels::Info, $($arg)*); + }); + ($($arg: tt)*) => ({ - use scribe::LogLevel; - - log!(LogLevel::INFO, $($arg)*); + log!($crate::Levels::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_rules! debug { + (domain: $domain_name: expr, $($arg: tt)*) => + ({ + if cfg!(debug_assertions) == true + { + log!(domain: $domain_name, $crate::Levels::Debug, $($arg)*); + } + }); + ($($arg: tt)*) => ({ - use scribe::LogLevel; - // Only log this if we are currently in a debug build. if cfg!(debug_assertions) == true { - log!(LogLevel::DEBUG, $($arg)*); + log!($crate::Levels::Debug, $($arg)*); } - }) + }); } diff --git a/src/record.rs b/src/record.rs new file mode 100644 index 0000000..f456df5 --- /dev/null +++ b/src/record.rs @@ -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()) + } +}