| 
 | 1 | +//! Logging infrastructure for bitkit-core  | 
 | 2 | +
  | 
 | 3 | +use std::sync::Arc;  | 
 | 4 | +use once_cell::sync::OnceCell;  | 
 | 5 | + | 
 | 6 | +/// Global logger instance  | 
 | 7 | +static LOGGER: OnceCell<Arc<Logger>> = OnceCell::new();  | 
 | 8 | + | 
 | 9 | +/// Get the global logger instance  | 
 | 10 | +pub(crate) fn get_logger() -> Option<&'static Arc<Logger>> {  | 
 | 11 | +    LOGGER.get()  | 
 | 12 | +}  | 
 | 13 | + | 
 | 14 | +/// Set the global logger instance  | 
 | 15 | +///  | 
 | 16 | +/// This should be called once during application initialization.  | 
 | 17 | +/// Subsequent calls will be ignored.  | 
 | 18 | +pub fn set_logger(log_writer: Arc<dyn LogWriter>) {  | 
 | 19 | +    LOGGER.get_or_init(|| Arc::new(Logger::new_custom_writer(log_writer)));  | 
 | 20 | +}  | 
 | 21 | + | 
 | 22 | +/// Log level for filtering log messages  | 
 | 23 | +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, uniffi::Enum)]  | 
 | 24 | +pub enum LogLevel {  | 
 | 25 | +    /// Trace-level logs (most verbose)  | 
 | 26 | +    Trace,  | 
 | 27 | +    /// Debug-level logs  | 
 | 28 | +    Debug,  | 
 | 29 | +    /// Info-level logs  | 
 | 30 | +    Info,  | 
 | 31 | +    /// Warning-level logs  | 
 | 32 | +    Warn,  | 
 | 33 | +    /// Error-level logs (least verbose)  | 
 | 34 | +    Error,  | 
 | 35 | +}  | 
 | 36 | + | 
 | 37 | +impl std::fmt::Display for LogLevel {  | 
 | 38 | +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {  | 
 | 39 | +        match self {  | 
 | 40 | +            LogLevel::Trace => write!(f, "TRACE"),  | 
 | 41 | +            LogLevel::Debug => write!(f, "DEBUG"),  | 
 | 42 | +            LogLevel::Info => write!(f, "INFO"),  | 
 | 43 | +            LogLevel::Warn => write!(f, "WARN"),  | 
 | 44 | +            LogLevel::Error => write!(f, "ERROR"),  | 
 | 45 | +        }  | 
 | 46 | +    }  | 
 | 47 | +}  | 
 | 48 | + | 
 | 49 | +/// A log record containing metadata and the log message  | 
 | 50 | +#[derive(Debug, Clone, uniffi::Record)]  | 
 | 51 | +pub struct LogRecord {  | 
 | 52 | +    /// The log level  | 
 | 53 | +    pub level: LogLevel,  | 
 | 54 | +    /// The formatted log message  | 
 | 55 | +    pub message: String,  | 
 | 56 | +    /// The module path where the log was generated  | 
 | 57 | +    pub module_path: String,  | 
 | 58 | +    /// The line number where the log was generated  | 
 | 59 | +    pub line: u32,  | 
 | 60 | +}  | 
 | 61 | + | 
 | 62 | +/// Trait for custom log writers that can be implemented by FFI consumers  | 
 | 63 | +///  | 
 | 64 | +/// This trait should be implemented by Kotlin/Swift code to receive log messages  | 
 | 65 | +/// from the Rust library. When Rust code logs, it will call the `log` method  | 
 | 66 | +/// on the registered LogWriter implementation.  | 
 | 67 | +#[uniffi::export(with_foreign)]  | 
 | 68 | +pub trait LogWriter: Send + Sync {  | 
 | 69 | +    /// Process a log record  | 
 | 70 | +    fn log(&self, record: LogRecord);  | 
 | 71 | +}  | 
 | 72 | + | 
 | 73 | +/// Internal logger implementation  | 
 | 74 | +pub(crate) struct Logger {  | 
 | 75 | +    writer: Arc<dyn LogWriter>,  | 
 | 76 | +}  | 
 | 77 | + | 
 | 78 | +impl Logger {  | 
 | 79 | +    /// Create a new logger with a custom writer  | 
 | 80 | +    pub fn new_custom_writer(log_writer: Arc<dyn LogWriter>) -> Self {  | 
 | 81 | +        Self { writer: log_writer }  | 
 | 82 | +    }  | 
 | 83 | + | 
 | 84 | +    /// Log a message  | 
 | 85 | +    pub fn log(&self, record: LogRecord) {  | 
 | 86 | +        self.writer.log(record);  | 
 | 87 | +    }  | 
 | 88 | +}  | 
 | 89 | + | 
 | 90 | +/// Macro for trace-level logging  | 
 | 91 | +#[macro_export]  | 
 | 92 | +macro_rules! log_trace {  | 
 | 93 | +    ($logger:expr, $($arg:tt)*) => {  | 
 | 94 | +        if let Some(logger) = $logger.as_ref() {  | 
 | 95 | +            logger.log($crate::logger::LogRecord {  | 
 | 96 | +                level: $crate::logger::LogLevel::Trace,  | 
 | 97 | +                message: format!($($arg)*),  | 
 | 98 | +                module_path: module_path!().to_string(),  | 
 | 99 | +                line: line!(),  | 
 | 100 | +            });  | 
 | 101 | +        }  | 
 | 102 | +    };  | 
 | 103 | +}  | 
 | 104 | + | 
 | 105 | +/// Macro for debug-level logging  | 
 | 106 | +#[macro_export]  | 
 | 107 | +macro_rules! log_debug {  | 
 | 108 | +    ($logger:expr, $($arg:tt)*) => {  | 
 | 109 | +        if let Some(logger) = $logger.as_ref() {  | 
 | 110 | +            logger.log($crate::logger::LogRecord {  | 
 | 111 | +                level: $crate::logger::LogLevel::Debug,  | 
 | 112 | +                message: format!($($arg)*),  | 
 | 113 | +                module_path: module_path!().to_string(),  | 
 | 114 | +                line: line!(),  | 
 | 115 | +            });  | 
 | 116 | +        }  | 
 | 117 | +    };  | 
 | 118 | +}  | 
 | 119 | + | 
 | 120 | +/// Macro for info-level logging  | 
 | 121 | +#[macro_export]  | 
 | 122 | +macro_rules! log_info {  | 
 | 123 | +    ($logger:expr, $($arg:tt)*) => {  | 
 | 124 | +        if let Some(logger) = $logger.as_ref() {  | 
 | 125 | +            logger.log($crate::logger::LogRecord {  | 
 | 126 | +                level: $crate::logger::LogLevel::Info,  | 
 | 127 | +                message: format!($($arg)*),  | 
 | 128 | +                module_path: module_path!().to_string(),  | 
 | 129 | +                line: line!(),  | 
 | 130 | +            });  | 
 | 131 | +        }  | 
 | 132 | +    };  | 
 | 133 | +}  | 
 | 134 | + | 
 | 135 | +/// Macro for warning-level logging  | 
 | 136 | +#[macro_export]  | 
 | 137 | +macro_rules! log_warn {  | 
 | 138 | +    ($logger:expr, $($arg:tt)*) => {  | 
 | 139 | +        if let Some(logger) = $logger.as_ref() {  | 
 | 140 | +            logger.log($crate::logger::LogRecord {  | 
 | 141 | +                level: $crate::logger::LogLevel::Warn,  | 
 | 142 | +                message: format!($($arg)*),  | 
 | 143 | +                module_path: module_path!().to_string(),  | 
 | 144 | +                line: line!(),  | 
 | 145 | +            });  | 
 | 146 | +        }  | 
 | 147 | +    };  | 
 | 148 | +}  | 
 | 149 | + | 
 | 150 | +/// Macro for error-level logging  | 
 | 151 | +#[macro_export]  | 
 | 152 | +macro_rules! log_error {  | 
 | 153 | +    ($logger:expr, $($arg:tt)*) => {  | 
 | 154 | +        if let Some(logger) = $logger.as_ref() {  | 
 | 155 | +            logger.log($crate::logger::LogRecord {  | 
 | 156 | +                level: $crate::logger::LogLevel::Error,  | 
 | 157 | +                message: format!($($arg)*),  | 
 | 158 | +                module_path: module_path!().to_string(),  | 
 | 159 | +                line: line!(),  | 
 | 160 | +            });  | 
 | 161 | +        }  | 
 | 162 | +    };  | 
 | 163 | +}  | 
0 commit comments