logging_rs/lib.rs
1#![doc = include_str!("../.github/README.md")]
2// Logging-rs
3// Version: 1.1.0
4
5// Copyright (c) 2023-present I Language Development.
6
7// Permission is hereby granted, free of charge, to any person obtaining a
8// copy of this software and associated documentation files (the 'Software'),
9// to deal in the Software without restriction, including without limitation
10// the rights to use, copy, modify, merge, publish, distribute, sublicense,
11// and/or sell copies of the Software, and to permit persons to whom the
12// Software is furnished to do so, subject to the following conditions:
13
14// The above copyright notice and this permission notice shall be included in
15// all copies or substantial portions of the Software.
16
17// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS
18// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23// DEALINGS IN THE SOFTWARE.
24
25/////////////
26// EXPORTS //
27/////////////
28
29pub mod errors;
30
31
32/////////////
33// IMPORTS //
34/////////////
35
36use std;
37use std::io::Write;
38
39use chrono;
40
41
42////////////////
43// LOG LEVELS //
44////////////////
45
46/// Log levels
47#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
48pub enum Level {
49 /// Debug log level. The default value
50 #[default]
51 DEBUG,
52 /// Info log level
53 INFO,
54 /// Warn log level
55 WARN,
56 /// Error log level
57 ERROR,
58 /// Fatal log level
59 FATAL,
60 /// Message log level
61 MESSAGE
62}
63
64
65/////////////////
66// OUTPUT TYPE //
67/////////////////
68
69/// Output types
70#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
71pub enum Output {
72 /// Stdout. The default value
73 #[default]
74 STDOUT,
75 /// Stderr
76 STDERR,
77 /// File
78 FILE {
79 /// File path
80 path: String
81 }
82}
83
84
85///////////////
86// FORMATTER //
87///////////////
88
89/// Logging formatter object.
90///
91/// Use [`Formatter::new()`] to create formatter objects instead of using this struct.
92///
93/// # Parameters
94///
95/// - `color_format_string`: Format string supporting special ASCII control characters
96/// - `format_string`: Format string *NOT* supporting special ASCII control characters
97/// - `timestamp_format`: Timestamp format string in strftime format
98///
99/// # Returns
100///
101/// A new `Formatter` object with the specified format strings.
102///
103/// # Examples
104///
105/// ```rust
106/// # use logging_rs;
107/// logging_rs::Formatter {
108/// color_format_string: "format string with color support".to_owned(),
109/// format_string: "format string".to_owned(),
110/// timestamp_format: "timestamp format".to_owned()
111/// };
112/// ```
113#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
114pub struct Formatter {
115 /// Format string supporting special ASCII control characters
116 pub color_format_string: String,
117 /// Format string *NOT* supporting special ASCII control characters
118 pub format_string: String,
119 /// Timestamp format string in strftime format
120 pub timestamp_format: String,
121}
122
123impl Default for Formatter {
124 fn default() -> Formatter {
125 return Formatter::new("[{{color.bright_blue}}{{timestamp}}{{end}}] [{{level}}] {{path}}: {{message}}", "[{{timestamp}}] [{{level}}] {{path}}: {{message}}", "%Y-%m-%d %H:%M:%S");
126 }
127}
128
129impl Formatter {
130 /// Creates a new formatter object.
131 ///
132 /// # Parameters
133 ///
134 /// - `color_format_string`: Format string supporting special ASCII control characters
135 /// - `format_string`: Format string *NOT* supporting special ASCII control characters
136 /// - `timestamp_format`: Timestamp format string in strftime format
137 ///
138 /// # Returns
139 ///
140 /// A new `Formatter` object with the specified format strings.
141 ///
142 /// # Examples
143 ///
144 /// ```rust
145 /// # use logging_rs;
146 /// logging_rs::Formatter::new(
147 /// "[{{color.bright_blue}}{{timestamp}}{{end}}] [{{level}}] {{path}}: {{message}}",
148 /// "[{{timestamp}}] [{{level}}] {{path}}: {{message}}",
149 /// "%Y-%m-%d %H:%M:%S"
150 /// );
151 /// ```
152 ///
153 /// # See also
154 ///
155 /// - [`Formatter`]
156 pub fn new(color_format_string: &str, format_string: &str, timestamp_format: &str) -> Formatter {
157 Formatter {
158 color_format_string: color_format_string.to_owned(),
159 format_string: format_string.to_owned(),
160 timestamp_format: timestamp_format.to_owned()
161 }
162 }
163
164 /// Formats the given message.
165 ///
166 /// # Parameters
167 ///
168 /// - `self`: The formatter object
169 /// - `output`: The [`Output`] to write to
170 /// - `level`: The log [`Level`] to use for formatting
171 /// - `message`: The message to log
172 /// - `arguments`: A vector of additional formatting arguments
173 ///
174 /// # Returns
175 ///
176 /// A `String` containing the formatted message.
177 ///
178 /// # Examples
179 ///
180 /// ```rust
181 /// # use logging_rs;
182 /// # let formatter: logging_rs::Formatter = logging_rs::Formatter::default();
183 /// formatter.format(
184 /// logging_rs::Output::default(),
185 /// logging_rs::Level::default(),
186 /// "Some message with an {{argument}}",
187 /// vec![("argument", "replaced value".to_string())]
188 /// );
189 /// ```
190 ///
191 /// # See also
192 ///
193 /// - [`Formatter`]
194 /// - [`Output`]
195 /// - [`Level`]
196 #[doc = include_str!("../.github/formatting_codes.md")]
197 pub fn format<'a>(&self, output: Output, level: Level, message: &'a str, mut extra_arguments: Vec<(&str, String)>) -> String {
198 let mut arguments: Vec<(&str, String)> = vec![];
199 let mut colors: Vec<(&str, String)> = vec![
200 // Formatting codes
201 ("end", "\x1b[0m".to_string()),
202 ("bold", "\x1b[1m".to_string()),
203 ("italic", "\x1b[3m".to_string()),
204 ("underline", "\x1b[4m".to_string()),
205 ("overline", "\x1b[53m".to_string()),
206
207 // Foreground colors
208 ("color.black", "\x1b[30m".to_string()),
209 ("color.red", "\x1b[31m".to_string()),
210 ("color.green", "\x1b[32m".to_string()),
211 ("color.yellow", "\x1b[33m".to_string()),
212 ("color.blue", "\x1b[34m".to_string()),
213 ("color.magenta", "\x1b[35m".to_string()),
214 ("color.cyan", "\x1b[36m".to_string()),
215 ("color.white", "\x1b[37m".to_string()),
216
217 // Bright foreground colors
218 ("color.bright_black", "\x1b[90m".to_string()),
219 ("color.bright_red", "\x1b[91m".to_string()),
220 ("color.bright_green", "\x1b[92m".to_string()),
221 ("color.bright_yellow", "\x1b[93m".to_string()),
222 ("color.bright_blue", "\x1b[94m".to_string()),
223 ("color.bright_magenta", "\x1b[95m".to_string()),
224 ("color.bright_cyan", "\x1b[96m".to_string()),
225 ("color.bright_white", "\x1b[97m".to_string()),
226
227 // Background colors
228 ("back.black", "\x1b[40m".to_string()),
229 ("back.red", "\x1b[41m".to_string()),
230 ("back.green", "\x1b[42m".to_string()),
231 ("back.yellow", "\x1b[43m".to_string()),
232 ("back.blue", "\x1b[44m".to_string()),
233 ("back.magenta", "\x1b[45m".to_string()),
234 ("back.cyan", "\x1b[46m".to_string()),
235 ("back.white", "\x1b[47m".to_string()),
236
237 // Bright background colors
238 ("back.bright_black", "\x1b[100m".to_string()),
239 ("back.bright_red", "\x1b[101m".to_string()),
240 ("back.bright_green", "\x1b[102m".to_string()),
241 ("back.bright_yellow", "\x1b[103m".to_string()),
242 ("back.bright_blue", "\x1b[104m".to_string()),
243 ("back.bright_magenta", "\x1b[105m".to_string()),
244 ("back.bright_cyan", "\x1b[106m".to_string()),
245 ("back.bright_white", "\x1b[107m".to_string()),
246 ];
247
248 let level_string: (&str, String) = ("level", match level {
249 Level::DEBUG => "DEBUG",
250 Level::INFO => "INFO",
251 Level::WARN => "WARNING",
252 Level::ERROR => "ERROR",
253 Level::FATAL => "FATAL",
254 Level::MESSAGE => "MESSAGE"
255 }.to_string());
256 let colored_level_string: (&str, String) = ("level", match level {
257 Level::DEBUG => "DEBUG",
258 Level::INFO => "{{color.blue}}INFO{{end}}",
259 Level::WARN => "{{color.yellow}}WARNING{{end}}",
260 Level::ERROR => "{{color.red}}ERROR{{end}}",
261 Level::FATAL => "{{color.red}}FATAL{{end}}",
262 Level::MESSAGE => "{{color.blue}}MESSAGE{{end}}"
263 }.to_string());
264
265 arguments.push(("message", message.to_string()));
266 arguments.push(("timestamp", chrono::Utc::now().format(&self.timestamp_format).to_string()));
267 arguments.append(&mut extra_arguments);
268
269 let mut result: String = match output {
270 Output::STDOUT | Output::STDERR => {
271 arguments.push(colored_level_string);
272 self.color_format_string.to_owned()
273 },
274 _ => {
275 arguments.push(level_string);
276 self.format_string.to_owned()
277 }
278 };
279
280 arguments.append(&mut colors);
281
282 for (key, value) in arguments {
283 result = result.replace(("{{".to_owned() + key + "}}").as_str(), &value);
284 }
285
286 return result.clone();
287 }
288}
289
290
291///////////////////
292// LOGGER STRUCT //
293///////////////////
294
295/// Logger object.
296///
297/// Use [`Logger::new()`] to create logger objects instead of using this struct.
298///
299/// # Parameters
300///
301/// - `formatter`: The [`Formatter`] to use for formatting messages
302/// - `writable_list`: A vector of [`Output`]s to write to
303///
304/// # Returns
305///
306/// A new `Logger` object with the specified formatter and writables.
307///
308/// # Examples
309///
310/// ```rust
311/// # use logging_rs;
312/// logging_rs::Logger {
313/// formatter: logging_rs::Formatter::default(),
314/// writable_list: vec![logging_rs::Output::default()]
315/// };
316/// ```
317#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
318pub struct Logger {
319 pub formatter: Formatter,
320 pub writable_list: Vec<Output>
321}
322
323impl Default for Logger {
324 fn default() -> Logger {
325 return Logger::new(Formatter::default(), vec![Output::STDOUT]);
326 }
327}
328
329impl Logger {
330 /// Creates a new logger object.
331 ///
332 /// # Parameters
333 ///
334 /// - `formatter`: The [`Formatter`] to use for formatting messages
335 /// - `writable_list`: A vector of [`Output`]s to write to
336 ///
337 /// # Returns
338 ///
339 /// A new `Logger` object with the specified formatter and writables.
340 ///
341 /// # Examples
342 ///
343 /// ```rust
344 /// # use logging_rs;
345 /// logging_rs::Logger::new(logging_rs::Formatter::default(), vec![logging_rs::Output::default()]);
346 /// ```
347 ///
348 /// # See also
349 ///
350 /// - [`Logger`]
351 pub fn new(formatter: Formatter, writable_list: Vec<Output>) -> Logger {
352 Logger {
353 formatter: formatter,
354 writable_list: writable_list
355 }
356 }
357
358 /// Logs the given message.
359 ///
360 /// # Parameters
361 ///
362 /// - `self`: The logger object
363 /// - `message`: The message to log
364 /// - `level`: The log [`Level`] to use for logging
365 /// - `path`: The path of the calling file
366 /// - `arguments`: A list of arguments to use when formatting the message
367 ///
368 /// # Returns
369 ///
370 /// A `String` containing the formatted message.
371 ///
372 /// # Examples
373 ///
374 /// ```rust
375 /// # use logging_rs;
376 /// # let logger: logging_rs::Logger = logging_rs::Logger::default();
377 /// logger.log(
378 /// "Some message",
379 /// logging_rs::Level::default(),
380 /// "src/lib.rs",
381 /// vec![]
382 /// );
383 /// ```
384 ///
385 /// # See also
386 ///
387 /// - [`debug!()`]
388 /// - [`info!()`]
389 /// - [`warn!()`]
390 /// - [`error!()`]
391 /// - [`fatal!()`]
392 /// - [`log!()`]
393 /// - [`Logger`]
394 /// - [`Level`]
395 pub fn log(&self, message: &str, level: Level, path: &str, mut arguments: Vec<(&str, String)>) {
396 arguments.push(("path", path.to_string()));
397 for writable in self.writable_list.clone() {
398 let formatted: String = self.formatter.format(writable.clone(), level, message, arguments.clone());
399
400 match writable {
401 Output::STDOUT => println!("{}", formatted),
402 Output::STDERR => eprintln!("{}", formatted),
403 Output::FILE { ref path } => {
404 let file: Result<std::fs::File, std::io::Error> = std::fs::OpenOptions::new().create(true).append(true).write(true).open(path);
405 let write: Result<_, std::io::Error> = write!(file.as_ref().unwrap(), "{}", formatted);
406
407 if let Err(error) = file {
408 errors::Error::new("File error", "The file could not be opened", 1).raise(format!("Path: {}\nError: {}", path, error).as_str());
409 }
410
411 if let Err(error) = write {
412 errors::Error::new("Writing error", "The file could not be edited", 2).raise(format!("File: {}\nText: {}\nError: {}", path, formatted, error).as_str());
413 }
414 }
415 }
416 }
417 }
418}
419
420
421////////////
422// MACROS //
423////////////
424
425/// Logs the given message with logging level [`Level::DEBUG`].
426///
427/// # Parameters
428///
429/// - `logger`: The logger object to log with
430/// - `message`: The message to log
431///
432/// # Examples
433///
434/// ```rust
435/// # use logging_rs;
436/// # let logger: logging_rs::Logger = logging_rs::Logger::default();
437/// logging_rs::debug!(logger, "A message");
438/// logging_rs::debug!(logger, "A message with more {{details}}", "details" = "stuff");
439/// ```
440///
441/// # See also
442///
443/// - [`info!()`]
444/// - [`warn!()`]
445/// - [`error!()`]
446/// - [`fatal!()`]
447/// - [`log!()`]
448/// - [`Logger`]
449#[macro_export]
450macro_rules! debug {
451 ($logger:expr, $message:expr) => {
452 {
453 $logger.log($message, $crate::Level::DEBUG, std::panic::Location::caller().file(), vec![]);
454 }
455 };
456
457 ($logger:expr, $message:expr, $($argument_name:literal = $argument_value:literal),* $(,)?) => {
458 {
459 let mut arguments: Vec<(&str, String)> = vec![];
460
461 $(
462 arguments.push(($argument_name, $argument_value.to_string()));
463 )*
464
465 $logger.log($message, $crate::Level::DEBUG, std::panic::Location::caller().file(), arguments);
466 }
467 };
468}
469
470/// Logs the given message with logging level [`Level::INFO`].
471///
472/// # Parameters
473///
474/// - `logger`: The logger object to log with
475/// - `message`: The message to log
476///
477/// # Examples
478///
479/// ```rust
480/// # use logging_rs;
481/// # let logger: logging_rs::Logger = logging_rs::Logger::default();
482/// logging_rs::info!(logger, "A message");
483/// logging_rs::info!(logger, "A message with more {{details}}", "details" = "stuff");
484/// ```
485///
486/// # See also
487///
488/// - [`debug!()`]
489/// - [`warn!()`]
490/// - [`error!()`]
491/// - [`fatal!()`]
492/// - [`log!()`]
493/// - [`Logger`]
494#[macro_export]
495macro_rules! info {
496 ($logger:expr, $message:expr) => {
497 {
498 $logger.log($message, $crate::Level::INFO, std::panic::Location::caller().file(), vec![]);
499 }
500 };
501
502 ($logger:expr, $message:expr, $($argument_name:literal = $argument_value:literal),* $(,)?) => {
503 {
504 let mut arguments: Vec<(&str, String)> = vec![];
505
506 $(
507 arguments.push(($argument_name, $argument_value.to_string()));
508 )*
509
510 $logger.log($message, $crate::Level::INFO, std::panic::Location::caller().file(), arguments);
511 }
512 }
513}
514
515/// Logs the given message with logging level [`Level::WARN`].
516///
517/// # Parameters
518///
519/// - `logger`: The logger object to log with
520/// - `message`: The message to log
521///
522/// # Examples
523///
524/// ```rust
525/// # use logging_rs;
526/// # let logger: logging_rs::Logger = logging_rs::Logger::default();
527/// logging_rs::warn!(logger, "A message");
528/// logging_rs::warn!(logger, "A message with more {{details}}", "details" = "stuff");
529/// ```
530///
531/// # See also
532///
533/// - [`debug!()`]
534/// - [`info!()`]
535/// - [`error!()`]
536/// - [`fatal!()`]
537/// - [`log!()`]
538/// - [`Logger`]
539#[macro_export]
540macro_rules! warn {
541 ($logger:expr, $message:expr) => {
542 {
543 $logger.log($message, $crate::Level::WARN, std::panic::Location::caller().file(), vec![]);
544 }
545 };
546
547 ($logger:expr, $message:expr, $($argument_name:literal = $argument_value:literal),* $(,)?) => {
548 {
549 let mut arguments: Vec<(&str, String)> = vec![];
550
551 $(
552 arguments.push(($argument_name, $argument_value.to_string()));
553 )*
554
555 $logger.log($message, $crate::Level::WARN, std::panic::Location::caller().file(), arguments);
556 }
557 }
558}
559
560/// Logs the given message with logging level [`Level::ERROR`].
561///
562/// # Parameters
563///
564/// - `logger`: The logger object to log with
565/// - `message`: The message to log
566///
567/// # Examples
568///
569/// ```rust
570/// # use logging_rs;
571/// # let logger: logging_rs::Logger = logging_rs::Logger::default();
572/// logging_rs::error!(logger, "A message");
573/// logging_rs::error!(logger, "A message with more {{details}}", "details" = "stuff");
574/// ```
575///
576/// # See also
577///
578/// - [`debug!()`]
579/// - [`info!()`]
580/// - [`warn!()`]
581/// - [`fatal!()`]
582/// - [`log!()`]
583/// - [`Logger`]
584#[macro_export]
585macro_rules! error {
586 ($logger:expr, $message:expr) => {
587 {
588 $logger.log($message, $crate::Level::ERROR, std::panic::Location::caller().file(), vec![]);
589 }
590 };
591
592 ($logger:expr, $message:expr, $($argument_name:literal = $argument_value:literal),* $(,)?) => {
593 {
594 let mut arguments: Vec<(&str, String)> = vec![];
595
596 $(
597 arguments.push(($argument_name, $argument_value.to_string()));
598 )*
599
600 $logger.log($message, $crate::Level::ERROR, std::panic::Location::caller().file(), arguments);
601 }
602 }
603}
604
605/// Logs the given message with logging level [`Level::FATAL`].
606///
607/// # Parameters
608///
609/// - `logger`: The logger object to log with
610/// - `message`: The message to log
611///
612/// # Examples
613///
614/// ```rust
615/// # use logging_rs;
616/// # let logger: logging_rs::Logger = logging_rs::Logger::default();
617/// logging_rs::fatal!(logger, "A message");
618/// logging_rs::fatal!(logger, "A message with more {{details}}", "details" = "stuff");
619/// ```
620///
621/// # See also
622///
623/// - [`debug!()`]
624/// - [`info!()`]
625/// - [`warn!()`]
626/// - [`error!()`]
627/// - [`log!()`]
628/// - [`Logger`]
629#[macro_export]
630macro_rules! fatal {
631 ($logger:expr, $message:expr) => {
632 {
633 $logger.log($message, $crate::Level::FATAL, std::panic::Location::caller().file(), vec![]);
634 }
635 };
636
637 ($logger:expr, $message:expr, $($argument_name:literal = $argument_value:literal),* $(,)?) => {
638 {
639 let mut arguments: Vec<(&str, String)> = vec![];
640
641 $(
642 arguments.push(($argument_name, $argument_value.to_string()));
643 )*
644
645 $logger.log($message, $crate::Level::FATAL, std::panic::Location::caller().file(), arguments);
646 }
647 }
648}
649
650/// Logs the given message with logging level [`Level::MESSAGE`].
651///
652/// # Parameters
653///
654/// - `logger`: The logger object to log with
655/// - `message`: The message to log
656///
657/// # Examples
658///
659/// ```rust
660/// # use logging_rs;
661/// # let logger: logging_rs::Logger = logging_rs::Logger::default();
662/// logging_rs::log!(logger, "A message");
663/// logging_rs::log!(logger, "A message with more {{details}}", "details" = "stuff");
664/// ```
665///
666/// # See also
667///
668/// - [`debug!()`]
669/// - [`info!()`]
670/// - [`warn!()`]
671/// - [`error!()`]
672/// - [`fatal!()`]
673/// - [`Logger`]
674#[macro_export]
675macro_rules! log {
676 ($logger:expr, $message:expr) => {
677 {
678 $logger.log($message, $crate::Level::MESSAGE, std::panic::Location::caller().file(), vec![]);
679 }
680 };
681
682 ($logger:expr, $message:expr, $($argument_name:literal = $argument_value:literal),* $(,)?) => {
683 {
684 let mut arguments: Vec<(&str, String)> = vec![];
685
686 $(
687 arguments.push(($argument_name, $argument_value.to_string()));
688 )*
689
690 $logger.log($message, $crate::Level::MESSAGE, std::panic::Location::caller().file(), arguments);
691 }
692 }
693}