use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ApiLogLevel {
Debug,
Info,
Warn,
Error,
}
impl Default for ApiLogLevel {
fn default() -> Self {
ApiLogLevel::Info
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiLogEntry {
pub timestamp: String,
pub level: ApiLogLevel,
pub message: String,
pub details: Option<serde_json::Value>,
}
impl ApiLogEntry {
pub fn new(level: ApiLogLevel, message: impl Into<String>) -> Self {
Self {
timestamp: chrono::Utc::now().to_rfc3339(),
level,
message: message.into(),
details: None,
}
}
pub fn with_details(mut self, details: serde_json::Value) -> Self {
self.details = Some(details);
self
}
pub fn debug(message: impl Into<String>) -> Self {
Self::new(ApiLogLevel::Debug, message)
}
pub fn info(message: impl Into<String>) -> Self {
Self::new(ApiLogLevel::Info, message)
}
pub fn warn(message: impl Into<String>) -> Self {
Self::new(ApiLogLevel::Warn, message)
}
pub fn error(message: impl Into<String>) -> Self {
Self::new(ApiLogLevel::Error, message)
}
}
pub struct ApiLogger {
enabled: bool,
min_level: ApiLogLevel,
}
impl ApiLogger {
pub fn new() -> Self {
Self {
enabled: true,
min_level: ApiLogLevel::Info,
}
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
pub fn set_min_level(&mut self, level: ApiLogLevel) {
self.min_level = level;
}
pub fn log(&self, entry: &ApiLogEntry) {
if !self.enabled {
return;
}
let level_priority = match entry.level {
ApiLogLevel::Debug => 0,
ApiLogLevel::Info => 1,
ApiLogLevel::Warn => 2,
ApiLogLevel::Error => 3,
};
let min_priority = match self.min_level {
ApiLogLevel::Debug => 0,
ApiLogLevel::Info => 1,
ApiLogLevel::Warn => 2,
ApiLogLevel::Error => 3,
};
if level_priority >= min_priority {
eprintln!("[API] {:?}: {}", entry.level, entry.message);
}
}
}
impl Default for ApiLogger {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_api_log_entry_creation() {
let entry = ApiLogEntry::info("test message");
assert_eq!(entry.level, ApiLogLevel::Info);
assert_eq!(entry.message, "test message");
assert!(entry.details.is_none());
}
#[test]
fn test_api_log_entry_with_details() {
let entry = ApiLogEntry::info("test").with_details(serde_json::json!({"key": "value"}));
assert!(entry.details.is_some());
}
}