Skip to main content

httpward_core/
module_logging.rs

1// httpward-core/src/module_logging.rs
2// Module logging utilities for HttpWard dynamic modules
3// Provides reusable logging infrastructure for dynamic modules
4
5use std::ffi::{CStr, CString};
6use std::os::raw::c_char;
7
8/// Host logging function types with different log levels
9pub type HostLogErrorFn = extern "C" fn(*const c_char);
10pub type HostLogWarnFn = extern "C" fn(*const c_char);
11pub type HostLogInfoFn = extern "C" fn(*const c_char);
12pub type HostLogDebugFn = extern "C" fn(*const c_char);
13pub type HostLogTraceFn = extern "C" fn(*const c_char);
14
15/// Module logging interface that can be used by dynamic modules
16pub trait ModuleLogger {
17    /// Log error message
18    fn error(&self, msg: &str);
19    
20    /// Log warning message  
21    fn warn(&self, msg: &str);
22    
23    /// Log info message
24    fn info(&self, msg: &str);
25    
26    /// Log debug message
27    fn debug(&self, msg: &str);
28    
29    /// Log trace message
30    fn trace(&self, msg: &str);
31    
32    /// Log general message (defaults to info level)
33    fn log(&self, msg: &str) {
34        self.info(msg);
35    }
36}
37
38/// Default module logger implementation with host callback support
39pub struct DefaultModuleLogger {
40    // Host logging callbacks - these will be set by the host
41    host_log_error: Option<HostLogErrorFn>,
42    host_log_warn: Option<HostLogWarnFn>,
43    host_log_info: Option<HostLogInfoFn>,
44    host_log_debug: Option<HostLogDebugFn>,
45    host_log_trace: Option<HostLogTraceFn>,
46    // Module name for log identification
47    module_name: String,
48}
49
50impl DefaultModuleLogger {
51    /// Create a new module logger with default name
52    pub const fn new() -> Self {
53        Self {
54            host_log_error: None,
55            host_log_warn: None,
56            host_log_info: None,
57            host_log_debug: None,
58            host_log_trace: None,
59            module_name: String::new(),
60        }
61    }
62    
63    /// Create a new module logger with custom name
64    pub fn with_name(name: &str) -> Self {
65        Self {
66            host_log_error: None,
67            host_log_warn: None,
68            host_log_info: None,
69            host_log_debug: None,
70            host_log_trace: None,
71            module_name: name.to_string(),
72        }
73    }
74    
75    /// Set module name
76    pub fn set_module_name(&mut self, name: &str) {
77        self.module_name = name.to_string();
78    }
79    
80    /// Get module name
81    pub fn module_name(&self) -> &str {
82        &self.module_name
83    }
84    
85    /// Set host logging callbacks
86    pub fn set_host_callbacks(
87        &mut self,
88        error_fn: HostLogErrorFn,
89        warn_fn: HostLogWarnFn,
90        info_fn: HostLogInfoFn,
91        debug_fn: HostLogDebugFn,
92        trace_fn: HostLogTraceFn,
93    ) {
94        self.host_log_error = Some(error_fn);
95        self.host_log_warn = Some(warn_fn);
96        self.host_log_info = Some(info_fn);
97        self.host_log_debug = Some(debug_fn);
98        self.host_log_trace = Some(trace_fn);
99    }
100    
101    /// Get static reference to global logger instance
102    pub fn global() -> *mut DefaultModuleLogger {
103        static mut GLOBAL_LOGGER: DefaultModuleLogger = DefaultModuleLogger::new();
104        unsafe { &raw mut GLOBAL_LOGGER }
105    }
106}
107
108impl Default for DefaultModuleLogger {
109    fn default() -> Self {
110        Self::new()
111    }
112}
113
114impl ModuleLogger for DefaultModuleLogger {
115    fn error(&self, msg: &str) {
116        let prefix = if self.module_name.is_empty() {
117            "[module]".to_string()
118        } else {
119            format!("[{}]", self.module_name)
120        };
121        
122        if let Some(cb) = self.host_log_error {
123            let c = CString::new(format!("{} {}", prefix, msg)).unwrap();
124            unsafe { cb(c.as_ptr()) };
125        } else {
126            // Fallback to direct tracing
127            tracing::error!("{} {}", prefix, msg);
128        }
129    }
130    
131    fn warn(&self, msg: &str) {
132        let prefix = if self.module_name.is_empty() {
133            "[module]".to_string()
134        } else {
135            format!("[{}]", self.module_name)
136        };
137        
138        if let Some(cb) = self.host_log_warn {
139            let c = CString::new(format!("{} {}", prefix, msg)).unwrap();
140            unsafe { cb(c.as_ptr()) };
141        } else {
142            tracing::warn!("{} {}", prefix, msg);
143        }
144    }
145    
146    fn info(&self, msg: &str) {
147        let prefix = if self.module_name.is_empty() {
148            "[module]".to_string()
149        } else {
150            format!("[{}]", self.module_name)
151        };
152        
153        if let Some(cb) = self.host_log_info {
154            let c = CString::new(format!("{} {}", prefix, msg)).unwrap();
155            unsafe { cb(c.as_ptr()) };
156        } else {
157            tracing::info!("{} {}", prefix, msg);
158        }
159    }
160    
161    fn debug(&self, msg: &str) {
162        let prefix = if self.module_name.is_empty() {
163            "[module]".to_string()
164        } else {
165            format!("[{}]", self.module_name)
166        };
167        
168        if let Some(cb) = self.host_log_debug {
169            let c = CString::new(format!("{} {}", prefix, msg)).unwrap();
170            unsafe { cb(c.as_ptr()) };
171        } else {
172            tracing::debug!("{} {}", prefix, msg);
173        }
174    }
175    
176    fn trace(&self, msg: &str) {
177        let prefix = if self.module_name.is_empty() {
178            "[module]".to_string()
179        } else {
180            format!("[{}]", self.module_name)
181        };
182        
183        if let Some(cb) = self.host_log_trace {
184            let c = CString::new(format!("{} {}", prefix, msg)).unwrap();
185            unsafe { cb(c.as_ptr()) };
186        } else {
187            tracing::trace!("{} {}", prefix, msg);
188        }
189    }
190}
191
192/// Host logging functions that modules can call
193pub mod host_functions {
194    use super::*;
195    
196    /// Host logging function for error level
197    #[unsafe(no_mangle)]
198    pub extern "C" fn host_log_error(ptr: *const c_char) {
199        let msg = unsafe { CStr::from_ptr(ptr) }
200            .to_string_lossy()
201            .into_owned();
202
203        tracing::error!(target: "module", "[MODULE] {}", msg);
204    }
205
206    /// Host logging function for warn level
207    #[unsafe(no_mangle)]
208    pub extern "C" fn host_log_warn(ptr: *const c_char) {
209        let msg = unsafe { CStr::from_ptr(ptr) }
210            .to_string_lossy()
211            .into_owned();
212
213        tracing::warn!(target: "module", "[MODULE] {}", msg);
214    }
215
216    /// Host logging function for info level
217    #[unsafe(no_mangle)]
218    pub extern "C" fn host_log_info(ptr: *const c_char) {
219        let msg = unsafe { CStr::from_ptr(ptr) }
220            .to_string_lossy()
221            .into_owned();
222
223        tracing::info!(target: "module", "[MODULE] {}", msg);
224    }
225
226    /// Host logging function for debug level
227    #[unsafe(no_mangle)]
228    pub extern "C" fn host_log_debug(ptr: *const c_char) {
229        let msg = unsafe { CStr::from_ptr(ptr) }
230            .to_string_lossy()
231            .into_owned();
232
233        tracing::debug!(target: "module", "[MODULE] {}", msg);
234    }
235
236    /// Host logging function for trace level
237    #[unsafe(no_mangle)]
238    pub extern "C" fn host_log_trace(ptr: *const c_char) {
239        let msg = unsafe { CStr::from_ptr(ptr) }
240            .to_string_lossy()
241            .into_owned();
242
243        tracing::trace!(target: "module", "[MODULE] {}", msg);
244    }
245}
246
247/// Module setup utilities
248pub mod module_setup {
249    use super::*;
250    
251    /// Type for module logger setter function
252    pub type SetLoggerFn = unsafe extern "C" fn(
253        HostLogErrorFn,
254        HostLogWarnFn,
255        HostLogInfoFn,
256        HostLogDebugFn,
257        HostLogTraceFn,
258    );
259    
260    /// Setup module logger with host callbacks
261    /// This should be called from module_set_logger function
262    pub unsafe fn setup_module_logger(
263        error_fn: HostLogErrorFn,
264        warn_fn: HostLogWarnFn,
265        info_fn: HostLogInfoFn,
266        debug_fn: HostLogDebugFn,
267        trace_fn: HostLogTraceFn,
268    ) {
269        let logger = DefaultModuleLogger::global();
270        unsafe {
271            (*logger).set_host_callbacks(error_fn, warn_fn, info_fn, debug_fn, trace_fn);
272        }
273    }
274    
275    /// Set module name for the global logger
276    pub fn set_module_name(name: &str) {
277        let logger = DefaultModuleLogger::global();
278        unsafe {
279            (*logger).set_module_name(name);
280        }
281    }
282    
283    /// Setup module logger with host callbacks and module name
284    /// This should be called from module_set_logger function
285    pub unsafe fn setup_module_logger_with_name(
286        module_name: &str,
287        error_fn: HostLogErrorFn,
288        warn_fn: HostLogWarnFn,
289        info_fn: HostLogInfoFn,
290        debug_fn: HostLogDebugFn,
291        trace_fn: HostLogTraceFn,
292    ) {
293        let logger = DefaultModuleLogger::global();
294        unsafe {
295            (*logger).set_module_name(module_name);
296            (*logger).set_host_callbacks(error_fn, warn_fn, info_fn, debug_fn, trace_fn);
297        }
298    }
299    
300    /// Get global module logger instance
301    pub fn get_logger() -> &'static DefaultModuleLogger {
302        unsafe { &*DefaultModuleLogger::global() }
303    }
304}
305
306/// Convenience macros for module logging
307#[macro_export]
308macro_rules! module_log_error {
309    ($($arg:tt)*) => {
310        $crate::module_logging::module_setup::get_logger().error(&format!($($arg)*))
311    };
312}
313
314#[macro_export]
315macro_rules! module_log_warn {
316    ($($arg:tt)*) => {
317        $crate::module_logging::module_setup::get_logger().warn(&format!($($arg)*))
318    };
319}
320
321#[macro_export]
322macro_rules! module_log_info {
323    ($($arg:tt)*) => {
324        $crate::module_logging::module_setup::get_logger().info(&format!($($arg)*))
325    };
326}
327
328#[macro_export]
329macro_rules! module_log_debug {
330    ($($arg:tt)*) => {
331        $crate::module_logging::module_setup::get_logger().debug(&format!($($arg)*))
332    };
333}
334
335#[macro_export]
336macro_rules! module_log_trace {
337    ($($arg:tt)*) => {
338        $crate::module_logging::module_setup::get_logger().trace(&format!($($arg)*))
339    };
340}
341
342#[macro_export]
343macro_rules! module_log {
344    ($($arg:tt)*) => {
345        $crate::module_logging::module_setup::get_logger().log(&format!($($arg)*))
346    };
347}