Skip to main content

osal_rs/
log.rs

1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
18 *
19 ***************************************************************************/
20
21//! Logging system for embedded environments.
22//!
23//! Provides a flexible logging system with multiple severity levels and color support.
24//! Designed for no-std environments with UART output support.
25//!
26//! # Features
27//!
28//! - Multiple log levels (DEBUG, INFO, WARNING, ERROR, FATAL)
29//! - Color-coded output support (ANSI colors)
30//! - Timestamp logging with millisecond precision
31//! - Thread-safe logging with busy-wait synchronization
32//! - Configurable log level masking
33//! - Zero-cost when logs are disabled
34//!
35//! # Examples
36//!
37//! ## Basic logging
38//!
39//! ```ignore
40//! use osal_rs::{log_info, log_error, log_debug};
41//! 
42//! log_info!("APP", "Application started");
43//! log_debug!("APP", "Counter value: {}", 42);
44//! log_error!("APP", "Failed to initialize: {}", error_msg);
45//! ```
46//!
47//! ## Configuring log levels
48//!
49//! ```ignore
50//! use osal_rs::log::*;
51//! 
52//! // Set log level to WARNING and above
53//! set_level_log(log_levels::LEVEL_WARNING);
54//! 
55//! // Enable/disable logging
56//! set_enable_log(true);
57//! 
58//! // Enable/disable color output
59//! set_enable_color(true);
60//! ```
61//!
62//! ## Using print macros
63//!
64//! ```ignore
65//! use osal_rs::{print, println};
66//! 
67//! print!("Hello");
68//! println!(" World!");
69//! println!("Value: {}", 123);
70//! ```
71//!
72//! # Thread Safety
73//!
74//! The logging system uses a simple busy-wait lock to ensure thread-safe output:
75//! - Only one thread can log at a time
76//! - Other threads spin-wait until the log is complete
77//! - No heap allocation during the lock
78//! - Suitable for task context only (see ISR Context below)
79//!
80//! # ISR Context
81//!
82//! **WARNING**: Do not use logging macros from interrupt service routines (ISRs).
83//! 
84//! Reasons:
85//! - Busy-wait lock can cause priority inversion
86//! - String formatting allocates memory
87//! - UART operations may block
88//! - Can significantly delay interrupt response
89//!
90//! If you need logging from ISR context, use a queue to defer the log to a task.
91//!
92//! # Performance Considerations
93//!
94//! - Each log call allocates a string (uses RTOS heap)
95//! - UART transmission is synchronous and relatively slow
96//! - Verbose logging (DEBUG level) can impact real-time performance
97//! - Consider using WARNING or ERROR level in production
98//! - Logs are compiled out when the level is disabled (zero cost)
99//!
100//! # Color Output
101//!
102//! When colors are enabled, log levels are color-coded:
103//! - **DEBUG**: Cyan - Detailed debugging information
104//! - **INFO**: Green - Normal operational messages
105//! - **WARNING**: Yellow - Potential issues, non-critical
106//! - **ERROR**: Red - Errors affecting functionality
107//! - **FATAL**: Magenta - Critical errors, system failure
108//!
109//! Disable colors if your terminal doesn't support ANSI escape codes.
110//!
111//! # Best Practices
112//!
113//! 1. **Use appropriate tags**: Use meaningful tags like "NET", "FS", "APP" to identify sources
114//! 2. **Choose correct levels**: DEBUG for development, INFO for milestones, ERROR for failures
115//! 3. **Avoid logging in hot paths**: Logging can significantly slow down tight loops
116//! 4. **Set production levels**: Use WARNING or ERROR level in production builds
117//! 5. **Never log from ISRs**: Defer to task context using queues or notifications
118
119#[cfg(not(feature = "std"))]
120pub mod ffi {
121    //! Foreign Function Interface (FFI) to C UART functions.
122    //!
123    //! This module provides low-level bindings to C functions for UART communication.
124    //! These functions are only available in `no_std` mode.
125    //!
126    //! # Safety
127    //!
128    //! All functions in this module are `unsafe` because they:
129    //! - Call external C code that cannot be verified by Rust
130    //! - Require valid C string pointers (null-terminated)
131    //! - May access hardware registers directly
132    //! - Do not perform bounds checking
133    //!
134    //! # Platform Requirements
135    //!
136    //! The C implementation must provide `printf_on_uart` that:
137    //! - Accepts printf-style format strings
138    //! - Outputs to UART hardware
139    //! - Is thread-safe (or only called from synchronized contexts)
140    //! - Returns number of characters written, or negative on error
141    
142    use core::ffi::{c_char, c_int};
143
144    unsafe extern "C" {
145        /// FFI function to print formatted strings to UART.
146        ///
147        /// This is the low-level C function that interfaces with the hardware UART.
148        /// Typically implemented in the platform-specific porting layer.
149        ///
150        /// # Safety
151        ///
152        /// - `format` must be a valid null-terminated C string
153        /// - Variable arguments must match the format specifiers
154        /// - Must not be called from multiple threads simultaneously (unless C implementation is thread-safe)
155        ///
156        /// # Parameters
157        ///
158        /// * `format` - Printf-style format string (null-terminated)
159        /// * `...` - Variable arguments matching format specifiers
160        ///
161        /// # Returns
162        ///
163        /// Number of characters written, or negative value on error
164        pub fn printf_on_uart(format: *const c_char, ...) -> c_int;
165
166    }
167}
168
169use core::ffi::c_char;
170
171use alloc::{ffi::CString, format};
172
173use crate::log::ffi::printf_on_uart;
174use crate::os::{System, SystemFn};
175
176
177/// ANSI escape code for red text color
178const COLOR_RED: &str = "\x1b[31m";
179/// ANSI escape code for green text color
180const COLOR_GREEN: &str = "\x1b[32m";
181/// ANSI escape code for yellow text color
182const COLOR_YELLOW: &str = "\x1b[33m";
183/// ANSI escape code for blue text color
184const COLOR_BLUE: &str = "\x1b[34m";
185/// ANSI escape code for magenta text color
186const COLOR_MAGENTA: &str = "\x1b[35m";
187/// ANSI escape code for cyan text color
188const COLOR_CYAN: &str = "\x1b[36m";
189/// ANSI escape code to reset all text attributes
190const COLOR_RESET: &str = "\x1b[0m";
191/// Carriage return + line feed for proper terminal output
192pub const RETURN: &str = "\r\n";
193
194/// Log level flags and level configurations.
195///
196/// This module defines bit flags for different log levels and combined
197/// level masks for filtering log messages.
198///
199/// # Flag vs Level
200///
201/// - **FLAGS** (`FLAG_*`): Individual bits for each log level, used internally
202/// - **LEVELS** (`LEVEL_*`): Combined masks that include all levels at or above the specified severity
203///
204/// For example, `LEVEL_WARNING` includes WARNING, ERROR, and FATAL flags.
205///
206/// # Usage
207///
208/// ```ignore
209/// use osal_rs::log::log_levels::*;
210/// use osal_rs::log::set_level_log;
211///
212/// // Set minimum level to WARNING (shows WARNING, ERROR, FATAL)
213/// set_level_log(LEVEL_WARNING);
214///
215/// // Check if specific level is enabled
216/// if is_enabled_log(FLAG_DEBUG) {
217///     // Debug is enabled
218/// }
219/// ```
220pub mod log_levels {
221    /// Flag for DEBUG level messages (bit 0, most verbose).
222    ///
223    /// Use for detailed debugging information during development.
224    pub const FLAG_DEBUG: u8 = 1 << 0;
225    
226    /// Flag for INFO level messages (bit 1).
227    ///
228    /// Use for informational messages about normal operation.
229    pub const FLAG_INFO: u8 = 1 << 1;
230    
231    /// Flag for WARNING level messages (bit 2).
232    ///
233    /// Use for potentially problematic situations that don't prevent operation.
234    pub const FLAG_WARNING: u8 = 1 << 2;
235    
236    /// Flag for ERROR level messages (bit 3).
237    ///
238    /// Use for errors that affect functionality but allow continued operation.
239    pub const FLAG_ERROR: u8 = 1 << 3;
240    
241    /// Flag for FATAL level messages (bit 4, most severe).
242    ///
243    /// Use for critical errors that prevent continued operation.
244    pub const FLAG_FATAL: u8 = 1 << 4;
245    
246    /// Flag to enable color output (bit 6).
247    ///
248    /// When set, log messages are color-coded by severity level.
249    pub const FLAG_COLOR_ON: u8 = 1 << 6;
250    
251    /// Flag to enable/disable logging entirely (bit 7).
252    ///
253    /// When clear, all logging is disabled for zero runtime cost.
254    pub const FLAG_STATE_ON: u8 = 1 << 7;
255
256    /// DEBUG level: Shows all messages (DEBUG, INFO, WARNING, ERROR, FATAL).
257    ///
258    /// Most verbose setting, suitable for development and troubleshooting.
259    pub const LEVEL_DEBUG: u8 = FLAG_DEBUG | FLAG_INFO | FLAG_WARNING | FLAG_ERROR | FLAG_FATAL;
260    
261    /// INFO level: Shows INFO and above (INFO, WARNING, ERROR, FATAL).
262    ///
263    /// Filters out DEBUG messages, suitable for normal operation.
264    pub const LEVEL_INFO: u8 = FLAG_INFO | FLAG_WARNING | FLAG_ERROR | FLAG_FATAL;
265    
266    /// WARNING level: Shows WARNING and above (WARNING, ERROR, FATAL).
267    ///
268    /// Shows only warnings and errors, suitable for production.
269    pub const LEVEL_WARNING: u8 = FLAG_WARNING | FLAG_ERROR | FLAG_FATAL;
270    
271    /// ERROR level: Shows ERROR and FATAL only.
272    ///
273    /// Shows only errors and critical failures.
274    pub const LEVEL_ERROR: u8 = FLAG_ERROR | FLAG_FATAL;
275
276    /// FATAL level: Shows only FATAL messages.
277    ///
278    /// Most restrictive setting, shows only critical failures.
279    pub const LEVEL_FATAL: u8 = FLAG_FATAL;
280}
281
282/// Global log level mask with color and state flags enabled by default.
283///
284/// This mutable static holds the current logging configuration:
285/// - Bits 0-4: Log level flags (DEBUG, INFO, WARNING, ERROR, FATAL)
286/// - Bit 6: Color enable flag
287/// - Bit 7: Logging enable/disable flag
288///
289/// # Default
290///
291/// Initialized to `LEVEL_DEBUG | FLAG_COLOR_ON | FLAG_STATE_ON`:
292/// - All log levels enabled
293/// - Color output enabled
294/// - Logging enabled
295///
296/// # Thread Safety
297///
298/// Modifications are not atomic. Use the provided setter functions
299/// (`set_level_log`, `set_enable_log`, `set_enable_color`) which perform
300/// simple bit operations that are effectively atomic on most platforms.
301/// Race conditions during initialization are unlikely to cause issues
302/// beyond temporarily incorrect filter settings.
303static mut MASK: u8 = log_levels::LEVEL_DEBUG | log_levels::FLAG_COLOR_ON | log_levels::FLAG_STATE_ON;
304
305/// Simple busy flag for thread-safe logging (0 = free, non-zero = busy).
306///
307/// Used as a spinlock to ensure only one thread logs at a time:
308/// - 0 = Lock is free, logging available
309/// - Non-zero = Lock is held, other threads must wait
310///
311/// # Synchronization
312///
313/// Uses a basic busy-wait (spinlock) pattern:
314/// 1. Wait until BUSY == 0
315/// 2. Set BUSY = 1
316/// 3. Perform logging
317/// 4. Set BUSY = 0
318///
319/// # Limitations
320///
321/// - Not a true atomic operation (no memory barriers)
322/// - Priority inversion possible (low-priority task holds lock)
323/// - Wastes CPU cycles during contention
324/// - **Never use from ISR context** - can deadlock
325///
326/// This simple approach is sufficient for most embedded use cases where
327/// logging contention is infrequent.
328static mut BUSY: u8 = 0;
329
330/// Prints formatted text to UART without a newline.
331///
332/// This macro is only available in no-std mode. In std mode, use the standard `print!` macro.
333///
334/// # Examples
335///
336/// ```ignore
337/// use osal_rs::print;
338/// 
339/// print!("Hello");
340/// print!(" World: {}", 42);
341/// ```
342#[cfg(not(feature = "std"))]
343#[macro_export]
344macro_rules! print {
345    ($($arg:tt)*) => {{
346        unsafe {
347            use alloc::string::ToString;
348            let formatted = alloc::format!($($arg)*);
349            if let Ok(c_str) = alloc::ffi::CString::new(formatted) {
350                $crate::log::ffi::printf_on_uart(b"%s\0".as_ptr() as *const core::ffi::c_char, c_str.as_ptr());
351            }
352        }
353    }};
354}
355
356/// Prints formatted text to UART with a newline (\r\n).
357///
358/// This macro is only available in no-std mode. In std mode, use the standard `println!` macro.
359///
360/// # Examples
361///
362/// ```ignore
363/// use osal_rs::println;
364/// 
365/// println!("Hello World");
366/// println!("Value: {}", 42);
367/// println!();  // Just a newline
368/// ```
369#[cfg(not(feature = "std"))]
370#[macro_export]
371macro_rules! println {
372    () => {
373        $crate::print!("\r\n")
374    };
375    ($fmt:expr) => {{
376        unsafe {
377            let formatted = alloc::format!(concat!($fmt, "\r\n"));
378            if let Ok(c_str) = alloc::ffi::CString::new(formatted) {
379                $crate::log::ffi::printf_on_uart(b"%s\0".as_ptr() as *const core::ffi::c_char, c_str.as_ptr());
380            }
381        }
382    }};
383    ($fmt:expr, $($arg:tt)*) => {{
384        unsafe {
385            let formatted = alloc::format!(concat!($fmt, "\r\n"), $($arg)*);
386            if let Ok(c_str) = alloc::ffi::CString::new(formatted) {
387                $crate::log::ffi::printf_on_uart(b"%s\0".as_ptr() as *const core::ffi::c_char, c_str.as_ptr());
388            }
389        }
390    }};
391}
392
393/// Sets the log level threshold.
394///
395/// Only log messages at or above this level will be displayed.
396///
397/// # Parameters
398///
399/// * `level` - Log level (use constants from `log_levels` module)
400///
401/// # Examples
402///
403/// ```ignore
404/// use osal_rs::log::*;
405/// 
406/// // Show only warnings and errors
407/// set_level_log(log_levels::LEVEL_WARNING);
408/// 
409/// // Show all messages
410/// set_level_log(log_levels::LEVEL_DEBUG);
411/// ```
412pub fn set_level_log(level: u8) {
413    unsafe {
414        MASK =
415            (MASK & log_levels::FLAG_STATE_ON) | (level & !log_levels::FLAG_STATE_ON);
416    }
417}
418
419/// Enables or disables all logging.
420///
421/// When disabled, all log macros become no-ops for zero runtime cost.
422///
423/// # Parameters
424///
425/// * `enabled` - `true` to enable logging, `false` to disable
426///
427/// # Examples
428///
429/// ```ignore
430/// use osal_rs::log::set_enable_log;
431/// 
432/// set_enable_log(false);  // Disable all logging
433/// // ... logs will not be printed ...
434/// set_enable_log(true);   // Re-enable logging
435/// ```
436pub fn set_enable_log(enabled: bool) {
437    unsafe {
438        if enabled {
439            MASK |= log_levels::FLAG_STATE_ON;
440        } else {
441            MASK &= !log_levels::FLAG_STATE_ON;
442        }
443    }
444}
445
446/// Checks if logging is currently enabled.
447///
448/// # Returns
449///
450/// `true` if logging is enabled, `false` otherwise
451///
452/// # Examples
453///
454/// ```ignore
455/// use osal_rs::log::get_enable_log;
456/// 
457/// if get_enable_log() {
458///     println!("Logging is active");
459/// }
460/// ```
461pub fn get_enable_log() -> bool {
462    unsafe { (MASK & log_levels::FLAG_STATE_ON) != 0 }
463}
464
465/// Checks if a specific log level is enabled.
466///
467/// # Parameters
468///
469/// * `log_type` - Log level flag to check
470///
471/// # Returns
472///
473/// `true` if the log level is enabled, `false` otherwise
474///
475/// # Examples
476///
477/// ```ignore
478/// use osal_rs::log::*;
479/// 
480/// if is_enabled_log(log_levels::FLAG_DEBUG) {
481///     // Debug logging is active
482/// }
483/// ```
484pub fn is_enabled_log(log_type: u8) -> bool {
485    unsafe { (MASK & log_levels::FLAG_STATE_ON) != 0 && (MASK & log_type) != 0 }
486}
487
488/// Gets the current log level threshold.
489///
490/// # Returns
491///
492/// Current log level mask (without state and color flags)
493///
494/// # Examples
495///
496/// ```ignore
497/// use osal_rs::log::*;
498/// 
499/// let level = get_level_log();
500/// ```
501pub fn get_level_log() -> u8 {
502    unsafe { MASK & !log_levels::FLAG_STATE_ON & !log_levels::FLAG_COLOR_ON }
503}
504
505/// Enables or disables color output.
506///
507/// When enabled, log messages are color-coded by severity:
508/// - DEBUG: Cyan
509/// - INFO: Green  
510/// - WARNING: Yellow
511/// - ERROR: Red
512/// - FATAL: Magenta
513///
514/// # Parameters
515///
516/// * `enabled` - `true` to enable colors, `false` for plain text
517///
518/// # Examples
519///
520/// ```ignore
521/// use osal_rs::log::set_enable_color;
522/// 
523/// set_enable_color(true);   // Enable colored output
524/// set_enable_color(false);  // Disable colors
525/// ```
526pub fn set_enable_color(enabled: bool) {
527    unsafe {
528        if enabled {
529            MASK |= log_levels::FLAG_COLOR_ON;
530        } else {
531            MASK &= !log_levels::FLAG_COLOR_ON;
532        }
533    }
534}
535
536
537
538/// Core logging function that outputs formatted log messages.
539///
540/// This is the low-level function called by all log macros. It handles:
541/// - Thread-safe output using a busy-wait lock
542/// - Color formatting based on log level
543/// - Timestamp prefixing with millisecond precision
544/// - Tag prefixing for message categorization
545///
546/// # Parameters
547///
548/// * `tag` - Category or module name for the log message (e.g., "APP", "NET", "FS")
549/// * `log_type` - Log level flag (DEBUG, INFO, WARNING, ERROR, FATAL)
550/// * `to_print` - The formatted message string to log
551///
552/// # Thread Safety
553///
554/// Uses a busy-wait lock (BUSY flag) to ensure only one thread logs at a time:
555/// 1. Spins until BUSY == 0
556/// 2. Sets BUSY = 1
557/// 3. Formats and outputs the message
558/// 4. Sets BUSY = 0
559///
560/// Other threads will spin-wait during this time.
561///
562/// # Output Format
563///
564/// In `no_std` mode:
565/// ```text
566/// {color}({timestamp}ms)[{tag}] {message}{color_reset}\r\n
567/// ```
568///
569/// Example:
570/// ```text
571/// \x1b[32m(1234ms)[APP] System initialized\x1b[0m\r\n
572/// ```
573///
574/// # Examples
575///
576/// ```ignore
577/// use osal_rs::log::*;
578/// 
579/// sys_log("APP", log_levels::FLAG_INFO, "Application started");
580/// sys_log("NET", log_levels::FLAG_ERROR, "Connection failed");
581/// ```
582///
583/// # Note
584///
585/// Prefer using the log macros (`log_info!`, `log_error!`, etc.) instead of
586/// calling this function directly. The macros check if the log level is enabled
587/// before formatting the message, avoiding allocation for disabled levels.
588///
589/// # Warning
590///
591/// **Never call from ISR context** - the busy-wait can cause deadlock if a
592/// higher-priority ISR preempts a task that holds the lock.
593pub fn sys_log(tag: &str, log_type: u8, to_print: &str) {
594    unsafe {
595        while BUSY != 0 {}
596        BUSY = 1;
597
598        let mut color_reset = COLOR_RESET;
599        let color = if MASK & log_levels::FLAG_COLOR_ON == log_levels::FLAG_COLOR_ON {
600
601            match log_type {
602                log_levels::FLAG_DEBUG => COLOR_CYAN,
603                log_levels::FLAG_INFO => COLOR_GREEN,
604                log_levels::FLAG_WARNING => COLOR_YELLOW,
605                log_levels::FLAG_ERROR => COLOR_RED,
606                log_levels::FLAG_FATAL => COLOR_MAGENTA,
607                _ => COLOR_RESET,
608            }
609        } else {
610            color_reset = "";
611            ""
612        };
613
614
615        let now = System::get_current_time_us();
616
617
618        #[cfg(not(feature = "std"))]
619        {
620            let formatted = format!("{color}({millis}ms)[{tag}] {to_print}{color_reset}{RETURN}", millis=now.as_millis());
621            if let Ok(c_str) = CString::new(formatted) {
622                printf_on_uart(b"%s\0".as_ptr() as *const c_char, c_str.as_ptr());
623            }
624        }
625
626        #[cfg(feature = "std")]
627        {
628            print!("{}[{}] ", color, tag);
629            core::fmt::write(&mut core::fmt::Formatter::new(), args).unwrap();
630            print!("{}", COLOR_RESET);
631            print!("\r\n");
632        }
633
634        BUSY = 0;
635    }
636}
637
638/// Logs a DEBUG level message.
639///
640/// Debug messages are the most verbose and typically used during development.
641/// Color: Cyan (if colors are enabled)
642///
643/// # Parameters
644///
645/// * `app_tag` - Category or module identifier
646/// * `fmt` - Format string
647/// * `arg` - Optional format arguments
648///
649/// # Examples
650///
651/// ```ignore
652/// use osal_rs::log_debug;
653/// 
654/// log_debug!("APP", "Initializing subsystem");
655/// log_debug!("APP", "Counter: {}, Status: {}", counter, status);
656/// ```
657#[macro_export]
658macro_rules! log_debug {
659    ($app_tag:expr, $fmt:expr $(, $($arg:tt)*)?) => {{
660        if $crate::log::is_enabled_log($crate::log::log_levels::FLAG_DEBUG) {
661            let msg = alloc::format!($fmt $(, $($arg)*)?);
662            $crate::log::sys_log($app_tag, $crate::log::log_levels::FLAG_DEBUG, &msg);
663        }
664    }};
665}
666
667/// Logs an INFO level message.
668///
669/// Informational messages about normal application operation.
670/// Color: Green (if colors are enabled)
671///
672/// # Parameters
673///
674/// * `app_tag` - Category or module identifier
675/// * `fmt` - Format string  
676/// * `arg` - Optional format arguments
677///
678/// # Examples
679///
680/// ```ignore
681/// use osal_rs::log_info;
682/// 
683/// log_info!("APP", "System initialized successfully");
684/// log_info!("NET", "Connected to server at {}", ip_addr);
685/// ```
686#[macro_export]
687macro_rules! log_info {
688    ($app_tag:expr, $fmt:expr $(, $($arg:tt)*)?) => {{
689        if $crate::log::is_enabled_log($crate::log::log_levels::FLAG_INFO) {
690            let msg = alloc::format!($fmt $(, $($arg)*)?);
691            $crate::log::sys_log($app_tag, $crate::log::log_levels::FLAG_INFO, &msg);
692        }
693    }};
694}
695
696/// Logs a WARNING level message.
697///
698/// Warning messages indicate potential issues that don't prevent operation.
699/// Color: Yellow (if colors are enabled)
700///
701/// # Parameters
702///
703/// * `app_tag` - Category or module identifier
704/// * `fmt` - Format string
705/// * `arg` - Optional format arguments
706///
707/// # Examples
708///
709/// ```ignore
710/// use osal_rs::log_warning;
711/// 
712/// log_warning!("MEM", "Memory usage above 80%");
713/// log_warning!("SENSOR", "Temperature high: {} C", temp);
714/// ```
715#[macro_export]
716macro_rules! log_warning {
717    ($app_tag:expr, $fmt:expr $(, $($arg:tt)*)?) => {{
718        if $crate::log::is_enabled_log($crate::log::log_levels::FLAG_WARNING) {
719            let msg = alloc::format!($fmt $(, $($arg)*)?);
720            $crate::log::sys_log($app_tag, $crate::log::log_levels::FLAG_WARNING, &msg);
721        }
722    }};
723}
724
725/// Logs an ERROR level message.
726///
727/// Error messages indicate failures that affect functionality.
728/// Color: Red (if colors are enabled)
729///
730/// # Parameters
731///
732/// * `app_tag` - Category or module identifier
733/// * `fmt` - Format string
734/// * `arg` - Optional format arguments
735///
736/// # Examples
737///
738/// ```ignore
739/// use osal_rs::log_error;
740/// 
741/// log_error!("FS", "Failed to open file");
742/// log_error!("NET", "Connection timeout: {}", error);
743/// ```
744#[macro_export]
745macro_rules! log_error {
746    ($app_tag:expr, $fmt:expr $(, $($arg:tt)*)?) => {{
747        if $crate::log::is_enabled_log($crate::log::log_levels::FLAG_ERROR) {
748            let msg = alloc::format!($fmt $(, $($arg)*)?);
749            $crate::log::sys_log($app_tag, $crate::log::log_levels::FLAG_ERROR, &msg);
750        }
751    }};
752}
753
754/// Logs a FATAL level message.
755///
756/// Fatal messages indicate critical errors that prevent continued operation.
757/// Color: Magenta (if colors are enabled)
758///
759/// # Parameters
760///
761/// * `app_tag` - Category or module identifier
762/// * `fmt` - Format string
763/// * `arg` - Optional format arguments
764///
765/// # Examples
766///
767/// ```ignore
768/// use osal_rs::log_fatal;
769/// 
770/// log_fatal!("SYS", "Kernel panic!");
771/// log_fatal!("HW", "Hardware fault detected: {}", fault_code);
772/// ```
773#[macro_export]
774macro_rules! log_fatal {
775    ($app_tag:expr, $fmt:expr $(, $($arg:tt)*)?) => {{
776        if $crate::log::is_enabled_log($crate::log::log_levels::FLAG_FATAL) {
777            let msg = alloc::format!($fmt $(, $($arg)*)?);
778            $crate::log::sys_log($app_tag, $crate::log::log_levels::FLAG_FATAL, &msg);
779        }
780    }};
781}