1use 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")]
10use std::{
11 ffi::OsString,
12 fs::OpenOptions,
13 path::{Path, PathBuf},
14};
15
16use log::{self, Log};
17
18use crate::{Filter, Formatter};
19
20#[cfg(all(not(windows), feature = "syslog-4"))]
21use crate::{Syslog4Rfc3164Logger, Syslog4Rfc5424Logger, Syslog4TransformFn};
22#[cfg(all(not(windows), feature = "syslog-6"))]
23use crate::{Syslog6Rfc3164Logger, Syslog6Rfc5424Logger, Syslog6TransformFn};
24#[cfg(all(not(windows), feature = "syslog-7"))]
25use crate::{Syslog7Rfc3164Logger, Syslog7Rfc5424Logger, Syslog7TransformFn};
26
27pub enum LevelConfiguration {
28 JustDefault,
29 Minimal(Vec<(Cow<'static, str>, log::LevelFilter)>),
30 Many(HashMap<Cow<'static, str>, log::LevelFilter>),
31}
32
33pub struct Dispatch {
34 pub output: Vec<Output>,
35 pub default_level: log::LevelFilter,
36 pub levels: LevelConfiguration,
37 pub format: Option<Box<Formatter>>,
38 pub filters: Vec<Box<Filter>>,
39}
40
41#[must_use = "format callback must be used for log to process correctly"]
58pub struct FormatCallback<'a>(InnerFormatCallback<'a>);
59
60struct InnerFormatCallback<'a>(&'a mut bool, &'a Dispatch, &'a log::Record<'a>);
61
62pub enum Output {
63 Stdout(Stdout),
64 Stderr(Stderr),
65 File(File),
66 Sender(Sender),
67 #[cfg(all(not(windows), feature = "syslog-3"))]
68 Syslog3(Syslog3),
69 #[cfg(all(not(windows), feature = "syslog-4"))]
70 Syslog4Rfc3164(Syslog4Rfc3164),
71 #[cfg(all(not(windows), feature = "syslog-4"))]
72 Syslog4Rfc5424(Syslog4Rfc5424),
73 #[cfg(all(not(windows), feature = "syslog-6"))]
74 Syslog6Rfc3164(Syslog6Rfc3164),
75 #[cfg(all(not(windows), feature = "syslog-6"))]
76 Syslog6Rfc5424(Syslog6Rfc5424),
77 #[cfg(all(not(windows), feature = "syslog-7"))]
78 Syslog7Rfc3164(Syslog7Rfc3164),
79 #[cfg(all(not(windows), feature = "syslog-7"))]
80 Syslog7Rfc5424(Syslog7Rfc5424),
81 Dispatch(Dispatch),
82 SharedDispatch(Arc<Dispatch>),
83 OtherBoxed(Box<dyn Log>),
84 OtherStatic(&'static dyn Log),
85 Panic(Panic),
86 Writer(Writer),
87 #[cfg(feature = "date-based")]
88 DateBased(DateBased),
89 #[cfg(all(not(windows), feature = "reopen-03"))]
90 Reopen(Reopen),
91 #[cfg(all(not(windows), feature = "reopen-1"))]
92 Reopen1(Reopen1),
93}
94
95pub struct Stdout {
96 pub stream: io::Stdout,
97 pub line_sep: Cow<'static, str>,
98}
99
100pub struct Stderr {
101 pub stream: io::Stderr,
102 pub line_sep: Cow<'static, str>,
103}
104
105pub struct File {
106 pub stream: Mutex<BufWriter<fs::File>>,
107 pub line_sep: Cow<'static, str>,
108}
109
110pub struct Sender {
111 pub stream: Mutex<mpsc::Sender<String>>,
112 pub line_sep: Cow<'static, str>,
113}
114
115pub struct Writer {
116 pub stream: Mutex<Box<dyn Write + Send>>,
117 pub line_sep: Cow<'static, str>,
118}
119
120#[cfg(all(not(windows), feature = "reopen-03"))]
121pub struct Reopen {
122 pub stream: Mutex<reopen03::Reopen<fs::File>>,
123 pub line_sep: Cow<'static, str>,
124}
125
126#[cfg(all(not(windows), feature = "reopen-1"))]
127pub struct Reopen1 {
128 pub stream: Mutex<reopen1::Reopen<fs::File>>,
129 pub line_sep: Cow<'static, str>,
130}
131
132#[cfg(all(not(windows), feature = "syslog-3"))]
133pub struct Syslog3 {
134 pub inner: syslog3::Logger,
135}
136
137#[cfg(all(not(windows), feature = "syslog-4"))]
138pub struct Syslog4Rfc3164 {
139 pub inner: Mutex<Syslog4Rfc3164Logger>,
140}
141
142#[cfg(all(not(windows), feature = "syslog-4"))]
143pub struct Syslog4Rfc5424 {
144 pub inner: Mutex<Syslog4Rfc5424Logger>,
145 pub transform: Box<Syslog4TransformFn>,
146}
147
148#[cfg(all(not(windows), feature = "syslog-6"))]
149pub struct Syslog6Rfc3164 {
150 pub inner: Mutex<Syslog6Rfc3164Logger>,
151}
152
153#[cfg(all(not(windows), feature = "syslog-6"))]
154pub struct Syslog6Rfc5424 {
155 pub inner: Mutex<Syslog6Rfc5424Logger>,
156 pub transform: Box<Syslog6TransformFn>,
157}
158
159#[cfg(all(not(windows), feature = "syslog-7"))]
160pub struct Syslog7Rfc3164 {
161 pub inner: Mutex<Syslog7Rfc3164Logger>,
162}
163
164#[cfg(all(not(windows), feature = "syslog-7"))]
165pub struct Syslog7Rfc5424 {
166 pub inner: Mutex<Syslog7Rfc5424Logger>,
167 pub transform: Box<Syslog7TransformFn>,
168}
169
170pub struct Panic;
171
172pub struct Null;
173
174#[derive(Debug)]
176#[cfg(feature = "date-based")]
177pub struct DateBased {
178 pub config: DateBasedConfig,
179 pub state: Mutex<DateBasedState>,
180}
181
182#[derive(Debug)]
183#[cfg(feature = "date-based")]
184pub enum ConfiguredTimezone {
185 Local,
186 Utc,
187}
188
189#[derive(Debug)]
190#[cfg(feature = "date-based")]
191pub struct DateBasedConfig {
192 pub line_sep: Cow<'static, str>,
193 pub file_prefix: PathBuf,
195 pub file_suffix: Cow<'static, str>,
196 pub timezone: ConfiguredTimezone,
197}
198
199#[derive(Debug)]
200#[cfg(feature = "date-based")]
201pub struct DateBasedState {
202 pub current_suffix: String,
203 pub file_stream: Option<BufWriter<fs::File>>,
204}
205
206#[cfg(feature = "date-based")]
207impl DateBasedState {
208 pub fn new(current_suffix: String, file_stream: Option<fs::File>) -> Self {
209 DateBasedState {
210 current_suffix,
211 file_stream: file_stream.map(BufWriter::new),
212 }
213 }
214
215 pub fn replace_file(&mut self, new_suffix: String, new_file: Option<fs::File>) {
216 if let Some(mut old) = self.file_stream.take() {
217 let _ = old.flush();
218 }
219 self.current_suffix = new_suffix;
220 self.file_stream = new_file.map(BufWriter::new)
221 }
222}
223
224#[cfg(feature = "date-based")]
225impl DateBasedConfig {
226 pub fn new(
227 line_sep: Cow<'static, str>,
228 file_prefix: PathBuf,
229 file_suffix: Cow<'static, str>,
230 timezone: ConfiguredTimezone,
231 ) -> Self {
232 DateBasedConfig {
233 line_sep,
234 file_prefix,
235 file_suffix,
236 timezone,
237 }
238 }
239
240 pub fn compute_current_suffix(&self) -> String {
241 match self.timezone {
242 ConfiguredTimezone::Utc => chrono::Utc::now().format(&self.file_suffix).to_string(),
243 ConfiguredTimezone::Local => chrono::Local::now().format(&self.file_suffix).to_string(),
244 }
245 }
246
247 pub fn compute_file_path(&self, suffix: &str) -> PathBuf {
248 let mut path = OsString::from(&*self.file_prefix);
249 path.push(suffix);
252 path.into()
253 }
254
255 pub fn open_log_file(path: &Path) -> io::Result<fs::File> {
256 OpenOptions::new().create(true).append(true).open(path)
257 }
258
259 pub fn open_current_log_file(&self, suffix: &str) -> io::Result<fs::File> {
260 Self::open_log_file(&self.compute_file_path(suffix))
261 }
262}
263
264impl From<Vec<(Cow<'static, str>, log::LevelFilter)>> for LevelConfiguration {
265 fn from(mut levels: Vec<(Cow<'static, str>, log::LevelFilter)>) -> Self {
266 match levels.len() {
269 0 => LevelConfiguration::JustDefault,
270 x if x > 15 => LevelConfiguration::Many(levels.into_iter().collect()),
271 _ => {
272 levels.shrink_to_fit();
273 LevelConfiguration::Minimal(levels)
274 }
275 }
276 }
277}
278
279impl LevelConfiguration {
280 #[inline]
282 fn find_module(&self, module: &str) -> Option<log::LevelFilter> {
283 match *self {
284 LevelConfiguration::JustDefault => None,
285 _ => {
286 if let Some(level) = self.find_exact(module) {
287 return Some(level);
288 }
289
290 let mut last_char_colon = false;
295
296 for (index, ch) in module.char_indices().rev() {
297 if last_char_colon {
298 last_char_colon = false;
299 if ch == ':' {
300 let sub_module = &module[0..index];
301
302 if let Some(level) = self.find_exact(sub_module) {
303 return Some(level);
304 }
305 }
306 } else if ch == ':' {
307 last_char_colon = true;
308 }
309 }
310
311 None
312 }
313 }
314 }
315
316 fn find_exact(&self, module: &str) -> Option<log::LevelFilter> {
317 match *self {
318 LevelConfiguration::JustDefault => None,
319 LevelConfiguration::Minimal(ref levels) => levels
320 .iter()
321 .find(|(test_module, _)| test_module == module)
322 .map(|(_, level)| *level),
323 LevelConfiguration::Many(ref levels) => levels.get(module).cloned(),
324 }
325 }
326}
327
328impl Log for Output {
329 fn enabled(&self, metadata: &log::Metadata) -> bool {
330 match *self {
331 Output::Stdout(ref s) => s.enabled(metadata),
332 Output::Stderr(ref s) => s.enabled(metadata),
333 Output::File(ref s) => s.enabled(metadata),
334 Output::Sender(ref s) => s.enabled(metadata),
335 Output::Dispatch(ref s) => s.enabled(metadata),
336 Output::SharedDispatch(ref s) => s.enabled(metadata),
337 Output::OtherBoxed(ref s) => s.enabled(metadata),
338 Output::OtherStatic(ref s) => s.enabled(metadata),
339 #[cfg(all(not(windows), feature = "syslog-3"))]
340 Output::Syslog3(ref s) => s.enabled(metadata),
341 #[cfg(all(not(windows), feature = "syslog-4"))]
342 Output::Syslog4Rfc3164(ref s) => s.enabled(metadata),
343 #[cfg(all(not(windows), feature = "syslog-4"))]
344 Output::Syslog4Rfc5424(ref s) => s.enabled(metadata),
345 #[cfg(all(not(windows), feature = "syslog-6"))]
346 Output::Syslog6Rfc3164(ref s) => s.enabled(metadata),
347 #[cfg(all(not(windows), feature = "syslog-6"))]
348 Output::Syslog6Rfc5424(ref s) => s.enabled(metadata),
349 #[cfg(all(not(windows), feature = "syslog-7"))]
350 Output::Syslog7Rfc3164(ref s) => s.enabled(metadata),
351 #[cfg(all(not(windows), feature = "syslog-7"))]
352 Output::Syslog7Rfc5424(ref s) => s.enabled(metadata),
353 Output::Panic(ref s) => s.enabled(metadata),
354 Output::Writer(ref s) => s.enabled(metadata),
355 #[cfg(feature = "date-based")]
356 Output::DateBased(ref s) => s.enabled(metadata),
357 #[cfg(all(not(windows), feature = "reopen-03"))]
358 Output::Reopen(ref s) => s.enabled(metadata),
359 #[cfg(all(not(windows), feature = "reopen-1"))]
360 Output::Reopen1(ref s) => s.enabled(metadata),
361 }
362 }
363
364 fn log(&self, record: &log::Record) {
365 match *self {
366 Output::Stdout(ref s) => s.log(record),
367 Output::Stderr(ref s) => s.log(record),
368 Output::File(ref s) => s.log(record),
369 Output::Sender(ref s) => s.log(record),
370 Output::Dispatch(ref s) => s.log(record),
371 Output::SharedDispatch(ref s) => s.log(record),
372 Output::OtherBoxed(ref s) => s.log(record),
373 Output::OtherStatic(ref s) => s.log(record),
374 #[cfg(all(not(windows), feature = "syslog-3"))]
375 Output::Syslog3(ref s) => s.log(record),
376 #[cfg(all(not(windows), feature = "syslog-4"))]
377 Output::Syslog4Rfc3164(ref s) => s.log(record),
378 #[cfg(all(not(windows), feature = "syslog-4"))]
379 Output::Syslog4Rfc5424(ref s) => s.log(record),
380 #[cfg(all(not(windows), feature = "syslog-6"))]
381 Output::Syslog6Rfc3164(ref s) => s.log(record),
382 #[cfg(all(not(windows), feature = "syslog-6"))]
383 Output::Syslog6Rfc5424(ref s) => s.log(record),
384 #[cfg(all(not(windows), feature = "syslog-7"))]
385 Output::Syslog7Rfc3164(ref s) => s.log(record),
386 #[cfg(all(not(windows), feature = "syslog-7"))]
387 Output::Syslog7Rfc5424(ref s) => s.log(record),
388 Output::Panic(ref s) => s.log(record),
389 Output::Writer(ref s) => s.log(record),
390 #[cfg(feature = "date-based")]
391 Output::DateBased(ref s) => s.log(record),
392 #[cfg(all(not(windows), feature = "reopen-03"))]
393 Output::Reopen(ref s) => s.log(record),
394 #[cfg(all(not(windows), feature = "reopen-1"))]
395 Output::Reopen1(ref s) => s.log(record),
396 }
397 }
398
399 fn flush(&self) {
400 match *self {
401 Output::Stdout(ref s) => s.flush(),
402 Output::Stderr(ref s) => s.flush(),
403 Output::File(ref s) => s.flush(),
404 Output::Sender(ref s) => s.flush(),
405 Output::Dispatch(ref s) => s.flush(),
406 Output::SharedDispatch(ref s) => s.flush(),
407 Output::OtherBoxed(ref s) => s.flush(),
408 Output::OtherStatic(ref s) => s.flush(),
409 #[cfg(all(not(windows), feature = "syslog-3"))]
410 Output::Syslog3(ref s) => s.flush(),
411 #[cfg(all(not(windows), feature = "syslog-4"))]
412 Output::Syslog4Rfc3164(ref s) => s.flush(),
413 #[cfg(all(not(windows), feature = "syslog-4"))]
414 Output::Syslog4Rfc5424(ref s) => s.flush(),
415 #[cfg(all(not(windows), feature = "syslog-6"))]
416 Output::Syslog6Rfc3164(ref s) => s.flush(),
417 #[cfg(all(not(windows), feature = "syslog-6"))]
418 Output::Syslog6Rfc5424(ref s) => s.flush(),
419 #[cfg(all(not(windows), feature = "syslog-7"))]
420 Output::Syslog7Rfc3164(ref s) => s.flush(),
421 #[cfg(all(not(windows), feature = "syslog-7"))]
422 Output::Syslog7Rfc5424(ref s) => s.flush(),
423 Output::Panic(ref s) => s.flush(),
424 Output::Writer(ref s) => s.flush(),
425 #[cfg(feature = "date-based")]
426 Output::DateBased(ref s) => s.flush(),
427 #[cfg(all(not(windows), feature = "reopen-03"))]
428 Output::Reopen(ref s) => s.flush(),
429 #[cfg(all(not(windows), feature = "reopen-1"))]
430 Output::Reopen1(ref s) => s.flush(),
431 }
432 }
433}
434
435impl Log for Null {
436 fn enabled(&self, _: &log::Metadata) -> bool {
437 false
438 }
439
440 fn log(&self, _: &log::Record) {}
441
442 fn flush(&self) {}
443}
444
445impl Log for Dispatch {
446 fn enabled(&self, metadata: &log::Metadata) -> bool {
447 self.deep_enabled(metadata)
448 }
449
450 fn log(&self, record: &log::Record) {
451 if self.shallow_enabled(record.metadata()) {
452 match self.format {
453 Some(ref format) => {
454 let mut callback_called_flag = false;
457
458 (format)(
459 FormatCallback(InnerFormatCallback(
460 &mut callback_called_flag,
461 self,
462 record,
463 )),
464 record.args(),
465 record,
466 );
467
468 if !callback_called_flag {
469 self.finish_logging(record);
470 }
471 }
472 None => {
473 self.finish_logging(record);
474 }
475 }
476 }
477 }
478
479 fn flush(&self) {
480 for log in &self.output {
481 log.flush();
482 }
483 }
484}
485
486impl Dispatch {
487 fn finish_logging(&self, record: &log::Record) {
488 for log in &self.output {
489 log.log(record);
490 }
491 }
492
493 fn shallow_enabled(&self, metadata: &log::Metadata) -> bool {
495 metadata.level()
496 <= self
497 .levels
498 .find_module(metadata.target())
499 .unwrap_or(self.default_level)
500 && self.filters.iter().all(|f| f(metadata))
501 }
502
503 fn deep_enabled(&self, metadata: &log::Metadata) -> bool {
508 self.shallow_enabled(metadata) && self.output.iter().any(|l| l.enabled(metadata))
509 }
510}
511
512impl<'a> FormatCallback<'a> {
513 pub fn finish(self, formatted_message: fmt::Arguments) {
532 let FormatCallback(InnerFormatCallback(callback_called_flag, dispatch, record)) = self;
533
534 *callback_called_flag = true;
536
537 let new_record = log::RecordBuilder::new()
540 .args(formatted_message)
541 .metadata(record.metadata().clone())
542 .level(record.level())
543 .target(record.target())
544 .module_path(record.module_path())
545 .file(record.file())
546 .line(record.line())
547 .build();
548
549 dispatch.finish_logging(&new_record);
550 }
551}
552
553macro_rules! std_log_impl {
555 ($ident:ident) => {
556 impl Log for $ident {
557 fn enabled(&self, _: &log::Metadata) -> bool {
558 true
559 }
560
561 fn log(&self, record: &log::Record) {
562 fallback_on_error(record, |record| {
563 if cfg!(feature = "meta-logging-in-format") {
564 let msg = format!("{}{}", record.args(), self.line_sep);
569
570 write!(self.stream.lock(), "{}", msg)?;
571 } else {
572 write!(self.stream.lock(), "{}{}", record.args(), self.line_sep)?;
573 }
574
575 Ok(())
576 });
577 }
578
579 fn flush(&self) {
580 let _ = self.stream.lock().flush();
581 }
582 }
583 };
584}
585
586std_log_impl!(Stdout);
587std_log_impl!(Stderr);
588
589macro_rules! writer_log_impl {
590 ($ident:ident) => {
591 impl Log for $ident {
592 fn enabled(&self, _: &log::Metadata) -> bool {
593 true
594 }
595
596 fn log(&self, record: &log::Record) {
597 fallback_on_error(record, |record| {
598 if cfg!(feature = "meta-logging-in-format") {
599 let msg = format!("{}{}", record.args(), self.line_sep);
604
605 let mut writer = self.stream.lock().unwrap_or_else(|e| e.into_inner());
606
607 write!(writer, "{}", msg)?;
608
609 writer.flush()?;
610 } else {
611 let mut writer = self.stream.lock().unwrap_or_else(|e| e.into_inner());
612
613 write!(writer, "{}{}", record.args(), self.line_sep)?;
614
615 writer.flush()?;
616 }
617 Ok(())
618 });
619 }
620
621 fn flush(&self) {
622 let _ = self
623 .stream
624 .lock()
625 .unwrap_or_else(|e| e.into_inner())
626 .flush();
627 }
628 }
629 };
630}
631
632writer_log_impl!(File);
633writer_log_impl!(Writer);
634
635#[cfg(all(not(windows), feature = "reopen-03"))]
636writer_log_impl!(Reopen);
637
638#[cfg(all(not(windows), feature = "reopen-1"))]
639writer_log_impl!(Reopen1);
640
641impl Log for Sender {
642 fn enabled(&self, _: &log::Metadata) -> bool {
643 true
644 }
645
646 fn log(&self, record: &log::Record) {
647 fallback_on_error(record, |record| {
648 let msg = format!("{}{}", record.args(), self.line_sep);
649 self.stream
650 .lock()
651 .unwrap_or_else(|e| e.into_inner())
652 .send(msg)?;
653 Ok(())
654 });
655 }
656
657 fn flush(&self) {}
658}
659
660#[cfg(all(
661 not(windows),
662 any(
663 feature = "syslog-3",
664 feature = "syslog-4",
665 feature = "syslog-6",
666 feature = "syslog-7"
667 )
668))]
669macro_rules! send_syslog {
670 ($logger:expr, $level:expr, $message:expr) => {
671 use log::Level;
672 match $level {
673 Level::Error => $logger.err($message)?,
674 Level::Warn => $logger.warning($message)?,
675 Level::Info => $logger.info($message)?,
676 Level::Debug | Level::Trace => $logger.debug($message)?,
677 }
678 };
679}
680
681#[cfg(all(not(windows), feature = "syslog-3"))]
682impl Log for Syslog3 {
683 fn enabled(&self, _: &log::Metadata) -> bool {
684 true
685 }
686
687 fn log(&self, record: &log::Record) {
688 fallback_on_error(record, |record| {
689 let message = record.args();
690 send_syslog!(self.inner, record.level(), message);
691
692 Ok(())
693 });
694 }
695 fn flush(&self) {}
696}
697
698#[cfg(all(not(windows), feature = "syslog-4"))]
699impl Log for Syslog4Rfc3164 {
700 fn enabled(&self, _: &log::Metadata) -> bool {
701 true
702 }
703
704 fn log(&self, record: &log::Record) {
705 fallback_on_error(record, |record| {
706 let message = record.args().to_string();
707 let mut log = self.inner.lock().unwrap_or_else(|e| e.into_inner());
708 send_syslog!(log, record.level(), message);
709
710 Ok(())
711 });
712 }
713 fn flush(&self) {}
714}
715
716#[cfg(all(not(windows), feature = "syslog-4"))]
717impl Log for Syslog4Rfc5424 {
718 fn enabled(&self, _: &log::Metadata) -> bool {
719 true
720 }
721
722 fn log(&self, record: &log::Record) {
723 fallback_on_error(record, |record| {
724 let transformed = (self.transform)(record);
725 let mut log = self.inner.lock().unwrap_or_else(|e| e.into_inner());
726 send_syslog!(log, record.level(), transformed);
727
728 Ok(())
729 });
730 }
731 fn flush(&self) {}
732}
733
734#[cfg(all(not(windows), feature = "syslog-6"))]
735impl Log for Syslog6Rfc3164 {
736 fn enabled(&self, _: &log::Metadata) -> bool {
737 true
738 }
739
740 fn log(&self, record: &log::Record) {
741 fallback_on_error(record, |record| {
742 let message = record.args().to_string();
743 let mut log = self.inner.lock().unwrap_or_else(|e| e.into_inner());
744 send_syslog!(log, record.level(), message);
745
746 Ok(())
747 });
748 }
749 fn flush(&self) {}
750}
751
752#[cfg(all(not(windows), feature = "syslog-6"))]
753impl Log for Syslog6Rfc5424 {
754 fn enabled(&self, _: &log::Metadata) -> bool {
755 true
756 }
757
758 fn log(&self, record: &log::Record) {
759 fallback_on_error(record, |record| {
760 let transformed = (self.transform)(record);
761 let mut log = self.inner.lock().unwrap_or_else(|e| e.into_inner());
762 send_syslog!(log, record.level(), transformed);
763
764 Ok(())
765 });
766 }
767 fn flush(&self) {}
768}
769
770#[cfg(all(not(windows), feature = "syslog-7"))]
771impl Log for Syslog7Rfc3164 {
772 fn enabled(&self, _: &log::Metadata) -> bool {
773 true
774 }
775
776 fn log(&self, record: &log::Record) {
777 fallback_on_error(record, |record| {
778 let message = record.args().to_string();
779 let mut log = self.inner.lock().unwrap_or_else(|e| e.into_inner());
780 send_syslog!(log, record.level(), message);
781
782 Ok(())
783 });
784 }
785 fn flush(&self) {}
786}
787
788#[cfg(all(not(windows), feature = "syslog-7"))]
789impl Log for Syslog7Rfc5424 {
790 fn enabled(&self, _: &log::Metadata) -> bool {
791 true
792 }
793
794 fn log(&self, record: &log::Record) {
795 fallback_on_error(record, |record| {
796 let transformed = (self.transform)(record);
797 let mut log = self.inner.lock().unwrap_or_else(|e| e.into_inner());
798 send_syslog!(log, record.level(), transformed);
799
800 Ok(())
801 });
802 }
803 fn flush(&self) {}
804}
805
806impl Log for Panic {
807 fn enabled(&self, _: &log::Metadata) -> bool {
808 true
809 }
810
811 fn log(&self, record: &log::Record) {
812 panic!("{}", record.args());
813 }
814
815 fn flush(&self) {}
816}
817
818#[cfg(feature = "date-based")]
819impl Log for DateBased {
820 fn enabled(&self, _: &log::Metadata) -> bool {
821 true
822 }
823
824 fn log(&self, record: &log::Record) {
825 fallback_on_error(record, |record| {
826 #[cfg(feature = "meta-logging-in-format")]
831 let msg = format!("{}{}", record.args(), self.config.line_sep);
832
833 let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
834
835 let new_suffix = self.config.compute_current_suffix();
837 if state.file_stream.is_none() || state.current_suffix != new_suffix {
838 let file_open_result = self.config.open_current_log_file(&new_suffix);
839 match file_open_result {
840 Ok(file) => {
841 state.replace_file(new_suffix, Some(file));
842 }
843 Err(e) => {
844 state.replace_file(new_suffix, None);
845 return Err(e.into());
846 }
847 }
848 }
849
850 let writer = state.file_stream.as_mut().unwrap();
852
853 #[cfg(feature = "meta-logging-in-format")]
854 write!(writer, "{}", msg)?;
855 #[cfg(not(feature = "meta-logging-in-format"))]
856 write!(writer, "{}{}", record.args(), self.config.line_sep)?;
857
858 writer.flush()?;
859
860 Ok(())
861 });
862 }
863
864 fn flush(&self) {
865 let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
866
867 if let Some(stream) = &mut state.file_stream {
868 let _ = stream.flush();
869 }
870 }
871}
872
873#[inline(always)]
874fn fallback_on_error<F>(record: &log::Record, log_func: F)
875where
876 F: FnOnce(&log::Record) -> Result<(), LogError>,
877{
878 if let Err(error) = log_func(record) {
879 backup_logging(record, &error)
880 }
881}
882
883fn backup_logging(record: &log::Record, error: &LogError) {
884 let second = write!(
885 io::stderr(),
886 "Error performing logging.\
887 \n\tattempted to log: {}\
888 \n\trecord: {:?}\
889 \n\tlogging error: {}",
890 record.args(),
891 record,
892 error
893 );
894
895 if let Err(second_error) = second {
896 panic!(
897 "Error performing stderr logging after error occurred during regular logging.\
898 \n\tattempted to log: {}\
899 \n\trecord: {:?}\
900 \n\tfirst logging error: {}\
901 \n\tstderr error: {}",
902 record.args(),
903 record,
904 error,
905 second_error,
906 );
907 }
908}
909
910#[derive(Debug)]
911enum LogError {
912 Io(io::Error),
913 Send(mpsc::SendError<String>),
914 #[cfg(all(not(windows), feature = "syslog-4"))]
915 Syslog4(syslog4::Error),
916 #[cfg(all(not(windows), feature = "syslog-6"))]
917 Syslog6(syslog6::Error),
918 #[cfg(all(not(windows), feature = "syslog-7"))]
919 Syslog7(syslog7::Error),
920}
921
922impl fmt::Display for LogError {
923 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
924 match *self {
925 LogError::Io(ref e) => write!(f, "{}", e),
926 LogError::Send(ref e) => write!(f, "{}", e),
927 #[cfg(all(not(windows), feature = "syslog-4"))]
928 LogError::Syslog4(ref e) => write!(f, "{}", e),
929 #[cfg(all(not(windows), feature = "syslog-6"))]
930 LogError::Syslog6(ref e) => write!(f, "{}", e),
931 #[cfg(all(not(windows), feature = "syslog-7"))]
932 LogError::Syslog7(ref e) => write!(f, "{}", e),
933 }
934 }
935}
936
937impl From<io::Error> for LogError {
938 fn from(error: io::Error) -> Self {
939 LogError::Io(error)
940 }
941}
942
943impl From<mpsc::SendError<String>> for LogError {
944 fn from(error: mpsc::SendError<String>) -> Self {
945 LogError::Send(error)
946 }
947}
948
949#[cfg(all(not(windows), feature = "syslog-4"))]
950impl From<syslog4::Error> for LogError {
951 fn from(error: syslog4::Error) -> Self {
952 LogError::Syslog4(error)
953 }
954}
955
956#[cfg(all(not(windows), feature = "syslog-6"))]
957impl From<syslog6::Error> for LogError {
958 fn from(error: syslog6::Error) -> Self {
959 LogError::Syslog6(error)
960 }
961}
962
963#[cfg(all(not(windows), feature = "syslog-7"))]
964impl From<syslog7::Error> for LogError {
965 fn from(error: syslog7::Error) -> Self {
966 LogError::Syslog7(error)
967 }
968}
969
970#[cfg(test)]
971mod test {
972 use super::LevelConfiguration;
973 use log::LevelFilter::*;
974
975 #[test]
976 fn test_level_config_find_exact_minimal() {
977 let config = LevelConfiguration::Minimal(
978 vec![("mod1", Info), ("mod2", Debug), ("mod3", Off)]
979 .into_iter()
980 .map(|(k, v)| (k.into(), v))
981 .collect(),
982 );
983
984 assert_eq!(config.find_exact("mod1"), Some(Info));
985 assert_eq!(config.find_exact("mod2"), Some(Debug));
986 assert_eq!(config.find_exact("mod3"), Some(Off));
987 }
988
989 #[test]
990 fn test_level_config_find_exact_many() {
991 let config = LevelConfiguration::Many(
992 vec![("mod1", Info), ("mod2", Debug), ("mod3", Off)]
993 .into_iter()
994 .map(|(k, v)| (k.into(), v))
995 .collect(),
996 );
997
998 assert_eq!(config.find_exact("mod1"), Some(Info));
999 assert_eq!(config.find_exact("mod2"), Some(Debug));
1000 assert_eq!(config.find_exact("mod3"), Some(Off));
1001 }
1002
1003 #[test]
1004 fn test_level_config_simple_hierarchy() {
1005 let config = LevelConfiguration::Minimal(
1006 vec![("mod1", Info), ("mod2::sub_mod", Debug), ("mod3", Off)]
1007 .into_iter()
1008 .map(|(k, v)| (k.into(), v))
1009 .collect(),
1010 );
1011
1012 assert_eq!(config.find_module("mod1::sub_mod"), Some(Info));
1013 assert_eq!(config.find_module("mod2::sub_mod::sub_mod_2"), Some(Debug));
1014 assert_eq!(config.find_module("mod3::sub_mod::sub_mod_2"), Some(Off));
1015 }
1016
1017 #[test]
1018 fn test_level_config_hierarchy_correct() {
1019 let config = LevelConfiguration::Minimal(
1020 vec![
1021 ("root", Trace),
1022 ("root::sub1", Debug),
1023 ("root::sub2", Info),
1024 ("root::sub2::sub2.3::sub2.4", Error),
1026 ("root::sub2::sub2.3", Warn),
1027 ("root::sub3", Off),
1028 ]
1029 .into_iter()
1030 .map(|(k, v)| (k.into(), v))
1031 .collect(),
1032 );
1033
1034 assert_eq!(config.find_module("root"), Some(Trace));
1035 assert_eq!(config.find_module("root::other_module"), Some(Trace));
1036
1037 assert_eq!(config.find_module("root::sub1"), Some(Debug));
1040 assert_eq!(config.find_module("root::sub1::other_module"), Some(Debug));
1041
1042 assert_eq!(config.find_module("root::sub2"), Some(Info));
1043 assert_eq!(config.find_module("root::sub2::other"), Some(Info));
1044
1045 assert_eq!(config.find_module("root::sub2::sub2.3"), Some(Warn));
1046 assert_eq!(
1047 config.find_module("root::sub2::sub2.3::sub2.4"),
1048 Some(Error)
1049 );
1050
1051 assert_eq!(config.find_module("root::sub3"), Some(Off));
1052 assert_eq!(
1053 config.find_module("root::sub3::any::children::of::sub3"),
1054 Some(Off)
1055 );
1056 }
1057
1058 #[test]
1059 fn test_level_config_similar_names_are_not_same() {
1060 let config = LevelConfiguration::Minimal(
1061 vec![("root", Trace), ("rootay", Info)]
1062 .into_iter()
1063 .map(|(k, v)| (k.into(), v))
1064 .collect(),
1065 );
1066
1067 assert_eq!(config.find_module("root"), Some(Trace));
1068 assert_eq!(config.find_module("root::sub"), Some(Trace));
1069 assert_eq!(config.find_module("rooty"), None);
1070 assert_eq!(config.find_module("rooty::sub"), None);
1071 assert_eq!(config.find_module("rootay"), Some(Info));
1072 assert_eq!(config.find_module("rootay::sub"), Some(Info));
1073 }
1074
1075 #[test]
1076 fn test_level_config_single_colon_is_not_double_colon() {
1077 let config = LevelConfiguration::Minimal(
1078 vec![
1079 ("root", Trace),
1080 ("root::su", Debug),
1081 ("root::su:b2", Info),
1082 ("root::sub2", Warn),
1083 ]
1084 .into_iter()
1085 .map(|(k, v)| (k.into(), v))
1086 .collect(),
1087 );
1088
1089 assert_eq!(config.find_module("root"), Some(Trace));
1090
1091 assert_eq!(config.find_module("root::su"), Some(Debug));
1092 assert_eq!(config.find_module("root::su::b2"), Some(Debug));
1093
1094 assert_eq!(config.find_module("root::su:b2"), Some(Info));
1095 assert_eq!(config.find_module("root::su:b2::b3"), Some(Info));
1096
1097 assert_eq!(config.find_module("root::sub2"), Some(Warn));
1098 assert_eq!(config.find_module("root::sub2::b3"), Some(Warn));
1099 }
1100
1101 #[test]
1102 fn test_level_config_all_chars() {
1103 let config = LevelConfiguration::Minimal(
1104 vec![("♲", Trace), ("☸", Debug), ("♲::☸", Info), ("♲::\t", Debug)]
1105 .into_iter()
1106 .map(|(k, v)| (k.into(), v))
1107 .collect(),
1108 );
1109
1110 assert_eq!(config.find_module("♲"), Some(Trace));
1111 assert_eq!(config.find_module("♲::other"), Some(Trace));
1112
1113 assert_eq!(config.find_module("☸"), Some(Debug));
1114 assert_eq!(config.find_module("☸::any"), Some(Debug));
1115
1116 assert_eq!(config.find_module("♲::☸"), Some(Info));
1117 assert_eq!(config.find_module("♲☸"), None);
1118
1119 assert_eq!(config.find_module("♲::\t"), Some(Debug));
1120 assert_eq!(config.find_module("♲::\t::\n\n::\t"), Some(Debug));
1121 assert_eq!(config.find_module("♲::\t\t"), Some(Trace));
1122 }
1123}