-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1a16442
Showing
7 changed files
with
910 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/.idea | ||
/target | ||
*.iml | ||
Cargo.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[package] | ||
name = "mdlog" | ||
authors = ["Michal Douša <[email protected]>"] | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
chrono = "0.4" |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# mdlog | ||
|
||
A logging utility written in Rust. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
use std::borrow::Borrow; | ||
use std::borrow::BorrowMut; | ||
use std::io; | ||
|
||
use crate::Logger; | ||
use crate::LogLevel; | ||
|
||
type LoggerVec = Vec<Box<dyn Logger + Send + Sync>>; | ||
|
||
/// Represents a [`Logger`] that consists of many [`Logger`]s. | ||
/// | ||
/// [`CompositeLogger`] is used through its [`Borrow`] and [`BorrowMut`] | ||
/// implementations which yield a borrow to the inner [`Vec`] of [`Logger`]s. | ||
pub struct CompositeLogger (LoggerVec); | ||
|
||
impl CompositeLogger { | ||
/// Creates a new [`CompositeLogger`] instance. | ||
pub fn new() -> Self { | ||
Self(Vec::new()) | ||
} | ||
|
||
/// Creates a new [`CompositeLogger`] with given capacity of the underlying | ||
/// [`Vec`]. See [`Vec::with_capacity`] method for more information. | ||
pub fn with_capacity(capacity: usize) -> Self { | ||
Self(Vec::with_capacity(capacity)) | ||
} | ||
} | ||
|
||
impl Logger for CompositeLogger { | ||
fn log(&mut self, log_level: LogLevel, message: &str) -> io::Result<()> { | ||
for logger in &mut self.0 { | ||
logger.log(log_level, message)?; | ||
} | ||
Result::Ok(()) | ||
} | ||
} | ||
|
||
impl Borrow<LoggerVec> for CompositeLogger { | ||
fn borrow(&self) -> &LoggerVec { | ||
&self.0 | ||
} | ||
} | ||
|
||
impl BorrowMut<LoggerVec> for CompositeLogger { | ||
fn borrow_mut(&mut self) -> &mut LoggerVec { | ||
&mut self.0 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
pub mod composite; | ||
pub mod text; | ||
|
||
pub use self::composite::CompositeLogger; | ||
pub use self::text::TextLogger; | ||
|
||
use std::cmp::Ordering; | ||
use std::fmt; | ||
use std::fmt::Display; | ||
use std::fmt::Formatter; | ||
use std::io; | ||
|
||
/// Trait for implementing a logging utility. | ||
/// | ||
/// This trait has only one method, that is [`log`]. This method is called whenever | ||
/// some operation, that may be important, happens. | ||
/// | ||
/// Note that [`Logger`] trait does not specify how given message is logged nor | ||
/// where it should be logged to. | ||
pub trait Logger { | ||
/// Method for logging a message. | ||
/// | ||
/// # Parameters | ||
/// | ||
/// - `log_level`: [`LogLevel`] of given message | ||
/// - `message`: message text to be logged | ||
/// | ||
/// # Return value | ||
/// | ||
/// [`std::io::Result`]`<()>` indicating, whether the message was successfully logged | ||
fn log(&mut self, log_level: LogLevel, message: &str) -> io::Result<()>; | ||
} | ||
|
||
/// Represents how important a log message is. | ||
#[derive(Clone, Copy, Debug, Eq, PartialEq)] | ||
pub enum LogLevel { | ||
/// [`LogLevel`] for debugging purposes. | ||
Debug, | ||
|
||
/// [`LogLevel`] for informational messages. | ||
Info, | ||
|
||
/// [`LogLevel`] for messages that warns the user of application about potential risks. | ||
Warning, | ||
|
||
/// [`LogLevel`] for messages that informs the user that something went wrong and the error is | ||
/// cannot be corrected, but the application can continue running. | ||
Error, | ||
|
||
/// [`LogLevel`] for messages that informs the user that something went wrong and the error is | ||
/// unrecoverable and the application cannot continue. | ||
Fatal, | ||
} | ||
|
||
impl LogLevel { | ||
/// Describes the [`LogLevel`] as a number ([`u8`]). | ||
/// | ||
/// # Return value | ||
/// | ||
/// [`LogLevel`] represented as an [`u8`] | ||
pub fn numeric_level(&self) -> u8 { | ||
match self { | ||
Self::Debug => 0, | ||
Self::Info => 1, | ||
Self::Warning => 2, | ||
Self::Error => 3, | ||
Self::Fatal => 4, | ||
} | ||
} | ||
} | ||
|
||
impl Display for LogLevel { | ||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | ||
writeln!(f, "{}", | ||
match self { | ||
Self::Debug => "DEBUG", | ||
Self::Info => "INFO", | ||
Self::Warning => "WARN", | ||
Self::Error => "ERROR", | ||
Self::Fatal => "FATAL", | ||
} | ||
) | ||
} | ||
} | ||
|
||
impl PartialOrd for LogLevel { | ||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
Option::Some(self.cmp(other)) | ||
} | ||
} | ||
|
||
impl Ord for LogLevel { | ||
fn cmp(&self, other: &Self) -> Ordering { | ||
self.numeric_level().cmp(&other.numeric_level()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
use std::io; | ||
use std::io::Write; | ||
use std::thread; | ||
|
||
use chrono::Local; | ||
|
||
use crate::Logger; | ||
use crate::LogLevel; | ||
|
||
/// [`Logger`] implementation that writes log messages formatted as UTF-8 to given | ||
/// output stream. The output stream can be anything, from [`Stdout`] to [`File`] or | ||
/// [`TcpStream`]. | ||
/// | ||
/// [`File`]: std::fs::File | ||
/// [`Stdout`]: std::io::Stdout | ||
/// [`TcpStream`]: std::net::TcpStream | ||
/// | ||
/// # Usage | ||
/// | ||
/// ```rust | ||
/// use mdlog::Logger; | ||
/// use mdlog::LogLevel; | ||
/// use mdlog::TextLogger; | ||
/// | ||
/// use std::io::stdout; | ||
/// | ||
/// fn main() { | ||
/// let mut logger = TextLogger::new(LogLevel::Warning, stdout()); | ||
/// // These messages will not get printed, because their log level is too low: | ||
/// logger.log(LogLevel::Debug, "This is a debug message"); | ||
/// logger.log(LogLevel::Info, "This is an informational message"); | ||
/// // These will get printed, because their log level is high enough: | ||
/// logger.log(LogLevel::Warning, "Something suspicious happened"); | ||
/// logger.log(LogLevel::Error, "Oh, no! An error occurred!"); | ||
/// logger.log(LogLevel::Fatal, "NO! FATAL ERROR! Application is shut down!"); | ||
/// } | ||
/// ``` | ||
pub struct TextLogger<W> | ||
where W: Write { | ||
min_log_level: LogLevel, | ||
writer: W, | ||
} | ||
|
||
impl<W> TextLogger<W> | ||
where W: Write { | ||
/// Creates a new [`TextLogger`] instance with given name, minimum log level and | ||
/// output stream. | ||
/// | ||
/// # Parameters | ||
/// | ||
/// - `name`: name of the logger | ||
/// - `min_log_level`: minimum log level | ||
/// - `writer`: an output stream the log is written into | ||
pub fn new(min_log_level: LogLevel, writer: W) -> Self { | ||
Self { | ||
min_log_level, | ||
writer, | ||
} | ||
} | ||
|
||
/// Returns the minimum log level of the [`Logger`] instance. | ||
pub fn min_log_level(&self) -> LogLevel { | ||
self.min_log_level | ||
} | ||
} | ||
|
||
impl<W> Logger for TextLogger<W> | ||
where W: Write { | ||
fn log(&mut self, log_level: LogLevel, message: &str) -> io::Result<()> { | ||
// Do not do anything if log level is too low: | ||
if log_level < self.min_log_level { return Result::Ok(()) } | ||
// Write to the specified stream: | ||
writeln!(self.writer, "[{} {}]@{}: {}", | ||
thread::current().name().unwrap_or(""), | ||
log_level, Local::now(), message) | ||
} | ||
} |