Skip to content

Commit

Permalink
Add stdout/console logger
Browse files Browse the repository at this point in the history
  • Loading branch information
tjardoo committed Sep 10, 2024
1 parent 70c15b6 commit 62cf15b
Show file tree
Hide file tree
Showing 10 changed files with 425 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ log = { version = "0.4", features = ["std"] }
chrono = "0.4"

[workspace]
members = ["examples/console"]
members = ["examples/console", "examples/stdout"]
61 changes: 61 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Ftail

Ftail is simple logging implementation for the `log` crate with support for multiple drivers.

- Stdout (standard output logging)
- Console (formatted output logging)
- Daily (daily log rotation)
- Single (single log file)

## Usage

```rust
use ftail::Ftail;

Ftail::new()
.add_driver(ConsoleLogger::new(), log::LevelFilter::Trace)
.init()?;

log::debug!("This is a debug message");
log::info!("This is an info message");
```

## Drivers

### Stdout

Logs to the standard output.

```sh
2024-09-10 14:41:57 TRACE stdout This is a trace message
2024-09-10 14:41:57 DEBUG stdout This is a debug message
2024-09-10 14:41:57 INFO foo bar
2024-09-10 14:41:57 WARN stdout This is a warning
2024-09-10 14:41:57 ERROR stdout This is an error
```

### Console

Logs to the standard output with formatted and colored output.

```sh
2024-09-10 14:42:21 · TRACE
This is a trace message
examples\console\src/main.rs:8

2024-09-10 14:42:21 · DEBUG
This is a debug message
examples\console\src/main.rs:10

2024-09-10 14:42:21 · INFO
bar
examples\console\src/main.rs:12

2024-09-10 14:42:21 · WARN
This is a warning
examples\console\src/main.rs:14

2024-09-10 14:42:21 · ERROR
This is an error
examples\console\src/main.rs:16
```
4 changes: 1 addition & 3 deletions examples/console/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use ftail::{drivers::console::ConsoleLogger, Ftail};

fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Hello, world!");

Ftail::new()
.add_driver(ConsoleLogger::new(), log::LevelFilter::Debug)
.add_driver(ConsoleLogger::new(), log::LevelFilter::Trace)
.init()?;

log::trace!("This is a trace message");
Expand Down
9 changes: 9 additions & 0 deletions examples/stdout/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "stdout"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
log = "0.4"
ftail = { path = "../../../ftail" }
19 changes: 19 additions & 0 deletions examples/stdout/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use ftail::{drivers::stdout::StdOutLogger, Ftail};

fn main() -> Result<(), Box<dyn std::error::Error>> {
Ftail::new()
.add_driver(StdOutLogger::new(), log::LevelFilter::Trace)
.init()?;

log::trace!("This is a trace message");

log::debug!("This is a debug message");

log::info!(target: "foo", "bar");

log::warn!("This is a warning");

log::error!("This is an error");

Ok(())
}
270 changes: 270 additions & 0 deletions src/ansi_escape.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
use std::fmt::{Display, Formatter, Result};

