bt_logger/
lib.rs

1/// A simple and lightweight logger for Rust with various features such as logging to different output destinations (stdout, stderr), formatting log messages, and level checking.
2/// 
3/// LogLevel
4/// Represents the different levels of log severity. The values are ordered from lowest to highest severity.
5/// NONE = No logging,
6/// FATAL = Fatal error,
7/// ERROR = Error message,
8/// WARN = Warning message,
9/// INFO = Informational message,
10/// DEBUG = Debugging message,
11/// TRACE = Trace message,
12/// VERBOSE = Very verbose message
13/// 
14/// LogTarget
15/// Represents the different output destinations for log messages.
16/// STD_ERROR: Logs to stderr
17/// STD_OUT: Logs to stdout
18/// STD_BOTH: Logs to both stdout and stderr
19/// 
20/// Macros
21/// bt_logger provides several macros for logging different levels of messages. Each macro:
22/// - Checks if the log level is sufficient to log the message.
23/// - If sufficient, formats the message using get_formatted_msg.
24/// - Calls log_msg with the formatted message.
25/// 
26/// log_fatal!(function name, message, message arguments)
27/// log_error!(function name, message, message arguments)
28/// log_warning!(function name, message, message arguments)
29/// log_info!(function name, message, message arguments)
30/// log_debug!(function name, message, message arguments)
31/// log_trace!(function name, message, message arguments)
32/// log_verbose!(function name, message, message arguments)
33/// get_fatal!(function name, message, message arguments)
34/// get_error!(function name, message, message arguments)
35/// 
36/// Usage
37/// To use the bt_logger module, you would create a logger instance with the desired configuration and then use the macros to log messages at different levels. For example:
38/// build_logger("BACHUETECH", "My Application", LogLevel::INFO, LogTarget::StdOut);
39/// .......
40/// log_info!("function_name","Hello, {}", "Bachuetech User");
41///
42/// This code builds a logger instance with the tag BACHUETECH, application name My Application, and log level INFO. It then uses the log_info! macro to log a message to stdout.
43/// If the logger is not create the application will stop with the first call of a loggin macro
44/// build the logger as early as possible to avoid issue using the build_logger
45/// 
46/// The log output is:
47/// TAG APPLICATION CURRENT_UTC_TIME(YYYY-mm-ddTHH:MM:SS.3fz) level(one capital letter) source(module path::function)|>|message
48
49    use std::sync::Mutex;
50    use std::fmt;
51    use lazy_static::lazy_static;
52    use chrono::prelude::*;
53
54    #[macro_use]
55    pub mod macros;
56
57
58/// LogLevel
59/// Represents the different levels of log severity. The values are ordered from lowest to highest severity.
60/// NONE = No logging,
61/// FATAL = Fatal error,
62/// ERROR = Error message,
63/// WARN = Warning message,
64/// INFO = Informational message,
65/// DEBUG = Debugging message,
66/// TRACE = Trace message,
67/// VERBOSE = Very verbose message
68    #[repr(u8)]
69    #[derive(Clone)]
70    pub enum LogLevel{
71        NONE = 100,
72        FATAL = 60,
73        ERROR = 50,
74        WARN = 40,
75        INFO = 30,
76        DEBUG = 20,
77        TRACE = 10,
78        VERBOSE = 0,
79    }
80    
81    impl fmt::Display for LogLevel{
82        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83            match *self{
84                LogLevel::NONE    => write!(f, "N"),
85                LogLevel::FATAL   => write!(f, "F"),
86                LogLevel::ERROR   => write!(f, "E"),
87                LogLevel::WARN    => write!(f, "W"),
88                LogLevel::INFO    => write!(f, "I"),
89                LogLevel::DEBUG   => write!(f, "D"),
90                LogLevel::TRACE   => write!(f, "T"),
91                LogLevel::VERBOSE => write!(f, "V"),
92            }
93        }
94    }
95    
96/// LogTarget
97/// Represents the different output destinations for log messages.
98/// STD_ERROR: Logs to stderr
99/// STD_OUT: Logs to stdout
100/// STD_BOTH: Logs to both stdout and stderr
101    #[derive(Debug, Clone)]
102    pub enum LogTarget{
103        STD_ERROR,
104        STD_OUT,
105        STD_BOTH,
106    }
107    
108/// Logger
109/// Represents a logger instance with its configuration.
110/// log_tag: The tag used in log messages
111/// log_app: The application name used in log messages
112/// current_log_level_value: The current log level value (0-100)
113/// output_destination: The output destination for log messages (StdError, StdOut, or StdBoth)
114    #[derive(Clone)]
115    pub struct Logger{ //ToDo, check if this can be private
116        log_tag: String,
117        log_app: String,
118        current_log_level_value: u8,
119        output_destination: LogTarget,
120    }
121    
122    impl Logger{
123        ///Creates a new logger instance with the given configuration.
124        fn new(tag: &str, application: &str, level: LogLevel, output:LogTarget) -> Self{
125            Logger { log_tag: tag.to_owned(), log_app: application.to_owned(),
126                     current_log_level_value: level as u8, output_destination: output}
127        }
128    
129        ///Returns the current time as a string in the format "YYYY-MM-DDTHH:MM:SS.SSSZ".
130        fn get_current_time(&self) -> String{
131            let current_time: DateTime<Utc> = Utc::now();
132            // Format the current time as a string
133            current_time.format("%Y-%m-%dT%H:%M:%S%.3f%z").to_string()
134        }
135    
136        ///Formats a log message with the given level, source, and message.
137        fn get_formatted_msg(&self, level: LogLevel, source: &str, msg: &String) -> String{
138            format!("{} {} {} {} {}|>|{}", self.log_tag, self.log_app, self.get_current_time(), level, source, msg)
139        }
140    
141        ///Logs a message to stdout.
142        fn log_stdout(&self, message: &String ){
143            println!("{}", message);
144        }
145    
146        ///Logs a message to stderr.
147        fn log_stderr(&self, message: &String){
148            eprintln!("{}", message);
149        }
150    
151        ///Logs a message with the given level, message, and module and function name.
152        pub fn log_msg(&self, msg: &String, level: LogLevel, module: &str, function: &str){ //source: &str){
153    
154            let log_msg = self.get_formatted_msg(level, &format!("{}::{}",module, function), msg);
155    
156            match self.output_destination {
157                LogTarget::STD_ERROR => {self.log_stderr(&log_msg);},
158                LogTarget::STD_OUT => {self.log_stdout(&log_msg);},
159                LogTarget::STD_BOTH => {  self.log_stderr(&log_msg);
160                                               self.log_stdout(&log_msg); }, 
161            }
162        }
163    
164        ///Get formatted message with the given level, message, and module and function name.
165        pub fn get_msg(&self, msg: &String, level: LogLevel, module: &str, function: &str) -> String { //source: &str){
166            let log_msg = self.get_formatted_msg(level, &format!("{}::{}",module, function), msg);
167            log_msg
168        }
169
170        //Check if a particular log_level has to be logged based on the currect configuration
171        pub fn log_this(&self, log_level: LogLevel) -> bool{
172            if log_level as u8 >= self.current_log_level_value {
173                return true
174            }
175    
176            false
177        }
178    }
179    
180    lazy_static! {
181        static ref LOGGER: Mutex<Option<Logger>> = Mutex::new(None);
182    }
183    
184
185    ///Build the logger. Run this function first
186    pub fn build_logger(tag: &str, application: &str, level: LogLevel, output:LogTarget){
187        let mut _logger = LOGGER.lock().unwrap();
188        if _logger.is_none(){
189            *_logger = Some(Logger::new(tag, application, level, output));
190        }
191    }
192    
193    pub fn get_logger() -> Logger{
194        let _logger = LOGGER.lock().unwrap();
195        _logger.clone().unwrap()
196    }