1 use std::{
2     borrow::Cow,
3     collections::HashMap,
4     fmt, fs,
5     io::{self, BufWriter, Write},
6     sync::{mpsc, Arc, Mutex},
7 };
8 
9 #[cfg(feature = "date-based")]
10 use std::{
11     ffi::OsString,
12     fs::OpenOptions,
13     path::{Path, PathBuf},
14 };
15 
16 use log::{self, Log};
17 
18 use crate::{Filter, Formatter};
19 
20 #[cfg(all(not(windows), feature = "syslog-4"))]
21 use crate::{Syslog4Rfc3164Logger, Syslog4Rfc5424Logger};
22 #[cfg(all(not(windows), feature = "reopen-03"))]
23 use reopen;
24 
25 pub enum LevelConfiguration {
26     JustDefault,
27     Minimal(Vec<(Cow<'static, str>, log::LevelFilter)>),
28     Many(HashMap<Cow<'static, str>, log::LevelFilter>),
29 }
30 
31 pub struct Dispatch {
32     pub output: Vec<Output>,
33     pub default_level: log::LevelFilter,
34     pub levels: LevelConfiguration,
35     pub format: Option<Box<Formatter>>,
36     pub filters: Vec<Box<Filter>>,
37 }
38 
39 /// Callback struct for use within a formatter closure
40 ///
41 /// Callbacks are used for formatting in order to allow usage of
42 /// [`std::fmt`]-based formatting without the allocation of the formatted
43 /// result which would be required to return it.
44 ///
45 /// Example usage:
46 ///
47 /// ```
48 /// fern::Dispatch::new().format(|callback: fern::FormatCallback, message, record| {
49 ///     callback.finish(format_args!("[{}] {}", record.level(), message))
50 /// })
51 /// # ;
52 /// ```
53 ///
54 /// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html
55 #[must_use = "format callback must be used for log to process correctly"]
56 pub struct FormatCallback<'a>(InnerFormatCallback<'a>);
57 
58 struct InnerFormatCallback<'a>(&'a mut bool, &'a Dispatch, &'a log::Record<'a>);
59 
60 pub enum Output {
61     Stdout(Stdout),
62     Stderr(Stderr),
63     File(File),
64     Sender(Sender),
65     #[cfg(all(not(windows), feature = "syslog-3"))]
66     Syslog3(Syslog3),
67     #[cfg(all(not(windows), feature = "syslog-4"))]
68     Syslog4Rfc3164(Syslog4Rfc3164),
69     #[cfg(all(not(windows), feature = "syslog-4"))]
70     Syslog4Rfc5424(Syslog4Rfc5424),
71     Dispatch(Dispatch),
72     SharedDispatch(Arc<Dispatch>),
73     OtherBoxed(Box<dyn Log>),
74     OtherStatic(&'static dyn Log),
75     Panic(Panic),
76     Writer(Writer),
77     #[cfg(feature = "date-based")]
78     DateBased(DateBased),
79     #[cfg(all(not(windows), feature = "reopen-03"))]
80     Reopen(Reopen),
81 }
82 
83 pub struct Stdout {
84     pub stream: io::Stdout,
85     pub line_sep: Cow<'static, str>,
86 }
87 
88 pub struct Stderr {
89     pub stream: io::Stderr,
90     pub line_sep: Cow<'static, str>,
91 }
92 
93 pub struct File {
94     pub stream: Mutex<BufWriter<fs::File>>,
95     pub line_sep: Cow<'static, str>,
96 }
97 
98 pub struct Sender {
99     pub stream: Mutex<mpsc::Sender<String>>,
100     pub line_sep: Cow<'static, str>,
101 }
102 
103 pub struct Writer {
104     pub stream: Mutex<Box<dyn Write + Send>>,
105     pub line_sep: Cow<'static, str>,
106 }
107 
108 #[cfg(all(not(windows), feature = "reopen-03"))]
109 pub struct Reopen {
110     pub stream: Mutex<reopen::Reopen<fs::File>>,
111     pub line_sep: Cow<'static, str>,
112 }
113 
114 #[cfg(all(not(windows), feature = "syslog-3"))]
115 pub struct Syslog3 {
116     pub inner: syslog3::Logger,
117 }
118 
119 #[cfg(all(not(windows), feature = "syslog-4"))]
120 pub struct Syslog4Rfc3164 {
121     pub inner: Mutex<Syslog4Rfc3164Logger>,
122 }
123 
124 #[cfg(all(not(windows), feature = "syslog-4"))]
125 pub struct Syslog4Rfc5424 {
126     pub inner: Mutex<Syslog4Rfc5424Logger>,
127     pub transform: Box<
128         dyn Fn(&log::Record) -> (i32, HashMap<String, HashMap<String, String>>, String)
129             + Sync
130             + Send,
131     >,
132 }
133 
134 pub struct Panic;
135 
136 pub struct Null;
137 
138 /// File logger with a dynamic time-based name.
139 #[derive(Debug)]
140 #[cfg(feature = "date-based")]
141 pub struct DateBased {
142     pub config: DateBasedConfig,
143     pub state: Mutex<DateBasedState>,
144 }
145 
146 #[derive(Debug)]
147 #[cfg(feature = "date-based")]
148 pub enum ConfiguredTimezone {
149     Local,
150     Utc,
151 }
152 
153 #[derive(Debug)]
154 #[cfg(feature = "date-based")]
155 pub struct DateBasedConfig {
156     pub line_sep: Cow<'static, str>,
157     /// This is a Path not an str so it can hold invalid UTF8 paths correctly.
158     pub file_prefix: PathBuf,
159     pub file_suffix: Cow<'static, str>,
160     pub timezone: ConfiguredTimezone,
161 }
162 
163 #[derive(Debug)]
164 #[cfg(feature = "date-based")]
165 pub struct DateBasedState {
166     pub current_suffix: String,
167     pub file_stream: Option<BufWriter<fs::File>>,
168 }
169 
170 #[cfg(feature = "date-based")]
171 impl DateBasedState {
new(current_suffix: String, file_stream: Option<fs::File>) -> Self172     pub fn new(current_suffix: String, file_stream: Option<fs::File>) -> Self {
173         DateBasedState {
174             current_suffix,
175             file_stream: file_stream.map(BufWriter::new),
176         }
177     }
178 
replace_file(&mut self, new_suffix: String, new_file: Option<fs::File>)179     pub fn replace_file(&mut self, new_suffix: String, new_file: Option<fs::File>) {
180         if let Some(mut old) = self.file_stream.take() {
181             let _ = old.flush();
182         }
183         self.current_suffix = new_suffix;
184         self.file_stream = new_file.map(BufWriter::new)
185     }
186 }
187 
188 #[cfg(feature = "date-based")]
189 impl DateBasedConfig {
new( line_sep: Cow<'static, str>, file_prefix: PathBuf, file_suffix: Cow<'static, str>, timezone: ConfiguredTimezone, ) -> Self190     pub fn new(
191         line_sep: Cow<'static, str>,
192         file_prefix: PathBuf,
193         file_suffix: Cow<'static, str>,
194         timezone: ConfiguredTimezone,
195     ) -> Self {
196         DateBasedConfig {
197             line_sep,
198             file_prefix,
199             file_suffix,
200             timezone,
201         }
202     }
203 
compute_current_suffix(&self) -> String204     pub fn compute_current_suffix(&self) -> String {
205         match self.timezone {
206             ConfiguredTimezone::Utc => chrono::Utc::now().format(&self.file_suffix).to_string(),
207             ConfiguredTimezone::Local => chrono::Local::now().format(&self.file_suffix).to_string(),
208         }
209     }
210 
compute_file_path(&self, suffix: &str) -> PathBuf211     pub fn compute_file_path(&self, suffix: &str) -> PathBuf {
212         let mut path = OsString::from(&*self.file_prefix);
213         // use the OsString::push method, not PathBuf::push which would add a path
214         // separator
215         path.push(suffix);
216         path.into()
217     }
218 
open_log_file(path: &Path) -> io::Result<fs::File>219     pub fn open_log_file(path: &Path) -> io::Result<fs::File> {
220         OpenOptions::new()
221             .write(true)
222             .create(true)
223             .append(true)
224             .open(path)
225     }
226 
open_current_log_file(&self, suffix: &str) -> io::Result<fs::File>227     pub fn open_current_log_file(&self, suffix: &str) -> io::Result<fs::File> {
228         Self::open_log_file(&self.compute_file_path(&suffix))
229     }
230 }
231 
232 impl From<Vec<(Cow<'static, str>, log::LevelFilter)>> for LevelConfiguration {
from(mut levels: Vec<(Cow<'static, str>, log::LevelFilter)>) -> Self233     fn from(mut levels: Vec<(Cow<'static, str>, log::LevelFilter)>) -> Self {
234         // Benchmarked separately: https://gist.github.com/daboross/976978d8200caf86e02acb6805961195
235         // Use Vec if there are fewer than 15 items, HashMap if there are more than 15.
236         match levels.len() {
237             0 => LevelConfiguration::JustDefault,
238             x if x > 15 => LevelConfiguration::Many(levels.into_iter().collect()),
239             _ => {
240                 levels.shrink_to_fit();
241                 LevelConfiguration::Minimal(levels)
242             }
243         }
244     }
245 }
246 
247 impl LevelConfiguration {
248     // inline since we use it literally once.
249     #[inline]
find_module(&self, module: &str) -> Option<log::LevelFilter>250     fn find_module(&self, module: &str) -> Option<log::LevelFilter> {
251         match *self {
252             LevelConfiguration::JustDefault => None,
253             _ => {
254                 if let Some(level) = self.find_exact(module) {
255                     return Some(level);
256                 }
257 
258                 // The manual for loop here lets us just iterate over the module string once
259                 // while still finding each sub-module. For the module string
260                 // "hyper::http::h1", this loop will test first "hyper::http"
261                 // then "hyper".
262                 let mut last_char_colon = false;
263 
264                 for (index, ch) in module.char_indices().rev() {
265                     if last_char_colon {
266                         last_char_colon = false;
267                         if ch == ':' {
268                             let sub_module = &module[0..index];
269 
270                             if let Some(level) = self.find_exact(sub_module) {
271                                 return Some(level);
272                             }
273                         }
274                     } else if ch == ':' {
275                         last_char_colon = true;
276                     }
277                 }
278 
279                 None
280             }
281         }
282     }
283 
find_exact(&self, module: &str) -> Option<log::LevelFilter>284     fn find_exact(&self, module: &str) -> Option<log::LevelFilter> {
285         match *self {
286             LevelConfiguration::JustDefault => None,
287             LevelConfiguration::Minimal(ref levels) => levels
288                 .iter()
289                 .find(|&&(ref test_module, _)| test_module == module)
290                 .map(|&(_, level)| level),
291             LevelConfiguration::Many(ref levels) => levels.get(module).cloned(),
292         }
293     }
294 }
295 
296 impl Log for Output {
enabled(&self, metadata: &log::Metadata) -> bool297     fn enabled(&self, metadata: &log::Metadata) -> bool {
298         match *self {
299             Output::Stdout(ref s) => s.enabled(metadata),
300             Output::Stderr(ref s) => s.enabled(metadata),
301             Output::File(ref s) => s.enabled(metadata),
302             Output::Sender(ref s) => s.enabled(metadata),
303             Output::Dispatch(ref s) => s.enabled(metadata),
304             Output::SharedDispatch(ref s) => s.enabled(metadata),
305             Output::OtherBoxed(ref s) => s.enabled(metadata),
306             Output::OtherStatic(ref s) => s.enabled(metadata),
307             #[cfg(all(not(windows), feature = "syslog-3"))]
308             Output::Syslog3(ref s) => s.enabled(metadata),
309             #[cfg(all(not(windows), feature = "syslog-4"))]
310             Output::Syslog4Rfc3164(ref s) => s.enabled(metadata),
311             #[cfg(all(not(windows), feature = "syslog-4"))]
312             Output::Syslog4Rfc5424(ref s) => s.enabled(metadata),
313             Output::Panic(ref s) => s.enabled(metadata),
314             Output::Writer(ref s) => s.enabled(metadata),
315             #[cfg(feature = "date-based")]
316             Output::DateBased(ref s) => s.enabled(metadata),
317             #[cfg(all(not(windows), feature = "reopen-03"))]
318             Output::Reopen(ref s) => s.enabled(metadata),
319         }
320     }
321 
log(&self, record: &log::Record)322     fn log(&self, record: &log::Record) {
323         match *self {
324             Output::Stdout(ref s) => s.log(record),
325             Output::Stderr(ref s) => s.log(record),
326             Output::File(ref s) => s.log(record),
327             Output::Sender(ref s) => s.log(record),
328             Output::Dispatch(ref s) => s.log(record),
329             Output::SharedDispatch(ref s) => s.log(record),
330             Output::OtherBoxed(ref s) => s.log(record),
331             Output::OtherStatic(ref s) => s.log(record),
332             #[cfg(all(not(windows), feature = "syslog-3"))]
333             Output::Syslog3(ref s) => s.log(record),
334             #[cfg(all(not(windows), feature = "syslog-4"))]
335             Output::Syslog4Rfc3164(ref s) => s.log(record),
336             #[cfg(all(not(windows), feature = "syslog-4"))]
337             Output::Syslog4Rfc5424(ref s) => s.log(record),
338             Output::Panic(ref s) => s.log(record),
339             Output::Writer(ref s) => s.log(record),
340             #[cfg(feature = "date-based")]
341             Output::DateBased(ref s) => s.log(record),
342             #[cfg(all(not(windows), feature = "reopen-03"))]
343             Output::Reopen(ref s) => s.log(record),
344         }
345     }
346 
flush(&self)347     fn flush(&self) {
348         match *self {
349             Output::Stdout(ref s) => s.flush(),
350             Output::Stderr(ref s) => s.flush(),
351             Output::File(ref s) => s.flush(),
352             Output::Sender(ref s) => s.flush(),
353             Output::Dispatch(ref s) => s.flush(),
354             Output::SharedDispatch(ref s) => s.flush(),
355             Output::OtherBoxed(ref s) => s.flush(),
356             Output::OtherStatic(ref s) => s.flush(),
357             #[cfg(all(not(windows), feature = "syslog-3"))]
358             Output::Syslog3(ref s) => s.flush(),
359             #[cfg(all(not(windows), feature = "syslog-4"))]
360             Output::Syslog4Rfc3164(ref s) => s.flush(),
361             #[cfg(all(not(windows), feature = "syslog-4"))]
362             Output::Syslog4Rfc5424(ref s) => s.flush(),
363             Output::Panic(ref s) => s.flush(),
364             Output::Writer(ref s) => s.flush(),
365             #[cfg(feature = "date-based")]
366             Output::DateBased(ref s) => s.flush(),
367             #[cfg(all(not(windows), feature = "reopen-03"))]
368             Output::Reopen(ref s) => s.flush(),
369         }
370     }
371 }
372 
373 impl Log for Null {
enabled(&self, _: &log::Metadata) -> bool374     fn enabled(&self, _: &log::Metadata) -> bool {
375         false
376     }
377 
log(&self, _: &log::Record)378     fn log(&self, _: &log::Record) {}
379 
flush(&self)380     fn flush(&self) {}
381 }
382 
383 impl Log for Dispatch {
enabled(&self, metadata: &log::Metadata) -> bool384     fn enabled(&self, metadata: &log::Metadata) -> bool {
385         self.deep_enabled(metadata)
386     }
387 
log(&self, record: &log::Record)388     fn log(&self, record: &log::Record) {
389         if self.shallow_enabled(record.metadata()) {
390             match self.format {
391                 Some(ref format) => {
392                     // flag to ensure the log message is completed even if the formatter doesn't
393                     // complete the callback.
394                     let mut callback_called_flag = false;
395 
396                     (format)(
397                         FormatCallback(InnerFormatCallback(
398                             &mut callback_called_flag,
399                             self,
400                             record,
401                         )),
402                         record.args(),
403                         record,
404                     );
405 
406                     if !callback_called_flag {
407                         self.finish_logging(record);
408                     }
409                 }
410                 None => {
411                     self.finish_logging(record);
412                 }
413             }
414         }
415     }
416 
flush(&self)417     fn flush(&self) {
418         for log in &self.output {
419             log.flush();
420         }
421     }
422 }
423 
424 impl Dispatch {
finish_logging(&self, record: &log::Record)425     fn finish_logging(&self, record: &log::Record) {
426         for log in &self.output {
427             log.log(record);
428         }
429     }
430 
431     /// Check whether this log's filters prevent the given log from happening.
shallow_enabled(&self, metadata: &log::Metadata) -> bool432     fn shallow_enabled(&self, metadata: &log::Metadata) -> bool {
433         metadata.level()
434             <= self
435                 .levels
436                 .find_module(metadata.target())
437                 .unwrap_or(self.default_level)
438             && self.filters.iter().all(|f| f(metadata))
439     }
440 
441     /// Check whether a log with the given metadata would eventually end up
442     /// outputting something.
443     ///
444     /// This is recursive, and checks children.
deep_enabled(&self, metadata: &log::Metadata) -> bool445     fn deep_enabled(&self, metadata: &log::Metadata) -> bool {
446         self.shallow_enabled(metadata) && self.output.iter().any(|l| l.enabled(metadata))
447     }
448 }
449 
450 impl<'a> FormatCallback<'a> {
451     /// Complete the formatting call that this FormatCallback was created for.
452     ///
453     /// This will call the rest of the logging chain using the given formatted
454     /// message as the new payload message.
455     ///
456     /// Example usage:
457     ///
458     /// ```
459     /// # fern::Dispatch::new()
460     /// # .format(|callback: fern::FormatCallback, message, record| {
461     /// callback.finish(format_args!("[{}] {}", record.level(), message))
462     /// # })
463     /// # .into_log();
464     /// ```
465     ///
466     /// See [`format_args!`].
467     ///
468     /// [`format_args!`]: https://doc.rust-lang.org/std/macro.format_args.html
finish(self, formatted_message: fmt::Arguments)469     pub fn finish(self, formatted_message: fmt::Arguments) {
470         let FormatCallback(InnerFormatCallback(callback_called_flag, dispatch, record)) = self;
471 
472         // let the dispatch know that we did in fact get called.
473         *callback_called_flag = true;
474 
475         // NOTE: This needs to be updated whenever new things are added to
476         // `log::Record`.
477         let new_record = log::RecordBuilder::new()
478             .args(formatted_message)
479             .metadata(record.metadata().clone())
480             .level(record.level())
481             .target(record.target())
482             .module_path(record.module_path())
483             .file(record.file())
484             .line(record.line())
485             .build();
486 
487         dispatch.finish_logging(&new_record);
488     }
489 }
490 
491 // No need to write this twice (used for Stdout and Stderr structs)
492 macro_rules! std_log_impl {
493     ($ident:ident) => {
494         impl Log for $ident {
495             fn enabled(&self, _: &log::Metadata) -> bool {
496                 true
497             }
498 
499             fn log(&self, record: &log::Record) {
500                 fallback_on_error(record, |record| {
501                     if cfg!(feature = "meta-logging-in-format") {
502                         // Formatting first prevents deadlocks when the process of formatting
503                         // itself is logged. note: this is only ever needed if some
504                         // Debug, Display, or other formatting trait itself is
505                         // logging things too.
506                         let msg = format!("{}{}", record.args(), self.line_sep);
507 
508                         write!(self.stream.lock(), "{}", msg)?;
509                     } else {
510                         write!(self.stream.lock(), "{}{}", record.args(), self.line_sep)?;
511                     }
512 
513                     Ok(())
514                 });
515             }
516 
517             fn flush(&self) {
518                 let _ = self.stream.lock().flush();
519             }
520         }
521     };
522 }
523 
524 std_log_impl!(Stdout);
525 std_log_impl!(Stderr);
526 
527 macro_rules! writer_log_impl {
528     ($ident:ident) => {
529         impl Log for $ident {
530             fn enabled(&self, _: &log::Metadata) -> bool {
531                 true
532             }
533 
534             fn log(&self, record: &log::Record) {
535                 fallback_on_error(record, |record| {
536                     if cfg!(feature = "meta-logging-in-format") {
537                         // Formatting first prevents deadlocks on file-logging,
538                         // when the process of formatting itself is logged.
539                         // note: this is only ever needed if some Debug, Display, or other
540                         // formatting trait itself is logging.
541                         let msg = format!("{}{}", record.args(), self.line_sep);
542 
543                         let mut writer = self.stream.lock().unwrap_or_else(|e| e.into_inner());
544 
545                         write!(writer, "{}", msg)?;
546 
547                         writer.flush()?;
548                     } else {
549                         let mut writer = self.stream.lock().unwrap_or_else(|e| e.into_inner());
550 
551                         write!(writer, "{}{}", record.args(), self.line_sep)?;
552 
553                         writer.flush()?;
554                     }
555                     Ok(())
556                 });
557             }
558 
559             fn flush(&self) {
560                 let _ = self
561                     .stream
562                     .lock()
563                     .unwrap_or_else(|e| e.into_inner())
564                     .flush();
565             }
566         }
567     };
568 }
569 
570 writer_log_impl!(File);
571 writer_log_impl!(Writer);
572 
573 #[cfg(all(not(windows), feature = "reopen-03"))]
574 writer_log_impl!(Reopen);
575 
576 impl Log for Sender {
enabled(&self, _: &log::Metadata) -> bool577     fn enabled(&self, _: &log::Metadata) -> bool {
578         true
579     }
580 
log(&self, record: &log::Record)581     fn log(&self, record: &log::Record) {
582         fallback_on_error(record, |record| {
583             let msg = format!("{}{}", record.args(), self.line_sep);
584             self.stream
585                 .lock()
586                 .unwrap_or_else(|e| e.into_inner())
587                 .send(msg)?;
588             Ok(())
589         });
590     }
591 
flush(&self)592     fn flush(&self) {}
593 }
594 
595 #[cfg(any(feature = "syslog-3", feature = "syslog-4"))]
596 macro_rules! send_syslog {
597     ($logger:expr, $level:expr, $message:expr) => {
598         use log::Level;
599         match $level {
600             Level::Error => $logger.err($message)?,
601             Level::Warn => $logger.warning($message)?,
602             Level::Info => $logger.info($message)?,
603             Level::Debug | Level::Trace => $logger.debug($message)?,
604         }
605     };
606 }
607 
608 #[cfg(all(not(windows), feature = "syslog-3"))]
609 impl Log for Syslog3 {
enabled(&self, _: &log::Metadata) -> bool610     fn enabled(&self, _: &log::Metadata) -> bool {
611         true
612     }
613 
log(&self, record: &log::Record)614     fn log(&self, record: &log::Record) {
615         fallback_on_error(record, |record| {
616             let message = record.args();
617             send_syslog!(self.inner, record.level(), message);
618 
619             Ok(())
620         });
621     }
flush(&self)622     fn flush(&self) {}
623 }
624 
625 #[cfg(all(not(windows), feature = "syslog-4"))]
626 impl Log for Syslog4Rfc3164 {
enabled(&self, _: &log::Metadata) -> bool627     fn enabled(&self, _: &log::Metadata) -> bool {
628         true
629     }
630 
log(&self, record: &log::Record)631     fn log(&self, record: &log::Record) {
632         fallback_on_error(record, |record| {
633             let message = record.args().to_string();
634             let mut log = self.inner.lock().unwrap_or_else(|e| e.into_inner());
635             send_syslog!(log, record.level(), message);
636 
637             Ok(())
638         });
639     }
flush(&self)640     fn flush(&self) {}
641 }
642 
643 #[cfg(all(not(windows), feature = "syslog-4"))]
644 impl Log for Syslog4Rfc5424 {
enabled(&self, _: &log::Metadata) -> bool645     fn enabled(&self, _: &log::Metadata) -> bool {
646         true
647     }
648 
log(&self, record: &log::Record)649     fn log(&self, record: &log::Record) {
650         fallback_on_error(record, |record| {
651             let transformed = (self.transform)(record);
652             let mut log = self.inner.lock().unwrap_or_else(|e| e.into_inner());
653             send_syslog!(log, record.level(), transformed);
654 
655             Ok(())
656         });
657     }
flush(&self)658     fn flush(&self) {}
659 }
660 
661 impl Log for Panic {
enabled(&self, _: &log::Metadata) -> bool662     fn enabled(&self, _: &log::Metadata) -> bool {
663         true
664     }
665 
log(&self, record: &log::Record)666     fn log(&self, record: &log::Record) {
667         panic!("{}", record.args());
668     }
669 
flush(&self)670     fn flush(&self) {}
671 }
672 
673 #[cfg(feature = "date-based")]
674 impl Log for DateBased {
enabled(&self, _: &log::Metadata) -> bool675     fn enabled(&self, _: &log::Metadata) -> bool {
676         true
677     }
678 
log(&self, record: &log::Record)679     fn log(&self, record: &log::Record) {
680         fallback_on_error(record, |record| {
681             // Formatting first prevents deadlocks on file-logging,
682             // when the process of formatting itself is logged.
683             // note: this is only ever needed if some Debug, Display, or other
684             // formatting trait itself is logging.
685             #[cfg(feature = "meta-logging-in-format")]
686             let msg = format!("{}{}", record.args(), self.config.line_sep);
687 
688             let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
689 
690             // check if log needs to be rotated
691             let new_suffix = self.config.compute_current_suffix();
692             if state.file_stream.is_none() || state.current_suffix != new_suffix {
693                 let file_open_result = self.config.open_current_log_file(&new_suffix);
694                 match file_open_result {
695                     Ok(file) => {
696                         state.replace_file(new_suffix, Some(file));
697                     }
698                     Err(e) => {
699                         state.replace_file(new_suffix, None);
700                         return Err(e.into());
701                     }
702                 }
703             }
704 
705             // either just initialized writer above, or already errored out.
706             let writer = state.file_stream.as_mut().unwrap();
707 
708             #[cfg(feature = "meta-logging-in-format")]
709             write!(writer, "{}", msg)?;
710             #[cfg(not(feature = "meta-logging-in-format"))]
711             write!(writer, "{}{}", record.args(), self.config.line_sep)?;
712 
713             writer.flush()?;
714 
715             Ok(())
716         });
717     }
718 
flush(&self)719     fn flush(&self) {
720         let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
721 
722         if let Some(stream) = &mut state.file_stream {
723             let _ = stream.flush();
724         }
725     }
726 }
727 
728 #[inline(always)]
fallback_on_error<F>(record: &log::Record, log_func: F) where F: FnOnce(&log::Record) -> Result<(), LogError>,729 fn fallback_on_error<F>(record: &log::Record, log_func: F)
730 where
731     F: FnOnce(&log::Record) -> Result<(), LogError>,
732 {
733     if let Err(error) = log_func(record) {
734         backup_logging(record, &error)
735     }
736 }
737 
backup_logging(record: &log::Record, error: &LogError)738 fn backup_logging(record: &log::Record, error: &LogError) {
739     let second = write!(
740         io::stderr(),
741         "Error performing logging.\
742          \n\tattempted to log: {}\
743          \n\trecord: {:?}\
744          \n\tlogging error: {}",
745         record.args(),
746         record,
747         error
748     );
749 
750     if let Err(second_error) = second {
751         panic!(
752             "Error performing stderr logging after error occurred during regular logging.\
753              \n\tattempted to log: {}\
754              \n\trecord: {:?}\
755              \n\tfirst logging error: {}\
756              \n\tstderr error: {}",
757             record.args(),
758             record,
759             error,
760             second_error,
761         );
762     }
763 }
764 
765 #[derive(Debug)]
766 enum LogError {
767     Io(io::Error),
768     Send(mpsc::SendError<String>),
769     #[cfg(all(not(windows), feature = "syslog-4"))]
770     Syslog4(syslog4::Error),
771 }
772 
773 impl fmt::Display for LogError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result774     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
775         match *self {
776             LogError::Io(ref e) => write!(f, "{}", e),
777             LogError::Send(ref e) => write!(f, "{}", e),
778             #[cfg(all(not(windows), feature = "syslog-4"))]
779             LogError::Syslog4(ref e) => write!(f, "{}", e),
780         }
781     }
782 }
783 
784 impl From<io::Error> for LogError {
from(error: io::Error) -> Self785     fn from(error: io::Error) -> Self {
786         LogError::Io(error)
787     }
788 }
789 
790 impl From<mpsc::SendError<String>> for LogError {
from(error: mpsc::SendError<String>) -> Self791     fn from(error: mpsc::SendError<String>) -> Self {
792         LogError::Send(error)
793     }
794 }
795 
796 #[cfg(all(not(windows), feature = "syslog-4"))]
797 impl From<syslog4::Error> for LogError {
from(error: syslog4::Error) -> Self798     fn from(error: syslog4::Error) -> Self {
799         LogError::Syslog4(error)
800     }
801 }
802 
803 #[cfg(test)]
804 mod test {
805     use super::LevelConfiguration;
806     use log::LevelFilter::*;
807 
808     #[test]
test_level_config_find_exact_minimal()809     fn test_level_config_find_exact_minimal() {
810         let config = LevelConfiguration::Minimal(
811             vec![("mod1", Info), ("mod2", Debug), ("mod3", Off)]
812                 .into_iter()
813                 .map(|(k, v)| (k.into(), v))
814                 .collect(),
815         );
816 
817         assert_eq!(config.find_exact("mod1"), Some(Info));
818         assert_eq!(config.find_exact("mod2"), Some(Debug));
819         assert_eq!(config.find_exact("mod3"), Some(Off));
820     }
821 
822     #[test]
test_level_config_find_exact_many()823     fn test_level_config_find_exact_many() {
824         let config = LevelConfiguration::Many(
825             vec![("mod1", Info), ("mod2", Debug), ("mod3", Off)]
826                 .into_iter()
827                 .map(|(k, v)| (k.into(), v))
828                 .collect(),
829         );
830 
831         assert_eq!(config.find_exact("mod1"), Some(Info));
832         assert_eq!(config.find_exact("mod2"), Some(Debug));
833         assert_eq!(config.find_exact("mod3"), Some(Off));
834     }
835 
836     #[test]
test_level_config_simple_hierarchy()837     fn test_level_config_simple_hierarchy() {
838         let config = LevelConfiguration::Minimal(
839             vec![("mod1", Info), ("mod2::sub_mod", Debug), ("mod3", Off)]
840                 .into_iter()
841                 .map(|(k, v)| (k.into(), v))
842                 .collect(),
843         );
844 
845         assert_eq!(config.find_module("mod1::sub_mod"), Some(Info));
846         assert_eq!(config.find_module("mod2::sub_mod::sub_mod_2"), Some(Debug));
847         assert_eq!(config.find_module("mod3::sub_mod::sub_mod_2"), Some(Off));
848     }
849 
850     #[test]
test_level_config_hierarchy_correct()851     fn test_level_config_hierarchy_correct() {
852         let config = LevelConfiguration::Minimal(
853             vec![
854                 ("root", Trace),
855                 ("root::sub1", Debug),
856                 ("root::sub2", Info),
857                 // should work with all insertion orders
858                 ("root::sub2::sub2.3::sub2.4", Error),
859                 ("root::sub2::sub2.3", Warn),
860                 ("root::sub3", Off),
861             ]
862             .into_iter()
863             .map(|(k, v)| (k.into(), v))
864             .collect(),
865         );
866 
867         assert_eq!(config.find_module("root"), Some(Trace));
868         assert_eq!(config.find_module("root::other_module"), Some(Trace));
869 
870         // We want to ensure that it does pick up most specific level before trying
871         // anything more general.
872         assert_eq!(config.find_module("root::sub1"), Some(Debug));
873         assert_eq!(config.find_module("root::sub1::other_module"), Some(Debug));
874 
875         assert_eq!(config.find_module("root::sub2"), Some(Info));
876         assert_eq!(config.find_module("root::sub2::other"), Some(Info));
877 
878         assert_eq!(config.find_module("root::sub2::sub2.3"), Some(Warn));
879         assert_eq!(
880             config.find_module("root::sub2::sub2.3::sub2.4"),
881             Some(Error)
882         );
883 
884         assert_eq!(config.find_module("root::sub3"), Some(Off));
885         assert_eq!(
886             config.find_module("root::sub3::any::children::of::sub3"),
887             Some(Off)
888         );
889     }
890 
891     #[test]
test_level_config_similar_names_are_not_same()892     fn test_level_config_similar_names_are_not_same() {
893         let config = LevelConfiguration::Minimal(
894             vec![("root", Trace), ("rootay", Info)]
895                 .into_iter()
896                 .map(|(k, v)| (k.into(), v))
897                 .collect(),
898         );
899 
900         assert_eq!(config.find_module("root"), Some(Trace));
901         assert_eq!(config.find_module("root::sub"), Some(Trace));
902         assert_eq!(config.find_module("rooty"), None);
903         assert_eq!(config.find_module("rooty::sub"), None);
904         assert_eq!(config.find_module("rootay"), Some(Info));
905         assert_eq!(config.find_module("rootay::sub"), Some(Info));
906     }
907 
908     #[test]
test_level_config_single_colon_is_not_double_colon()909     fn test_level_config_single_colon_is_not_double_colon() {
910         let config = LevelConfiguration::Minimal(
911             vec![
912                 ("root", Trace),
913                 ("root::su", Debug),
914                 ("root::su:b2", Info),
915                 ("root::sub2", Warn),
916             ]
917             .into_iter()
918             .map(|(k, v)| (k.into(), v))
919             .collect(),
920         );
921 
922         assert_eq!(config.find_module("root"), Some(Trace));
923 
924         assert_eq!(config.find_module("root::su"), Some(Debug));
925         assert_eq!(config.find_module("root::su::b2"), Some(Debug));
926 
927         assert_eq!(config.find_module("root::su:b2"), Some(Info));
928         assert_eq!(config.find_module("root::su:b2::b3"), Some(Info));
929 
930         assert_eq!(config.find_module("root::sub2"), Some(Warn));
931         assert_eq!(config.find_module("root::sub2::b3"), Some(Warn));
932     }
933 
934     #[test]
test_level_config_all_chars()935     fn test_level_config_all_chars() {
936         let config = LevelConfiguration::Minimal(
937             vec![("♲", Trace), ("☸", Debug), ("♲::☸", Info), ("♲::\t", Debug)]
938                 .into_iter()
939                 .map(|(k, v)| (k.into(), v))
940                 .collect(),
941         );
942 
943         assert_eq!(config.find_module("♲"), Some(Trace));
944         assert_eq!(config.find_module("♲::other"), Some(Trace));
945 
946         assert_eq!(config.find_module("☸"), Some(Debug));
947         assert_eq!(config.find_module("☸::any"), Some(Debug));
948 
949         assert_eq!(config.find_module("♲::☸"), Some(Info));
950         assert_eq!(config.find_module("♲☸"), None);
951 
952         assert_eq!(config.find_module("♲::\t"), Some(Debug));
953         assert_eq!(config.find_module("♲::\t::\n\n::\t"), Some(Debug));
954         assert_eq!(config.find_module("♲::\t\t"), Some(Trace));
955     }
956 }
957