logfmt_logger 0.1.1

Logging interface implementing Heroku's logfmt.
Documentation
use std::io::Error;

use log::Record;

pub trait Format {
  fn write(&self, record: &Record) -> Result<(), Error>;
}

pub fn create_default_format() -> Box<dyn Format + Send + Sync> {
  #[cfg(feature = "color")]
  #[cfg(debug_assertions)]
  return Box::new(color::ColorFormat::default());
  #[cfg(not(debug_assertions))]
  return Box::new(default::DefaultFormat::default());
}

pub mod default {
  use std::io::Error;

  use log::Record;

  use crate::fmt::Format;

  #[derive(Default)]
  pub struct DefaultFormat;

  impl Format for DefaultFormat {
    fn write(&self, record: &Record) -> Result<(), Error> {
      let level = record.level();
      let level_name = level.to_string().to_lowercase();

      print!("level={}", level_name);
      print!(" message=\"{}\"", record.args());
      print!(" target=\"{}\"", record.target());
      println!();

      Ok(())
    }
  }
}

#[cfg(feature = "color")]
pub mod color {
  use std::collections::HashMap;
  use std::io::{Error, Write};

  use log::{Level, Record};
  use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

  use crate::fmt::Format;

  pub struct ColorFormat {
    levels: HashMap<Level, Color>,
  }

  impl ColorFormat {
    pub fn new() -> Self {
      Self {
        levels: vec![
          (Level::Error, Color::Red),
          (Level::Trace, Color::Magenta),
          (Level::Debug, Color::Cyan),
          (Level::Info, Color::Blue),
          (Level::Warn, Color::Yellow),
        ]
        .into_iter()
        .collect(),
      }
    }
  }

  impl Format for ColorFormat {
    fn write(&self, record: &Record) -> Result<(), Error> {
      let mut stdout = StandardStream::stdout(ColorChoice::Always);

      let level = record.level();
      let level_name = level.to_string().to_lowercase();
      let level_color = self.levels.get(&level).cloned();

      stdout.set_color(ColorSpec::new().set_fg(level_color))?;

      write!(&mut stdout, "[{}]", level_name)?;

      if level > Level::Error {
        stdout.set_color(
          ColorSpec::new()
            .set_fg(Some(Color::White))
            .set_intense(true),
        )?;
      }

      write!(&mut stdout, " {}", record.args())?;

      stdout.set_color(
        ColorSpec::new()
          .set_fg(Some(Color::White))
          .set_intense(false),
      )?;

      write!(&mut stdout, " target=\"{}\"", record.target())?;
      write!(&mut stdout, " message=\"{}\"", record.args())?;
      writeln!(&mut stdout)?;

      stdout.flush()
    }
  }

  impl Default for ColorFormat {
    fn default() -> Self {
      ColorFormat::new()
    }
  }
}