1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//! Basic logging functionality.
use chrono::{Utc, SecondsFormat};
use std::{io::prelude::*, fs::OpenOptions};
use crate::math::general::NumTools;
/// The structure for the RapidLogger.
pub struct Logger {
/// The buffer size. If the buffer count exceeds this value, the buffer gets written to the specified file.
pub buff_size: usize,
buff_count: usize,
buffer: String,
/// Determines wether to write to the console.
pub log_to_console: bool,
/// Determines wether to write to a file.
pub log_to_file: bool,
/// The optional file path.
pub file_path: Option<String>,
}
impl Logger {
/// Creates a new `Logger` object.
#[must_use]
pub const fn new(buff_size: usize,
log_to_console: bool,
log_to_file: bool,
file_path: Option<String>)
-> Logger {
Logger { buff_size,
buffer: String::new(),
buff_count: 0,
log_to_console,
log_to_file,
file_path }
}
/// Creates a new `Logger` object with default values.
#[must_use]
pub const fn new_default() -> Logger {
Logger { buff_size: 10,
buff_count: 0,
buffer: String::new(),
log_to_console: true,
log_to_file: false,
file_path: None }
}
/// Logs to a `Logger`.
/// # Returns
/// A `Result<(), String>`. `()` if it was successful, otherwise the error message as a `String`.
/// # Examples
/// ```
/// use lib_rapid::compsci::rapidlogging::Logger;
/// let mut l: Logger = Logger::new(3, true, true, Some("log.txt".to_string()));
/// let _ = l.log(None, "Test-Log.");
/// let _ = l.log(None, "Test-Log.");
/// let _ = l.log(None, "Test-Log.");
/// let _ = l.log(Some(vec!["Warning"]), "This is a warning.");
/// ```
/// As you can see, we initialise a new Logger `l` with the buffer size 3. This means that only after 3x logging, the logger writes to the file and to the console.
pub fn log(&mut self, prefixes: Option<Vec<&str>>, msg: &str) -> Result<(), String> {
self.buff_count.inc();
let mut out: String = format!("[{}]", Utc::now()
.to_rfc3339_opts(SecondsFormat::Secs,
true));
if let Some(v) = prefixes {
for s in v
{ out.push_str(&format!("[{}]", s)); }
}
out.push(' ');
out.push_str(msg);
out.push('\n');
self.buffer.push_str(&out);
if self.buff_count == self.buff_size
{ return self.backend_log(); }
Ok(())
}
/// Resets `buffer` and `buff_counter`.
pub fn reset_buffs(&mut self) {
self.buff_count = 0;
self.buffer = String::new();
}
/// For cleaner code, the main functionality is hidden from the user in this function.
fn backend_log(&mut self) -> Result<(), String> {
if self.log_to_file {
let file = OpenOptions::new()
.create(true)
.append(true)
.open(&self.file_path.as_ref().unwrap());
let mut file = match file {
Ok(f) => f,
Err(e) => { return Err(format!("Problem opening or creating file: {:?}", e)); }
};
match write!(file, "{}", self.buffer) {
Ok(_) => { }
Err(e) => { return Err(format!("Problem writing to file: {:?}", e)); }
}
}
if self.log_to_console {
print!("{}", self.buffer);
std::io::stdout().flush().unwrap();
}
self.reset_buffs();
Ok(())
}
}