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 }