1use log::{self, Log, Metadata, Record, SetLoggerError, LevelFilter, Level};
2use time::{self, OffsetDateTime, format_description, error};
3use colored::{Color, Colorize };
4
5use std::collections::HashMap;
6use std::sync::Mutex;
8use std::fs::{File, OpenOptions};
9use std::io::Write;
10use std::io::ErrorKind;
11const DE_TIME_PASE_FORMAT:&str = "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \
12sign:mandatory]:[offset_minute]:[offset_second]";
13
14#[derive(Debug)]
18struct ColorOption {
19 use_color: bool,
20 level_to_color: HashMap<Level, (Color,Color)>
21}
22
23impl ColorOption {fn new(use_color: bool, level_to_color: HashMap<Level, (Color,Color)>) -> Self {
25 ColorOption {
26 use_color,
27 level_to_color
28 }
29 }
30 fn set_use_color(&mut self, use_color: bool) {
31 if use_color != self.use_color {
32 self.use_color = use_color;
33 }
34 }
35 fn get_use_color(&self) -> bool {
36 self.use_color
37 }
38 fn get_color_from_level(&self, l: Level) -> (Color,Color) {
39 match self.level_to_color.get(&l) {
40 Some(c) => *c,
41 None => panic!("get_color_from_level error !!!") }
43 }
44}
45
46#[derive(Debug)]
47struct TimeOption {
48 time_parse_formt: String,
49 use_local: bool,
52 log_time: bool
54}
55
56impl TimeOption {
57 fn new() -> Self {
58 TimeOption {
59 time_parse_formt: String::from(DE_TIME_PASE_FORMAT),
60 use_local: true,
61 log_time: true
62 }
63 }
64 fn get_time(&self) -> Result<String, error::Error>{if self.enabled() {
67 let now = if self.use_local {
68 OffsetDateTime::now_local()?
69 } else {
70 OffsetDateTime::now_utc()
71 };
72 let time_format = format_description::parse(&self.time_parse_formt)?;
73 Ok(now.format(&time_format)?)
74 } else {
75 Ok(String::new())
76 }
77 }
78 fn set_time_format(&mut self, f: &str) {
79 self.time_parse_formt = String::from(f);
80 }
81 fn enabled(&self) -> bool {
82 self.log_time
83 }
84 fn set_use_local(&mut self, is_use: bool) {
85 if is_use != self.use_local {
86 self.use_local = is_use
87 }
88 }
89 fn set_log_time(&mut self, is_open: bool) {
90 if is_open != self.log_time {
91 self.log_time = is_open;
92 }
93 }
94 fn resolve_time_error(result: Result<String, error::Error>) -> String{match result {
97 Ok(t) => t,
98 Err(error::Error::IndeterminateOffset(e)) => {
99 eprintln!("{e}");
100 String::new()
101 },
102 _ => String::new()
103 }
104 }
105}
106
107
108#[derive(Debug)]
111struct DestOption {
112 use_dest: bool,
114 dest_out: Option<String>,
116 output_stream: String,
118 }
121impl DestOption {
122 fn new() -> Self {
123 DestOption {
124 use_dest: false,
125 dest_out: None,
126 output_stream: String::new()
128 }
129 }
130 fn new_with_dest(dest: &str) -> Self {
132 DestOption {
133 use_dest: true,
134 dest_out: Some(String::from(dest)),
135 output_stream: String::new()
137 }
138 }
139 fn is_use_dest(&self) -> bool {
140 self.use_dest
141 }
142 fn set_use_dest(&mut self, is_open: bool) {
143 if self.use_dest != is_open {
144 self.use_dest = is_open;
145 }
146 }
147 fn push_output(&mut self, stream: &str) {
148 self.output_stream.push_str(stream);
149 }
150}
151#[derive(Debug)]
152pub struct ELogger{
153 time_option: TimeOption,
154 color_option: ColorOption,
155 dest: Mutex<DestOption> }
157impl Log for ELogger {
158 fn enabled(&self, _metadata: &Metadata) -> bool {
159 true
160 }
161 fn log(&self, record: &Record) {
162 let l = record.level(); let (f_color, _) = self.get_color(l);
165 let l = format!("[{l}]"); let time = self.get_time();
168 let output = format!("{}\r\n{}\r\n", time, record.args());
169
170 match self.is_use_dest() {
171 true => {
172 let output = format!("{} {}", l, output);
173 self.push_output(&output);
174 self.flush();
175 },
176 false => {
177 let l = l.color(f_color);
178 let output = format!("{} {}", l, output);
179 println!("{output}");
180 }
181 };
182
183 }
184 fn flush(&self){
185 let mut guard = self.dest.lock().unwrap();
186 match &guard.dest_out {Some(file_name) => {
188 let mut file = open_file(file_name);
189 let output = guard.output_stream.clone();
190 if let Ok(_) = file.write(output.as_bytes()) {
191 };
193 },
194 None => panic!("can't read file without filename")};
196 guard.output_stream.clear();
197 }
198}
199fn open_file(file_name: &str) -> File {
200 match OpenOptions::new().append(true).open(file_name) {
201 Ok(f) => f,
202 Err(e) => match e.kind() {
203 ErrorKind::NotFound => {
204 create_file(file_name) },
206 _ => panic!("unexpected error")
207 }
208 }
209}
210
211fn create_file(file_name: &str) -> File {
212 match OpenOptions::new().create_new(true).append(true).open(file_name) {
213 Ok(f) => f,
214 Err(e) => match e.kind() {
215 ErrorKind::AlreadyExists => panic!("file already exists"),
216 _ => panic!("unexpected error")
217 }
218 }
219}
220impl ELogger {
222 pub fn new() -> Self {
223 let default_level = LevelFilter::Info;
225 log::set_max_level(default_level);
226 let default_color_map = HashMap::from([
228 (Level::Trace, (Color::White, Color::BrightBlack)),
229 (Level::Debug, (Color::BrightWhite, Color::Black)),
230 (Level::Info, (Color::BrightBlue, Color::Black)),
231 (Level::Warn, (Color::BrightYellow, Color::Black)),
232 (Level::Error, (Color::BrightRed, Color::Black)),
233 ]);
234 ELogger{
235 time_option: TimeOption::new(),
236 color_option: ColorOption::new(true, default_color_map),
237 dest: Mutex::new(DestOption::new())
238 }
239 }
240 pub fn new_dest(dest: &str) -> Self {
242 let s = Self::new();
243 *s.dest.lock().unwrap() = DestOption::new_with_dest(dest);
244 s
245 }
246 pub fn set_max_level(self, l: Level) -> Self {
260 log::set_max_level(l.to_level_filter());
261 self
262 }
263
264 pub fn set_time_format(mut self, format: &str) -> Self {
267 self.time_option.set_time_format(format);
268 self
269 }
270 pub fn get_time(&self) -> String {
272 TimeOption::resolve_time_error(self.time_option.get_time())
273 }
274 pub fn set_use_local(mut self, is_use: bool) -> Self {
276 self.time_option.set_use_local(is_use);
277 self
278 }
279 pub fn set_log_time(mut self, is_open: bool) -> Self {
281 self.time_option.set_log_time(is_open);
282 self
283 }
284
285 pub fn enable_use_color(mut self) -> Self {
287 self.color_option.set_use_color(true);
288 self
289 }
290 pub fn disable_use_color(mut self) -> Self {
292 self.color_option.set_use_color(false);
293 self
294 }
295 pub fn is_use_color(&self) -> bool {
297 self.color_option.get_use_color()
298 }
299 pub fn get_color(&self, l: Level) -> (Color,Color) {
301 self.color_option.get_color_from_level(l)
302 }
303
304 pub fn is_use_dest(&self) -> bool {
306 self.dest.lock().unwrap().is_use_dest() }
308 pub fn push_output(&self, stream: &str) {
310 self.dest.lock().unwrap().push_output(stream) }
312 pub fn set_use_dest(self, is_open: bool) -> Self {
314 self.dest.lock().unwrap().set_use_dest(is_open);
315 self
316 }
317 #[must_use]
318 pub fn init(self) -> Result<(), SetLoggerError> {
319 log::set_boxed_logger(Box::new(self))
320 }
321}
322
323pub fn quick_init() -> Result<(), SetLoggerError> {
336 ELogger::new().init()
337}
338pub fn init_use_dest(dest: &str) -> Result<(), SetLoggerError> {
340 ELogger::new_dest(dest).init()
341}
342
343#[cfg(test)]
344mod tests {
345
346}