Skip to main content

astrolog/logger/
mod.rs

1/*
2Astrolog, a logging framework for Rust
3Copyright (C) 2019 Alessandro Pellizzari
4
5This library is free software; you can redistribute it and/or
6modify it under the terms of the GNU Lesser General Public
7License as published by the Free Software Foundation; either
8version 2.1 of the License, or (at your option) any later version.
9
10This library is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13Lesser General Public License for more details.
14
15You should have received a copy of the GNU Lesser General Public
16License along with this library; if not, write to the Free Software
17Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18*/
19
20use serde::Serialize;
21use std::collections::HashSet;
22use std::error::Error;
23use std::fmt;
24use std::rc::Rc;
25use std::sync::{mpsc, Arc};
26use std::thread;
27
28use crate::handler::{Handler, LevelAware, Handlers};
29
30mod context;
31mod item;
32mod level;
33mod record;
34
35pub use self::context::Context;
36pub use self::item::Item;
37pub use self::level::Level;
38pub use self::record::Record;
39
40//type Handlers = Vec<Box<Handler + Sync + Send + 'static>>;
41
42pub struct Logger {
43	handlers: Arc<Handlers>,
44	context: Context,
45	levels: HashSet<Level>,
46	q: Option<mpsc::SyncSender<Record>>,
47	worker: Option<thread::JoinHandle<()>>,
48	print_errors: bool,
49}
50
51impl Logger {
52	pub fn new() -> Logger {
53		Logger {
54			handlers: Arc::new(Handlers::new()),
55			context: Context::new(),
56			levels: Level::all_set(),
57			q: None,
58			worker: None,
59			print_errors: false,
60		}
61	}
62
63	pub fn into_rc(self) -> Rc<Logger> {
64		Rc::new(self)
65	}
66
67	pub fn into_arc(self) -> Arc<Logger> {
68		Arc::new(self)
69	}
70
71	pub fn set_print_errors(&mut self, print_errors: bool) -> &mut Self {
72		self.print_errors = print_errors;
73		self
74	}
75
76	pub fn print_errors(mut self, print_errors: bool) -> Self {
77		self.set_print_errors(print_errors);
78		self
79	}
80
81	pub fn push_handler<T>(&mut self, handler: T) -> &mut Self
82	where
83		T: Handler + Sync + Send + 'static,
84	{
85		self.handlers.push(handler);
86		self
87	}
88
89	pub fn with_handler<T>(mut self, handler: T) -> Self
90	where
91		T: Handler + Sync + Send + 'static,
92	{
93		self.push_handler(handler);
94		self
95	}
96
97	pub fn set_global<V>(&mut self, k: &str, v: V) -> &mut Self
98	where
99		V: Serialize,
100	{
101		self.context.set(k, v);
102		self
103	}
104
105	pub fn with_global<V>(mut self, k: &str, v: V) -> Self
106	where
107		V: Serialize,
108	{
109		self.set_global(k, v);
110		self
111	}
112
113	pub fn make_sync(&mut self) -> &mut Self {
114		if let Some(q) = self.q.take() {
115			drop(q);
116			if let Some(worker) = self.worker.take() {
117				worker.join().expect("Unable to join logger worker");
118			}
119		}
120		self
121	}
122
123	pub fn sync(mut self) -> Self {
124		self.make_sync();
125		self
126	}
127
128	pub fn make_async(&mut self) -> &mut Self {
129		if let Some(_) = self.q {
130			return self;
131		}
132
133		let handlers = self.handlers.clone();
134		let (sender, receiver) = mpsc::sync_channel(256);
135		let print_errors = self.print_errors;
136		self.q = Some(sender);
137
138		let worker = thread::spawn(move || {
139			for record in receiver {
140				handlers.handle(&record, print_errors);
141			}
142		});
143		self.worker = Some(worker);
144
145		self
146	}
147
148	pub fn r#async(mut self) -> Self {
149		self.make_async();
150		self
151	}
152
153	pub fn with<V>(&self, k: &str, v: V) -> Item
154	where
155		V: Serialize,
156	{
157		Item::new(self).with(k, v)
158	}
159
160	pub fn with_multi<V: Into<Context>>(&self, ctx: V) -> Item {
161		Item::new(self).with_multi(ctx.into())
162	}
163
164	pub fn with_error(&self, e: &Error) -> Item {
165		Item::new(self).with_error(e)
166	}
167
168	pub fn with_new_context(&self, ctx: Context) -> Item {
169		Item::new(self).with_new_context(ctx)
170	}
171
172	pub fn log<S: ToString>(&self, level: Level, msg: S) {
173		if !self.levels.contains(&level) {
174			return;
175		}
176		let record = Record::new(level, msg.to_string());
177		self.dispatch(record);
178	}
179
180	pub fn trace<S: ToString>(&self, msg: S) {
181		self.log(Level::Trace, msg);
182	}
183	pub fn profile<S: ToString>(&self, msg: S) {
184		self.log(Level::Profile, msg);
185	}
186	pub fn debug<S: ToString>(&self, msg: S) {
187		self.log(Level::Debug, msg);
188	}
189	pub fn info<S: ToString>(&self, msg: S) {
190		self.log(Level::Info, msg);
191	}
192	pub fn notice<S: ToString>(&self, msg: S) {
193		self.log(Level::Notice, msg);
194	}
195	pub fn warning<S: ToString>(&self, msg: S) {
196		self.log(Level::Warning, msg);
197	}
198	pub fn error<S: ToString>(&self, msg: S) {
199		self.log(Level::Error, msg);
200	}
201	pub fn critical<S: ToString>(&self, msg: S) {
202		self.log(Level::Critical, msg);
203	}
204	pub fn alert<S: ToString>(&self, msg: S) {
205		self.log(Level::Alert, msg);
206	}
207	pub fn emergency<S: ToString>(&self, msg: S) {
208		self.log(Level::Emergency, msg);
209	}
210
211	fn dispatch(&self, mut record: Record) {
212		record.merge_context(&self.context);
213
214		if let Some(ref q) = self.q {
215			// We don't want to block the app if the channel is full. Ignore the error.
216			let _ = q.try_send(record);
217		} else {
218			self.handlers.handle(&record, self.print_errors);
219		}
220	}
221}
222
223impl LevelAware for Logger {
224	fn set_levels(&mut self, new_levels: &[Level]) -> &mut Self {
225		self.levels = new_levels.iter().cloned().collect();
226		self
227	}
228}
229
230impl fmt::Debug for Logger {
231	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
232		write!(
233			f,
234			"Logger {{ Handlers: {} Context: {:?} }}",
235			self.handlers.len(),
236			self.context
237		)
238	}
239}
240
241#[cfg(test)]
242mod tests {
243	use super::*;
244	use crate::handler::{null::NullHandler, vec::VecHandler, LevelAware};
245
246	#[test]
247	fn test_build_with_no_handler() {
248		let logger = Logger::new();
249
250		logger.debug("a debug message");
251		logger.log(Level::Emergency, "an emergency message");
252	}
253
254	#[test]
255	fn test_build_with_null_handler() {
256		let logger = Logger::new().with_handler(NullHandler {});
257
258		logger.debug("a debug message");
259		logger.log(Level::Emergency, "an emergency message");
260	}
261
262	#[test]
263	fn test_build_with_async_dispatcher() {
264		let logger = Logger::new().r#async().with_handler(NullHandler {});
265
266		logger.debug("a debug message");
267		logger.log(Level::Emergency, "an emergency message");
268	}
269
270	#[test]
271	fn test_context() {
272		let logger = Logger::new()
273			.with_global("string", "some string")
274			.with_global("bool", true)
275			.with_global("array", vec!["a", "b"]);
276
277		let context = &logger.context;
278		assert!(context.contains_key("string"));
279		assert_eq!("some string", context.get("string").unwrap());
280
281		assert!(context.contains_key("bool"));
282		assert_eq!(true, context.get("bool").unwrap().as_bool().unwrap());
283
284		assert!(context.contains_key("array"));
285		assert_eq!("a", context.get("array").unwrap().as_array().unwrap()[0]);
286		assert_eq!("b", context.get("array").unwrap().as_array().unwrap()[1]);
287	}
288
289	#[test]
290	fn test_levels() {
291		let handler = VecHandler::new();
292		let store = handler.get_store();
293		let logger = Logger::new().with_handler(handler);
294
295		logger.trace("Trace message");
296		logger.profile("Profile message");
297		logger.debug("Debug message");
298		logger.info("Info message");
299		logger.notice("Notice message");
300		logger.warning("Warning message");
301		logger.error("Error message");
302		logger.critical("Critical message");
303		logger.alert("Alert message");
304		logger.emergency("Emergency message");
305
306		let logs = store.take();
307
308		assert_eq!(10, logs.len());
309
310		assert_eq!("Trace message", logs[0].msg);
311		assert_eq!(Level::Trace, logs[0].level);
312
313		assert_eq!("Profile message", logs[1].msg);
314		assert_eq!(Level::Profile, logs[1].level);
315
316		assert_eq!("Debug message", logs[2].msg);
317		assert_eq!(Level::Debug, logs[2].level);
318
319		assert_eq!("Info message", logs[3].msg);
320		assert_eq!(Level::Info, logs[3].level);
321
322		assert_eq!("Notice message", logs[4].msg);
323		assert_eq!(Level::Notice, logs[4].level);
324
325		assert_eq!("Warning message", logs[5].msg);
326		assert_eq!(Level::Warning, logs[5].level);
327
328		assert_eq!("Error message", logs[6].msg);
329		assert_eq!(Level::Error, logs[6].level);
330
331		assert_eq!("Critical message", logs[7].msg);
332		assert_eq!(Level::Critical, logs[7].level);
333
334		assert_eq!("Alert message", logs[8].msg);
335		assert_eq!(Level::Alert, logs[8].level);
336
337		assert_eq!("Emergency message", logs[9].msg);
338		assert_eq!(Level::Emergency, logs[9].level);
339	}
340
341	#[test]
342	fn test_with_levels() {
343		let handler = VecHandler::new();
344		let store = handler.get_store();
345		let logger = Logger::new()
346			.with_levels(&[Level::Warning, Level::Emergency])
347			.with_handler(handler);
348
349		logger.trace("Trace message");
350		logger.profile("Profile message");
351		logger.debug("Debug message");
352		logger.info("Info message");
353		logger.notice("Notice message");
354		logger.warning("Warning message");
355		logger.error("Error message");
356		logger.critical("Critical message");
357		logger.alert("Alert message");
358		logger.emergency("Emergency message");
359
360		let logs = store.take();
361
362		assert_eq!(2, logs.len());
363
364		assert_eq!("Warning message", logs[0].msg);
365		assert_eq!(Level::Warning, logs[0].level);
366
367		assert_eq!("Emergency message", logs[1].msg);
368		assert_eq!(Level::Emergency, logs[1].level);
369	}
370
371	#[test]
372	fn test_with_handler_levels() {
373		let handler = VecHandler::new().with_levels(&[Level::Warning, Level::Emergency]);
374
375		let store = handler.get_store();
376		let logger = Logger::new().with_handler(handler);
377
378		logger.trace("Trace message");
379		logger.profile("Profile message");
380		logger.debug("Debug message");
381		logger.info("Info message");
382		logger.notice("Notice message");
383		logger.warning("Warning message");
384		logger.error("Error message");
385		logger.critical("Critical message");
386		logger.alert("Alert message");
387		logger.emergency("Emergency message");
388
389		let logs = store.take();
390
391		assert_eq!(2, logs.len());
392
393		assert_eq!("Warning message", logs[0].msg);
394		assert_eq!(Level::Warning, logs[0].level);
395
396		assert_eq!("Emergency message", logs[1].msg);
397		assert_eq!(Level::Emergency, logs[1].level);
398	}
399
400	#[test]
401	fn multi_thread_logger() {
402		use std::thread;
403
404		let handler = VecHandler::new();
405		let store = handler.get_store();
406		let logger = Logger::new().with_handler(handler).into_arc();
407		let tlogger = logger.clone();
408
409		let th = thread::spawn(move || {
410			tlogger.debug("Thread debug message");
411		});
412
413		logger.warning("Main warning message");
414
415		let _ = th.join();
416
417		let logs = store.take();
418
419		assert!(logs.iter().position(|v| v.msg.contains("Thread debug")).is_some());
420		assert!(logs.iter().position(|v| v.msg.contains("Main warning")).is_some());
421	}
422
423	#[test]
424	fn test_levels_with_item() {
425		let handler = VecHandler::new();
426		let store = handler.get_store();
427		let logger = Logger::new().with_handler(handler);
428
429		logger.with("kt", "vt").trace("Trace message");
430		logger.with("kp", "vp").profile("Profile message");
431		logger.with("kd", "vd").debug("Debug message");
432		logger.with("ki", "vi").info("Info message");
433		logger.with("kn", "vn").notice("Notice message");
434		logger.with("kw", "vw").warning("Warning message");
435		logger.with("ke", "ve").error("Error message");
436		logger.with("kc", "vc").critical("Critical message");
437		logger.with("ka", "va").alert("Alert message");
438		logger.with("km", "vm").emergency("Emergency message");
439
440		let logs = store.take();
441
442		assert_eq!(10, logs.len());
443
444		assert_eq!("Trace message", logs[0].msg);
445		assert_eq!(Level::Trace, logs[0].level);
446		assert_eq!("vt", logs[0].context.get("kt").expect("Missing expected context vt"));
447
448		assert_eq!("Profile message", logs[1].msg);
449		assert_eq!(Level::Profile, logs[1].level);
450		assert_eq!("vp", logs[1].context.get("kp").expect("Missing expected context vp"));
451
452		assert_eq!("Debug message", logs[2].msg);
453		assert_eq!(Level::Debug, logs[2].level);
454		assert_eq!("vd", logs[2].context.get("kd").expect("Missing expected context vd"));
455
456		assert_eq!("Info message", logs[3].msg);
457		assert_eq!(Level::Info, logs[3].level);
458		assert_eq!("vi", logs[3].context.get("ki").expect("Missing expected context vi"));
459
460		assert_eq!("Notice message", logs[4].msg);
461		assert_eq!(Level::Notice, logs[4].level);
462		assert_eq!("vn", logs[4].context.get("kn").expect("Missing expected context vn"));
463
464		assert_eq!("Warning message", logs[5].msg);
465		assert_eq!(Level::Warning, logs[5].level);
466		assert_eq!("vw", logs[5].context.get("kw").expect("Missing expected context vw"));
467
468		assert_eq!("Error message", logs[6].msg);
469		assert_eq!(Level::Error, logs[6].level);
470		assert_eq!("ve", logs[6].context.get("ke").expect("Missing expected context ve"));
471
472		assert_eq!("Critical message", logs[7].msg);
473		assert_eq!(Level::Critical, logs[7].level);
474		assert_eq!("vc", logs[7].context.get("kc").expect("Missing expected context vc"));
475
476		assert_eq!("Alert message", logs[8].msg);
477		assert_eq!(Level::Alert, logs[8].level);
478		assert_eq!("va", logs[8].context.get("ka").expect("Missing expected context va"));
479
480		assert_eq!("Emergency message", logs[9].msg);
481		assert_eq!(Level::Emergency, logs[9].level);
482		assert_eq!("vm", logs[9].context.get("km").expect("Missing expected context vm"));
483	}
484
485	#[test]
486	fn multi_thread_logger_with_item() {
487		use std::thread;
488
489		let handler = VecHandler::new();
490		let store = handler.get_store();
491		let logger = Logger::new().with_handler(handler).into_arc();
492		let tlogger = logger.clone();
493
494		let th = thread::spawn(move || {
495			tlogger.with("kt", "vt").debug("Thread debug message");
496		});
497
498		logger.with("km", "vm").warning("Main warning message");
499
500		let _ = th.join();
501
502		let logs = store.take();
503
504		assert!(logs
505			.iter()
506			.position(|v| v.msg.contains("Thread debug") && v.context.get("kt").expect("Missing thread context") == "vt")
507			.is_some());
508		assert!(logs
509			.iter()
510			.position(|v| v.msg.contains("Main warning") && v.context.get("km").expect("Missing main context") == "vm")
511			.is_some());
512	}
513
514	#[test]
515	fn multi_with_error() {
516		use std::io::{Error as IoError, ErrorKind as IoErrorKind};
517
518		let handler = VecHandler::new();
519		let store = handler.get_store();
520		let logger = Logger::new().with_handler(handler);
521
522		// os_err 1 = permission denied
523		let err = IoError::new(IoErrorKind::PermissionDenied, IoError::from_raw_os_error(1));
524
525		logger.with_error(&err).trace("Trace message");
526
527		let logs = store.take();
528
529		assert!(logs
530			.iter()
531			.position(|v| v.msg.contains("Trace message")
532				&& v.context.get("error").expect("Missing error in context") == "permission denied")
533			.is_some());
534	}
535}