cirious_codex_logger 0.2.1

Structured logging library for the Cirious Codex ecosystem.
Documentation
use crate::Level;
use std::time::SystemTime;

/// Represents a captured log event.
///
/// A `Record` acts as the primary data structure for log information,
/// capturing both the message content and its source metadata.
#[derive(Clone, Debug)]
pub struct Record {
  /// The severity/verbosity level of the log entry.
  pub level: Level,
  /// The actual log message generated by the user.
  pub args: String,
  /// The source code file where the log occurred.
  pub file: &'static str,
  /// The source code line where the log occurred.
  pub line: u32,
  /// The module path where the log occurred.
  pub module_path: &'static str,
  /// The exact timestamp of the log creation.
  pub timestamp: SystemTime,
}

/// A trait for transforming log records into a specific output format.
///
/// Formatters are responsible for serializing a [`Record`] into a `String`.
/// This allows for flexible output styles ranging from human-readable terminal
/// logs to structured machine-parseable formats like JSON.
pub trait Formatter: Send + Sync {
  /// Formats the given log record into a `String`.
  fn format(&self, record: &Record) -> String;
}

/// A simple, human-readable formatter for terminal output.
///
/// Outputs records in a standard `[LEVEL] Message` format.
#[derive(Debug)]
pub struct HumanReadableFormatter;

impl Formatter for HumanReadableFormatter {
  fn format(&self, record: &Record) -> String {
    format!("[{:?}] {}", record.level, record.args)
  }
}

/// A structured JSON formatter for machine ingestion.
///
/// Outputs logs as single-line JSON strings. Ideal for log aggregators
/// like Datadog, ELK, or `CloudWatch`.
#[derive(Debug)]
pub struct JsonFormatter;

impl Formatter for JsonFormatter {
  fn format(&self, record: &Record) -> String {
    // Escaping double quotes in the message to ensure valid JSON output.
    let escaped_args = record.args.replace('"', "\\\"");
    format!(
      r#"{{"timestamp":{:?}, "level":"{:?}", "message":"{}", "module":"{}"}}"#,
      record.timestamp, record.level, escaped_args, record.module_path
    )
  }
}

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

  fn create_test_record(level: Level, message: &str) -> Record {
    Record {
      level,
      args: message.to_string(),
      file: "main.rs",
      line: 10,
      module_path: "app::network",
      timestamp: SystemTime::UNIX_EPOCH, // Fixed for deterministic testing
    }
  }

  #[test]
  fn test_human_readable_formatter() {
    let record = create_test_record(Level::Debug, "System initialized");
    let formatter = HumanReadableFormatter;
    assert_eq!(formatter.format(&record), "[Debug] System initialized");
  }

  #[test]
  fn test_json_formatter() {
    let record = create_test_record(Level::Info, "User login");
    let formatter = JsonFormatter;
    let result = formatter.format(&record);

    assert!(result.contains(r#""level":"Info""#));
    assert!(result.contains(r#""message":"User login""#));
    assert!(result.starts_with('{') && result.ends_with('}'));
  }
}