1#[doc(hidden)]
2pub extern crate chrono;
3#[doc(hidden)]
4pub extern crate crossbeam_channel;
5#[cfg(any(feature = "color"))]
6extern crate yansi;
7#[allow(unused_imports)]
9#[macro_use]
10pub extern crate log;
11
12#[macro_use]
13#[doc(hidden)]
14pub mod macros;
15mod consumer;
16mod error;
17mod filter;
18mod formater;
19
20pub use log::{debug, error, info, log, log_enabled, trace, warn};
22
23pub use consumer::{BaseConsumer, Consumer, Outputer};
24pub use error::Error;
25pub use filter::{BaseFilter, Filter};
26#[cfg(any(feature = "color"))]
27pub use formater::color::{ColoredFg, ColoredFgWith, ColoredFixedLevel, ColoredLogConfig};
28pub use formater::{current_thread_name, BaseFormater, FixedLevel, Formater};
29
30use crossbeam_channel as channel;
31
32use log::{set_logger, set_max_level, Level, Log, Metadata, Record, SetLoggerError};
33use std::sync::{
34 atomic::{AtomicBool, Ordering},
35 Arc,
36};
37use std::{fmt, mem, thread};
38
39static mut LOGGER: Option<NonblockLoggerGlobal> = None;
40
41const NAME: &str = "log";
42
43pub struct NonblockLogger {
44 name: Option<String>,
45 filter: Box<dyn Filter>,
46 formater: Box<dyn Formater>,
47 consumer: Option<Box<dyn Consumer>>,
48 sendfn: Box<dyn Fn(&NonblockLogger, Option<Message>) + Send + Sync + 'static>,
49 sender: Sender,
50 receiver: Option<Receiver>,
51 exited: AtomicBool,
52 quiet: bool,
53}
54
55pub type Sender = channel::Sender<Option<Message>>;
56pub type Receiver = channel::Receiver<Option<Message>>;
57
58#[derive(Debug, Clone)]
59pub struct Message {
60 pub content: String,
61 pub level: Level,
62}
63
64impl Message {
65 pub fn new(content: String, level: Level) -> Self {
66 Self { content, level }
67 }
68}
69
70impl NonblockLogger {
71 pub fn new() -> Self {
72 let (mp, mc) = channel::unbounded();
73 Self::new2(mp, mc)
74 }
75
76 pub fn with_capacity(cap: usize) -> Self {
77 let (mp, mc) = channel::bounded(cap);
78 Self::new2(mp, mc)
79 }
80
81 fn new2(mp: Sender, mc: Receiver) -> Self {
82 Self {
83 name: None,
84 sender: mp,
85 receiver: Some(mc),
86 sendfn: Box::new(sendfn) as _,
87 exited: AtomicBool::new(false),
88 quiet: false,
89 filter: BaseFilter::new().boxed().unwrap(),
90 formater: BaseFormater::new().boxed(),
91 consumer: Some(BaseConsumer::new().boxed().unwrap()),
92 }
93 }
94
95 pub fn sendfn<F>(mut self, sendfn: F) -> Self
96 where
97 F: Fn(&NonblockLogger, Option<Message>) + Send + Sync + 'static,
98 {
99 self.sendfn = Box::new(sendfn) as _;
100 self
101 }
102
103 pub fn formater<F: Formater>(mut self, formater: F) -> Self {
104 self.formater = formater.boxed();
105 self
106 }
107
108 pub fn filter<F: Filter>(mut self, filter: F) -> Result<Self, Error> {
109 self.filter = filter.boxed()?;
110 Ok(self)
111 }
112
113 pub fn consumer<C: Consumer>(mut self, consumer: C) -> Result<Self, Error> {
114 self.consumer = Some(consumer.boxed()?);
115 Ok(self)
116 }
117
118 pub fn name<S: Into<String>>(mut self, name: S) -> Self {
119 self.name = Some(name.into());
120 self
121 }
122
123 pub fn name_get(&self) -> Option<&String> {
124 self.name.as_ref()
125 }
126
127 pub fn quiet(mut self) -> Self {
129 self.quiet = true;
130 self
131 }
132
133 pub fn quiet_get(&self) -> bool {
134 self.quiet
135 }
136
137 fn log_to_channel(mut self) -> Result<Receiver, SetLoggerError> {
138 let mc = mem::replace(&mut self.receiver, None).expect("NonblockLogger's receiver is None!");
139 set_max_level(self.filter.maxlevel());
140 let nob = NonblockLoggerGlobal(Arc::new(self));
141
142 unsafe {
143 LOGGER = Some(nob);
144 assert!(LOGGER.is_some());
145 let np = LOGGER.as_ref().unwrap() as _;
146 set_logger(np)?;
147 }
148
149 Ok(mc)
150 }
151
152 pub fn spawn(mut self) -> Result<JoinHandle, Error> {
153 let name = mem::replace(&mut self.name, None).unwrap_or_else(|| NAME.into());
154 let mut consumer = mem::replace(&mut self.consumer, None).unwrap();
155 let mc = self.log_to_channel()?;
156
157 thread::Builder::new()
158 .name(name)
159 .spawn(move || {
160 consumer.consume(mc);
161 Self::global().map(|g| g.exit());
162 })
163 .map(|jh| JoinHandle::new(Self::global().unwrap(), jh))
164 .map_err(Error::from)
165 }
166
167 pub fn log_to_stdout(mut self) -> Result<JoinHandle, Error> {
168 let maxlevel = self.filter.maxlevel();
169 self.consumer = Some(BaseConsumer::stdout(maxlevel).boxed()?);
170
171 self.spawn()
172 }
173
174 pub fn log_to_stderr(mut self) -> Result<JoinHandle, Error> {
175 let maxlevel = self.filter.maxlevel();
176 self.consumer = Some(BaseConsumer::stderr(maxlevel).boxed()?);
177
178 self.spawn()
179 }
180}
181
182impl NonblockLogger {
183 pub fn global() -> Option<&'static Self> {
184 unsafe { LOGGER.as_ref().map(|g| g.0.as_ref()) }
185 }
186
187 pub fn send_exit(&self) {
188 (*self.sendfn)(self, None)
189 }
190
191 pub fn exit(&self) {
192 self.exited.store(true, Ordering::SeqCst)
193 }
194
195 pub fn exited(&self) -> bool {
196 self.exited.load(Ordering::Relaxed)
197 }
198
199 pub fn messages_in_channel(&self) -> usize {
200 self.sender.len()
201 }
202}
203
204fn sendfn(logger: &NonblockLogger, msg: Option<Message>) {
206 let res = logger.sender.try_send(msg);
207
208 if let Err(e) = &res {
209 if logger.quiet {
210 return;
211 }
212
213 use crossbeam_channel::TrySendError::*;
214 let is_some = match e {
215 Full(t) => t,
216 Disconnected(t) => t,
217 }
218 .is_some();
219
220 let e = if is_some {
221 "NonblockLogger send log message falied!"
222 } else {
223 "NonblockLogger send exit message falied!"
224 };
225
226 res.expect(e);
227 }
228}
229
230pub fn messages_in_channel() -> usize {
231 NonblockLogger::global().map(|g| g.messages_in_channel()).unwrap_or(0)
232}
233
234pub struct JoinHandle {
235 logger: &'static NonblockLogger,
236 join_handle: Option<thread::JoinHandle<()>>,
237}
238
239impl JoinHandle {
240 fn new(logger: &'static NonblockLogger, join_handle: thread::JoinHandle<()>) -> Self {
241 Self {
242 logger,
243 join_handle: Some(join_handle),
244 }
245 }
246
247 pub fn join(&mut self) {
249 mem::replace(&mut self.join_handle, None).map(|h| {
250 self.logger.send_exit();
251 h.join().ok()
252 });
253 }
254}
255
256impl Drop for JoinHandle {
257 fn drop(&mut self) {
258 #[cfg(any(feature = "dbg"))]
259 dbg!(self.join_handle.is_some());
260 self.join()
261 }
262}
263
264struct NonblockLoggerGlobal(Arc<NonblockLogger>);
265
266impl Log for NonblockLoggerGlobal {
267 fn flush(&self) {}
268
269 fn enabled(&self, metadata: &Metadata) -> bool {
270 self.0.filter.enabled(metadata)
271 }
272
273 fn log(&self, record: &Record) {
274 let g = &self.0;
275
276 if g.filter.log(record) {
277 let content = g.formater.format(record);
278 let message = Message::new(content, record.level());
279
280 (*g.sendfn)(g, Some(message))
281 }
282 }
283}
284
285impl fmt::Debug for NonblockLogger {
286 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
287 fmt.debug_struct("NonblockLogger")
288 .field("name", &self.name)
289 .field("exited", &self.exited)
290 .finish()
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 #[test]
297 fn it_works() {
298 assert_eq!(2 + 2, 4);
299 }
300}