sheen 0.3.0

A polished, colorful logging library for Rust
Documentation
use owo_colors::OwoColorize;

use crate::Level;
use std::fmt::Debug;

pub trait Formatter: Send + Sync {
    fn format(
        &self,
        level: Level,
        message: &str,
        timestamp: Option<&str>,
        prefix: Option<&str>,
        fields: &[(String, String)],
        extra: &[(&str, &dyn Debug)],
    ) -> String;
}

pub struct TextFormatter {
    colorize: bool,
}

impl TextFormatter {
    pub fn new(colorize: bool) -> Self {
        Self { colorize }
    }
}

impl Formatter for TextFormatter {
    fn format(
        &self,
        level: Level,
        message: &str,
        timestamp: Option<&str>,
        prefix: Option<&str>,
        fields: &[(String, String)],
        extra: &[(&str, &dyn Debug)],
    ) -> String {
        let mut output = String::new();

        if let Some(ts) = timestamp {
            output.push_str(ts);
            output.push(' ');
        };

        if let Some(p) = prefix {
            output.push_str(p);
            output.push(' ');
        };

        let level_str = format!("{:<5}", level.as_str());
        if self.colorize {
            let colored = match level {
                Level::Trace => level_str.dimmed().to_string(),
                Level::Debug => level_str.magenta().to_string(),
                Level::Info => level_str.cyan().to_string(),
                Level::Warn => level_str.yellow().to_string(),
                Level::Error => level_str.red().to_string(),
            };
            output.push_str(&colored);
        } else {
            output.push_str(&level_str);
        }
        output.push(' ');

        output.push_str(message);

        for (key, value) in fields {
            output.push_str(&format!(" {}={}", key, value));
        }

        for (key, value) in extra {
            output.push_str(&format!(" {}={:?}", key, value));
        }
        output
    }
}

pub struct JsonFormatter;

impl Formatter for JsonFormatter {
    fn format(
        &self,
        level: Level,
        message: &str,
        timestamp: Option<&str>,
        prefix: Option<&str>,
        fields: &[(String, String)],
        extra: &[(&str, &dyn Debug)],
    ) -> String {
        let mut parts = vec![
            format!("\"level\":\"{}\"", level.as_str().to_lowercase()),
            format!("\"msg\":\"{}\"", message),
        ];

        if let Some(ts) = timestamp {
            parts.push(format!("\"time\":\"{}\"", ts));
        }

        if let Some(p) = prefix {
            parts.push(format!("\"prefix\":\"{}\"", p));
        }

        for (key, value) in fields {
            parts.push(format!("\"{}\":{}", key, value));
        }

        for (key, value) in extra {
            parts.push(format!("\"{}\":{:?}", key, value));
        }

        format!("{{{}}}", parts.join(","))
    }
}

pub struct LogFmtFormatter;

impl Formatter for LogFmtFormatter {
    fn format(
        &self,
        level: Level,
        message: &str,
        timestamp: Option<&str>,
        prefix: Option<&str>,
        fields: &[(String, String)],
        extra: &[(&str, &dyn Debug)],
    ) -> String {
        let mut parts = vec![
            format!("level={}", level.as_str().to_lowercase()),
            format!("msg=\"{}\"", message),
        ];

        if let Some(ts) = timestamp {
            parts.push(format!("time=\"{}\"", ts));
        }

        if let Some(p) = prefix {
            parts.push(format!("prefix=\"{}\"", p));
        }

        for (key, value) in fields {
            parts.push(format!("{}={}", key, value));
        }

        for (key, value) in extra {
            parts.push(format!("{}={:?}", key, value));
        }

        parts.join(" ")
    }
}