macro_rules! generate_styling_functions {
(
$(#[$enum_attrs:meta])*
$vis:vis enum $enum_name:ident {
$(
#[code = $code:literal]
$(#[$variant_attrs:meta])*
$variant:ident
),*
$(,)?
}
) => {
$(#[$enum_attrs])*
$vis enum $enum_name {
$(
$(#[$variant_attrs])*
$variant,
)*
}

impl $enum_name {
#[allow(dead_code)]
pub fn code(&self) -> u8 {
match self {
$(Self::$variant => $code,)*
}
}
}

impl $enum_name {
$(
#[allow(dead_code)]
pub fn $variant() -> Self {
Self::$variant
}
)*
}

#[allow(dead_code)]
pub trait TextStyling: Display {
$(
fn $variant(self) -> Style<Self>
where
Self: Sized,
{
self.style($enum_name::$variant)
}
)*
}

impl<T: Display> TextStyling for T {}
}
}

pub struct Style<T> {
text: T,
code: u8,
}

generate_styling_functions! {
#[allow(dead_code)]
#[allow(non_camel_case_types)]
pub enum StyleCode {
#[code = 30]
black,
#[code = 31]
red,
#[code = 32]
green,
#[code = 33]
yellow,
#[code = 34]
blue,
#[code = 35]
magenta,
#[code = 36]
cyan,
#[code = 37]
white,
#[code = 90]
bright_black,
#[code = 91]
bright_red,
#[code = 92]
bright_green,
#[code = 93]
bright_yellow,
#[code = 94]
bright_blue,
#[code = 95]
bright_magenta,
#[code = 96]
bright_cyan,
#[code = 97]
bright_white,
#[code = 40]
bg_black,
#[code = 41]
bg_red,
#[code = 42]
bg_green,
#[code = 43]
bg_yellow,
#[code = 44]
bg_blue,
#[code = 45]
bg_magenta,
#[code = 46]
bg_cyan,
#[code = 47]
bg_white,
#[code = 100]
bg_bright_black,
#[code = 101]
bg_bright_red,
#[code = 102]
bg_bright_green,
#[code = 103]
bg_bright_yellow,
#[code = 104]
bg_bright_blue,
#[code = 105]
bg_bright_magenta,
#[code = 106]
bg_bright_cyan,
#[code = 107]
bg_bright_white,
#[code = 1]
bold,
#[code = 3]
italic,
#[code = 4]
underline,
#[code = 9]
strikethrough,
}
}

pub trait GeneratedTextStyling: Display + TextStyling {
fn style(self, style_code: StyleCode) -> Style<Self>
where
Self: Sized,
{
Style {
text: self,
code: style_code.code(),
}
}
}

impl<T: Display> GeneratedTextStyling for T {}

impl<T: Display> Display for Style<T> {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "\x1b[{}m{}\x1b[0m", self.code, self.text)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_text_styling() {
let text = "Hello, world!";

let black = text.black();
let red = text.red();
let green = text.green();
let yellow = text.yellow();
let blue = text.blue();
let magenta = text.magenta();
let cyan = text.cyan();
let white = text.white();
let bright_black = text.bright_black();
let bright_red = text.bright_red();
let bright_green = text.bright_green();
let bright_yellow = text.bright_yellow();
let bright_blue = text.bright_blue();
let bright_magenta = text.bright_magenta();
let bright_cyan = text.bright_cyan();
let bright_white = text.bright_white();
let bg_black = text.bg_black();
let bg_red = text.bg_red();
let bg_green = text.bg_green();
let bg_yellow = text.bg_yellow();
let bg_blue = text.bg_blue();
let bg_magenta = text.bg_magenta();
let bg_cyan = text.bg_cyan();
let bg_white = text.bg_white();
let bg_bright_black = text.bg_bright_black();
let bg_bright_red = text.bg_bright_red();
let bg_bright_green = text.bg_bright_green();
let bg_bright_yellow = text.bg_bright_yellow();
let bg_bright_blue = text.bg_bright_blue();
let bg_bright_magenta = text.bg_bright_magenta();
let bg_bright_cyan = text.bg_bright_cyan();
let bg_bright_white = text.bg_bright_white();
let bold = text.bold();
let italic = text.italic();
let underline = text.underline();
let strikethrough = text.strikethrough();

assert_eq!(format!("{}", black), "\x1b[30mHello, world!\x1b[0m");
assert_eq!(format!("{}", red), "\x1b[31mHello, world!\x1b[0m");
assert_eq!(format!("{}", green), "\x1b[32mHello, world!\x1b[0m");
assert_eq!(format!("{}", yellow), "\x1b[33mHello, world!\x1b[0m");
assert_eq!(format!("{}", blue), "\x1b[34mHello, world!\x1b[0m");
assert_eq!(format!("{}", magenta), "\x1b[35mHello, world!\x1b[0m");
assert_eq!(format!("{}", cyan), "\x1b[36mHello, world!\x1b[0m");
assert_eq!(format!("{}", white), "\x1b[37mHello, world!\x1b[0m");
assert_eq!(format!("{}", bright_black), "\x1b[90mHello, world!\x1b[0m");
assert_eq!(format!("{}", bright_red), "\x1b[91mHello, world!\x1b[0m");
assert_eq!(format!("{}", bright_green), "\x1b[92mHello, world!\x1b[0m");
assert_eq!(format!("{}", bright_yellow), "\x1b[93mHello, world!\x1b[0m");
assert_eq!(format!("{}", bright_blue), "\x1b[94mHello, world!\x1b[0m");
assert_eq!(
format!("{}", bright_magenta),
"\x1b[95mHello, world!\x1b[0m"
);
assert_eq!(format!("{}", bright_cyan), "\x1b[96mHello, world!\x1b[0m");
assert_eq!(format!("{}", bright_white), "\x1b[97mHello, world!\x1b[0m");
assert_eq!(format!("{}", bg_black), "\x1b[40mHello, world!\x1b[0m");
assert_eq!(format!("{}", bg_red), "\x1b[41mHello, world!\x1b[0m");
assert_eq!(format!("{}", bg_green), "\x1b[42mHello, world!\x1b[0m");
assert_eq!(format!("{}", bg_yellow), "\x1b[43mHello, world!\x1b[0m");
assert_eq!(format!("{}", bg_blue), "\x1b[44mHello, world!\x1b[0m");
assert_eq!(format!("{}", bg_magenta), "\x1b[45mHello, world!\x1b[0m");
assert_eq!(format!("{}", bg_cyan), "\x1b[46mHello, world!\x1b[0m");
assert_eq!(format!("{}", bg_white), "\x1b[47mHello, world!\x1b[0m");
assert_eq!(
format!("{}", bg_bright_black),
"\x1b[100mHello, world!\x1b[0m"
);
assert_eq!(
format!("{}", bg_bright_red),
"\x1b[101mHello, world!\x1b[0m"
);
assert_eq!(
format!("{}", bg_bright_green),
"\x1b[102mHello, world!\x1b[0m"
);
assert_eq!(
format!("{}", bg_bright_yellow),
"\x1b[103mHello, world!\x1b[0m"
);
assert_eq!(
format!("{}", bg_bright_blue),
"\x1b[104mHello, world!\x1b[0m"
);
assert_eq!(
format!("{}", bg_bright_magenta),
"\x1b[105mHello, world!\x1b[0m"
);
assert_eq!(
format!("{}", bg_bright_cyan),
"\x1b[106mHello, world!\x1b[0m"
);
assert_eq!(
format!("{}", bg_bright_white),
"\x1b[107mHello, world!\x1b[0m"
);
assert_eq!(format!("{}", bold), "\x1b[1mHello, world!\x1b[0m");
assert_eq!(format!("{}", italic), "\x1b[3mHello, world!\x1b[0m");
assert_eq!(format!("{}", underline), "\x1b[4mHello, world!\x1b[0m");
assert_eq!(format!("{}", strikethrough), "\x1b[9mHello, world!\x1b[0m");
}
}
Loading

0 comments on commit 62cf15b

Please sign in to comment.