diff options
Diffstat (limited to 'libs/nak/src/logging.rs')
| -rw-r--r-- | libs/nak/src/logging.rs | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/libs/nak/src/logging.rs b/libs/nak/src/logging.rs new file mode 100644 index 0000000..8c0f91a --- /dev/null +++ b/libs/nak/src/logging.rs @@ -0,0 +1,99 @@ +//! Logging for Fluorine Manager. +//! +//! All log messages are: +//! 1. Written to `~/.var/app/com.fluorine.manager/logs/nak.log` (always) +//! 2. Forwarded to an optional callback set via `set_log_callback()` (for MOBase::log) + +use std::fs; +use std::io::Write; +use std::path::PathBuf; +use std::sync::Mutex; + +/// Signature for the log callback: (level, message) +/// +/// Levels: "info", "warning", "error", "install", "action", "download" +type LogCallback = Box<dyn Fn(&str, &str) + Send + Sync>; + +static LOG_CALLBACK: Mutex<Option<LogCallback>> = Mutex::new(None); + +/// Set the global log callback. Call once at startup from FFI. +pub fn set_log_callback(cb: impl Fn(&str, &str) + Send + Sync + 'static) { + if let Ok(mut guard) = LOG_CALLBACK.lock() { + *guard = Some(Box::new(cb)); + } +} + +/// Get the log directory path. +fn log_dir() -> PathBuf { + let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string()); + PathBuf::from(home).join(".var/app/com.fluorine.manager/logs") +} + +/// Get the log file path. +fn log_file_path() -> PathBuf { + log_dir().join("nak.log") +} + +fn emit(level: &str, message: &str) { + // Write to file (always, even before callback is set) + write_to_file(level, message); + + // Forward to callback if set + if let Ok(guard) = LOG_CALLBACK.lock() { + if let Some(ref cb) = *guard { + cb(level, message); + } + } +} + +fn write_to_file(level: &str, message: &str) { + let path = log_file_path(); + + // Ensure log directory exists (only try once per message, cheap no-op if exists) + if let Some(parent) = path.parent() { + let _ = fs::create_dir_all(parent); + } + + // Rotate if over 2MB + if let Ok(meta) = fs::metadata(&path) { + if meta.len() > 2 * 1024 * 1024 { + let old = path.with_extension("log.old"); + let _ = fs::rename(&path, &old); + } + } + + let Ok(mut file) = fs::OpenOptions::new() + .create(true) + .append(true) + .open(&path) + else { + return; + }; + + let timestamp = chrono::Local::now().format("%H:%M:%S"); + let _ = writeln!(file, "[{}] [{}] {}", timestamp, level.to_uppercase(), message); +} + +pub fn log_info(message: &str) { + emit("info", message); +} + +pub fn log_warning(message: &str) { + emit("warning", message); +} + +pub fn log_error(message: &str) { + emit("error", message); +} + +pub fn log_install(message: &str) { + emit("install", message); +} + +pub fn log_action(message: &str) { + emit("action", message); +} + +pub fn log_download(message: &str) { + emit("download", message); +} |
