Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
dousamichal0807 committed Apr 10, 2022
0 parents commit 1a16442
Show file tree
Hide file tree
Showing 7 changed files with 910 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/.idea
/target
*.iml
Cargo.lock
8 changes: 8 additions & 0 deletions Cargo.toml
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"
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# mdlog

A logging utility written in Rust.
48 changes: 48 additions & 0 deletions src/composite.rs
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
}
}
96 changes: 96 additions & 0 deletions src/lib.rs
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())
}
}
77 changes: 77 additions & 0 deletions src/text.rs
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)
}
}

0 comments on commit 1a16442

Please sign in to comment.