edwardium_logger/
lib.rs

1//! Simple logger implementation that works using targets.
2//!
3//! Instead of needing multiple logging crates to log to multiple targets
4//! this crate provides a layer of abstraction and a few default targets.
5//! A target has to implement the [`Target`](target/trait.Target.html) trait.
6//!
7//! To start logging, create the [`Logger`](struct.Logger.html) object either statically or dynamically
8//! and then call one of its `init_` methods.
9//!
10//! For example, for a dynamic logger (requires std feature):
11//!
12//! ```
13//! use edwardium_logger::targets::stderr::StderrTarget;
14//! let logger = edwardium_logger::Logger::new(
15//! 	StderrTarget::new(log::Level::Trace, Default::default()),
16//! 	std::time::Instant::now()
17//! );
18//! logger.init_boxed().expect("Could not initialize logger");
19//! ```
20//!
21//! Logger can also be created and set statically, though this has a few caveats (read the documentation of [`Logger::new`](struct.Logger.html#method.new) for more):
22//!
23//! ```
24//! use edwardium_logger::{
25//! 	targets::{stderr::StderrTarget, util::ignore_list::IgnoreList},
26//! 	timing::DummyTiming
27//! };
28//! static LOGGER: edwardium_logger::Logger<(StderrTarget), DummyTiming> =
29//! 	edwardium_logger::Logger {
30//! 		targets: StderrTarget::new(log::Level::Trace, IgnoreList::EMPTY_PATTERNS),
31//! 		start: DummyTiming
32//! 	};
33//! LOGGER.init_static();
34//! ```
35
36// TODO: Not really tested
37// This crate also has a `no_std` (disable default features) version and
38// UART logging target using [`embedded-serial`](https://docs.rs/embedded-serial) is provided
39// under the `uart_target` feature.
40
41#![cfg_attr(not(feature = "std"), no_std)]
42
43#[cfg(not(feature = "std"))]
44use core as std;
45
46use log::{Log, Metadata, Record, SetLoggerError};
47
48pub mod target;
49pub mod timing;
50
51pub mod targets;
52
53use target::TargetResults;
54
55/// Logger
56///
57/// TODO: The fields of this struct are public only because generics on const fns are unstable.
58pub struct Logger<Targ, Time>
59where
60	Targ: target::Targets + Send + Sync + 'static,
61	Time: timing::Timing + Send + Sync + 'static
62{
63	pub targets: Targ,
64	pub start: Time
65}
66impl<Targ, Time> Logger<Targ, Time>
67where
68	Targ: target::Targets + Send + Sync + 'static,
69	Time: timing::Timing + Send + Sync + 'static
70{
71	/// Creates a new Logger.
72	///
73	/// TODO: This function should be `const` but that is unstable with generic parameters, thus the fields of the logger are made public instead.
74	pub fn new(targets: Targ, start: Time) -> Self {
75		Logger { targets, start }
76	}
77
78	/// Returns a reference to the `start` field.
79	pub fn start(&self) -> &Time {
80		&self.start
81	}
82
83	/// Returns a mutable reference to the `start` field.
84	///
85	/// This can be used to have a `static mut` logger and change the `start` at the beginning of the program.
86	pub fn start_mut(&mut self) -> &mut Time {
87		&mut self.start
88	}
89
90	#[cfg(feature = "std")]
91	pub fn init_boxed(self) -> Result<(), SetLoggerError> {
92		let max_level = self.targets.max_level();
93		log::set_max_level(max_level);
94
95		let logger = Box::new(self);
96		log::set_boxed_logger(logger)?;
97		Ok(())
98	}
99
100	// TODO: On thumbv6 this won't compile
101	pub fn init_static(&'static self) -> Result<(), SetLoggerError> {
102		let max_level = self.targets.max_level();
103		log::set_max_level(max_level);
104
105		log::set_logger(self)?;
106		Ok(())
107	}
108
109	pub unsafe fn init_static_racy(&'static self) -> Result<(), SetLoggerError> {
110		let max_level = self.targets.max_level();
111		log::set_max_level(max_level);
112
113		log::set_logger_racy(self)?;
114		Ok(())
115	}
116
117	#[cfg(feature = "std")]
118	fn on_error(&self, error: &dyn std::fmt::Display) {
119		eprintln!("{}", error);
120	}
121
122	#[cfg(not(feature = "std"))]
123	fn on_error(&self, error: &dyn core::fmt::Display) {
124		// Nothing to do?
125	}
126}
127impl<Targ, Time> Log for Logger<Targ, Time>
128where
129	Targ: target::Targets + Send + Sync + 'static,
130	Time: timing::Timing + Send + Sync + 'static
131{
132	fn enabled(&self, metadata: &Metadata) -> bool {
133		self.targets.max_level() >= metadata.level()
134	}
135
136	fn log(&self, record: &Record) {
137		let now = Time::now();
138		let duration_since_start = now.duration_since(&self.start);
139
140		let results = self.targets.write(duration_since_start, record);
141		results.log_errors(|err| self.on_error(err));
142	}
143
144	fn flush(&self) {
145		let results = self.targets.flush();
146		results.log_errors(|err| self.on_error(err));
147	}
148}