node_tree/structs/
logger.rs

1//===================================================================================================================================================================================//
2//
3//  /$$                                                        
4// | $$                                                        
5// | $$        /$$$$$$   /$$$$$$   /$$$$$$   /$$$$$$   /$$$$$$ 
6// | $$       /$$__  $$ /$$__  $$ /$$__  $$ /$$__  $$ /$$__  $$
7// | $$      | $$  \ $$| $$  \ $$| $$  \ $$| $$$$$$$$| $$  \__/
8// | $$      | $$  | $$| $$  | $$| $$  | $$| $$_____/| $$      
9// | $$$$$$$$|  $$$$$$/|  $$$$$$$|  $$$$$$$|  $$$$$$$| $$      
10// |________/ \______/  \____  $$ \____  $$ \_______/|__/      
11//                      /$$  \ $$ /$$  \ $$                    
12//                     |  $$$$$$/|  $$$$$$/                    
13//                      \______/  \______/
14//
15//===================================================================================================================================================================================//
16
17//?
18//? Created by LunaticWyrm467 and others.
19//? 
20//? All code is licensed under the MIT license.
21//? Feel free to reproduce, modify, and do whatever.
22//?
23
24//!
25//! The Logger manages system-wide messages and the creation of logs, both regular and crash/panic
26//! logs.
27//! 
28
29use std::time::SystemTime;
30
31use chrono::{ DateTime, Utc };
32
33
34use super::node_tree_base::NodeIdentity;
35use crate::prelude::{ RID, NodeTreeBase };
36use crate::utils::functions::draw_tree;
37
38
39/*
40 * Enum
41 *      Types
42 */
43
44
45/// Used to dictate the logger's verbosity level.
46#[derive(Debug, Clone)]
47pub enum LoggerVerbosity {
48    All,
49    NoDebug,
50    OnlyIssues,
51    OnlyPanics
52}
53
54
55/// Used to pass the system that called the log to the logger for proper formatting.
56#[derive(Debug, Clone)]
57pub enum SystemCall {
58    Named(String),
59    NodePath(String)
60}
61
62impl SystemCall {
63
64    /// Returns the calling system in a properly formatted string.
65    pub fn format(&self) -> String {
66        match self {
67            Self::Named(str)    => str.to_string(),
68            Self::NodePath(str) => format!("[{}]", str)
69        }
70    }
71
72    /// Gets the underlying system without formating.
73    pub fn to_str(&self) -> &str {
74        match self {
75            Self::Named(str)    => str,
76            Self::NodePath(str) => str
77        }
78    }
79}
80
81
82/// Used to pass messages of certain types to the logger.
83#[derive(Debug, Clone)]
84pub enum Log<'a> {
85    Debug(&'a str),
86    Info(&'a str),
87    Warn(&'a str),
88    Panic(&'a str)
89}
90
91impl <'a >Log<'a> {
92    
93    /// Used to get the name associated to the Log's level.
94    pub fn get_lv(&self) -> String {
95        match self {
96            Log::Debug(_) => "DEBUG".to_string(),
97            Log::Info(_)  => "INFO".to_string(),
98            Log::Warn(_)  => "WARN".to_string(),
99            Log::Panic(_) => "PANIC!".to_string()
100        }
101    }
102
103    /// Gets the message associated to the Log.
104    pub fn get_msg(&self) -> &'a str {
105        match self {
106            Log::Debug(str) => str,
107            Log::Info(str)  => str,
108            Log::Warn(str)  => str,
109            Log::Panic(str) => str
110        }
111    }
112
113    /// Gets the colour code associated with the log's level.
114    pub fn get_colour(&self) -> String {
115        match self {
116            Log::Debug(_) => "\u{001b}[30m".to_string(),   // Black/Dark Grey
117            Log::Info(_)  => "\u{001b}[37m".to_string(),   // White
118            Log::Warn(_)  => "\u{001b}[33m".to_string(),   // Yellow
119            Log::Panic(_) => "\u{001b}[31m".to_string()    // Red
120        }
121    }
122
123    /// Returns if this is a debug message.
124    pub fn is_debug(&self) -> bool {
125        match self {
126            Log::Debug(_) => true,
127            _             => false
128        }
129    }
130
131    /// Returns if this is a log about some sort of issue, such as a warning or panic (crash).
132    pub fn is_problematic(&self) -> bool {
133        match self {
134            Log::Warn(_) | Log::Panic(_) => true,
135            _                            => false
136        }
137    }
138
139    /// Returns if this is a panic (crash) log.
140    pub fn is_panic(&self) -> bool {
141        match self {
142            Log::Panic(_) => true,
143            _             => false
144        }
145    }
146}
147
148
149/*
150 * Logger
151 *      Struct
152 */
153
154
155#[derive(Debug, Clone)]
156pub struct Logger {
157    log:          String,
158    verbosity_lv: LoggerVerbosity,
159    crash_header: String,
160    crash_footer: String
161}
162
163impl Logger {
164    
165    /// Creates a new Logger instance.
166    pub fn new(verbosity_lv: LoggerVerbosity) -> Self {
167        let mut logger: Logger = Logger {
168            log:          String::new(),
169            verbosity_lv,
170            crash_header: "Unfortunately the program has crashed. Please contact the development team with the following crash report as well as the attachment of the log posted during the time of the crash.".to_string(),
171            crash_footer: "Goodbye World! (Program Exited)".to_string()
172        };
173        
174        logger.post_manual(SystemCall::Named("SysLogger".to_string()), Log::Debug("System logger has initialized. Hello World!"));
175        logger
176    }
177
178    /// Sets the default crash header message.
179    pub fn set_default_header_on_panic(&mut self, msg: &str) {
180        self.crash_header = msg.to_string();
181    }
182    
183    /// Sets the default crash footer message.
184    pub fn set_default_footer_on_panic(&mut self, msg: &str) {
185        self.crash_footer = msg.to_string();
186    }
187
188    /// Posts a new message to the log using the `NodeTreeBase` as a reference.
189    /// This will return whether the NodeTree should quit or not.
190    /// # Safety
191    /// This is marked unsafe because there is no way to validate that the passed in pointer to the
192    /// NodeTree is valid.
193    pub unsafe fn post(&mut self, calling: RID, log: Log, node_tree: *mut NodeTreeBase) -> bool {
194        match &self.verbosity_lv {
195            LoggerVerbosity::All        => {},
196            LoggerVerbosity::NoDebug    => if log.is_debug()        { return false; },
197            LoggerVerbosity::OnlyIssues => if !log.is_problematic() { return false; },
198            LoggerVerbosity::OnlyPanics => if !log.is_panic()       { return false; }
199        }
200        
201        let node_tree: &NodeTreeBase = &*node_tree;
202        let system:    SystemCall    = {
203            match node_tree.get_node_identity(calling) {
204                Some(NodeIdentity::NodePath)         => SystemCall::NodePath(unsafe { node_tree.get_node(calling).unwrap_unchecked() }.get_absolute_path().to_string()),
205                Some(NodeIdentity::UniqueName(name)) => SystemCall::Named(name),
206                None                                 => unimplemented!()
207            }
208        };
209
210        let colour: String = log.get_colour();
211        let panic:  bool   = log.is_panic();
212        let time:   String = self.post_manual(system, log);
213
214        if panic {
215            let node_tree_visual: String = draw_tree(node_tree, calling, 6, 6);
216            println!("
217{}{}
218
219\u{001b}[0m{}{}
220Time of Crash: {}
221Exit Code: {}
222
223{}\u{001b}[0m", colour, self.crash_header, node_tree_visual, colour, time, 1, self.crash_footer);
224            
225            self.log += &format!("
226{}
227
228{}
229Time of Crash: {}
230Exit Code: {}
231
232{}", self.crash_header, node_tree_visual, time, 1, self.crash_footer);
233        }
234        
235        panic
236    }
237
238    /// Posts a new message to the log, without printing a crash report if there is an Error.
239    /// Returns the time of the posted message
240    pub fn post_manual(&mut self, system: SystemCall, log: Log) -> String {
241        let time: String = DateTime::<Utc>::from(SystemTime::now()).format("%d/%m/%Y %T").to_string();
242        match &self.verbosity_lv {
243            LoggerVerbosity::All        => {},
244            LoggerVerbosity::NoDebug    => if log.is_debug()        { return time; },
245            LoggerVerbosity::OnlyIssues => if !log.is_problematic() { return time; },
246            LoggerVerbosity::OnlyPanics => if !log.is_panic()       { return time; }
247        }
248        
249        println!(
250            "{}<{} UTC> | {} | {} | {}\u{001b}[0m",
251            log.get_colour(),
252            time,
253            system.format(),
254            log.get_lv(),
255            log.get_msg()
256        );
257        
258        self.log += &format!(
259            "<{} UTC> | {} | {} | {}\n",
260            time,
261            system.format(),
262            log.get_lv(),
263            log.get_msg()
264        );
265
266        time
267    }
268
269    /// Gets the log as a string.
270    pub fn to_str(&self) -> &str {
271        &self.log
272    }
273}
274
275
276/*
277 * Logger
278 *      Macros
279 */
280
281
282/// A simple macro which is compatible with Rust's format syntax used in macros like `print!`,
283/// `println!`, and `format!`.
284/// Prints debug info to the logger.
285///
286/// # Note
287/// The first argument should be `self`.
288#[macro_export]
289macro_rules! debug {
290    ($self:ident, $fmt_str:literal) => {{
291        $self.post(Log::Debug(&format!($fmt_str)))
292    }};
293
294    ($self:ident, $fmt_str:literal, $($args:expr),*) => {{
295        $self.post(Log::Debug(&format!($fmt_str, $($args),*)))
296    }};
297}
298
299/// A simple macro which is compatible with Rust's format syntax used in macros like `print!`,
300/// `println!`, and `format!`.
301/// Prints info to the logger.
302///
303/// # Note
304/// The first argument should be `self`.
305#[macro_export]
306macro_rules! info {
307    ($self:ident, $fmt_str:literal) => {{
308        $self.post(Log::Info(&format!($fmt_str)))
309    }};
310
311    ($self:ident, $fmt_str:literal, $($args:expr),*) => {{
312        $self.post(Log::Info(&format!($fmt_str, $($args),*)))
313    }};
314}
315
316/// A simple macro which is compatible with Rust's format syntax used in macros like `print!`,
317/// `println!`, and `format!`.
318/// Prints a warning to the logger.
319///
320/// # Note
321/// The first argument should be `self`.
322#[macro_export]
323macro_rules! warn {
324    ($self:ident, $fmt_str:literal) => {{
325        $self.post(Log::Warn(&format!($fmt_str)))
326    }};
327
328    ($self:ident, $fmt_str:literal, $($args:expr),*) => {{
329        $self.post(Log::Warn(&format!($fmt_str, $($args),*)))
330    }};
331}
332
333/// A simple macro which is compatible with Rust's format syntax used in macros like `print!`,
334/// `println!`, and `format!`.
335/// Prints a panic to the logger and causes a crash.
336///
337/// # Note
338/// The first argument should be `self`.
339#[macro_export]
340macro_rules! error {
341    ($self:ident, $fmt_str:literal) => {{
342        $self.post(Log::Panic(&format!($fmt_str)))
343    }};
344
345    ($self:ident, $fmt_str:literal, $($args:expr),*) => {{
346        $self.post(Log::Panic(&format!($fmt_str, $($args),*)))
347    }};
348}