fern/
builders.rs

1use std::{
2    borrow::Cow,
3    cmp, fmt, fs, io,
4    io::Write,
5    sync::{mpsc::Sender, Arc, Mutex},
6};
7
8#[cfg(feature = "date-based")]
9use std::path::{Path, PathBuf};
10
11#[cfg(all(not(windows), any(feature = "syslog-4", feature = "syslog-6")))]
12use std::collections::HashMap;
13
14#[cfg(all(not(windows), feature = "syslog-7"))]
15use std::collections::BTreeMap;
16
17use log::Log;
18
19use crate::{log_impl, Filter, FormatCallback, Formatter};
20
21#[cfg(feature = "date-based")]
22use crate::log_impl::DateBasedState;
23
24#[cfg(all(not(windows), feature = "syslog-4"))]
25use crate::{Syslog4Rfc3164Logger, Syslog4Rfc5424Logger, Syslog4TransformFn};
26
27#[cfg(all(not(windows), feature = "syslog-6"))]
28use crate::{Syslog6Rfc3164Logger, Syslog6Rfc5424Logger, Syslog6TransformFn};
29
30#[cfg(all(not(windows), feature = "syslog-7"))]
31use crate::{Syslog7Rfc3164Logger, Syslog7Rfc5424Logger, Syslog7TransformFn};
32
33/// The base dispatch logger.
34///
35/// This allows for formatting log records, limiting what records can be passed
36/// through, and then dispatching records to other dispatch loggers or output
37/// loggers.
38///
39/// Note that all methods are position-insensitive.
40/// `Dispatch::new().format(a).chain(b)` produces the exact same result
41/// as `Dispatch::new().chain(b).format(a)`. Given this, it is preferred to put
42/// 'format' and other modifiers before 'chain' for the sake of clarity.
43///
44/// Example usage demonstrating all features:
45///
46/// ```no_run
47/// # // no_run because this creates log files.
48/// use std::{fs, io};
49///
50/// # fn setup_logger() -> Result<(), fern::InitError> {
51/// fern::Dispatch::new()
52///     .format(|out, message, record| {
53///         out.finish(format_args!(
54///             "[{} {}] {}",
55///             record.level(),
56///             record.target(),
57///             message,
58///         ))
59///     })
60///     .chain(
61///         fern::Dispatch::new()
62///             // by default only accept warn messages
63///             .level(log::LevelFilter::Warn)
64///             // accept info messages from the current crate too
65///             .level_for("my_crate", log::LevelFilter::Info)
66///             // `io::Stdout`, `io::Stderr` and `io::File` can be directly passed in.
67///             .chain(io::stdout()),
68///     )
69///     .chain(
70///         fern::Dispatch::new()
71///             // output all messages
72///             .level(log::LevelFilter::Trace)
73///             // except for hyper, in that case only show info messages
74///             .level_for("hyper", log::LevelFilter::Info)
75///             // `log_file(x)` equates to
76///             // `OpenOptions::new().write(true).append(true).create(true).open(x)`
77///             .chain(fern::log_file("persistent-log.log")?)
78///             .chain(
79///                 fs::OpenOptions::new()
80///                     .write(true)
81///                     .create(true)
82///                     .truncate(true)
83///                     .create(true)
84///                     .open("/tmp/temp.log")?,
85///             ),
86///     )
87///     .chain(
88///         fern::Dispatch::new()
89///             .level(log::LevelFilter::Error)
90///             .filter(|_meta_data| {
91///                 // as an example, randomly reject half of the messages
92///                 # /*
93///                 rand::random()
94///                 # */
95///                 # true
96///             })
97///             .chain(io::stderr()),
98///     )
99///     // and finally, set as the global logger!
100///     .apply()?;
101/// # Ok(())
102/// # }
103/// #
104/// # fn main() { setup_logger().expect("failed to set up logger") }
105/// ```
106#[must_use = "this is only a logger configuration and must be consumed with into_log() or apply()"]
107pub struct Dispatch {
108    format: Option<Box<Formatter>>,
109    children: Vec<OutputInner>,
110    default_level: log::LevelFilter,
111    levels: Vec<(Cow<'static, str>, log::LevelFilter)>,
112    filters: Vec<Box<Filter>>,
113}
114
115/// Logger which is usable as an output for multiple other loggers.
116///
117/// This struct contains a built logger stored in an [`Arc`], and can be
118/// safely cloned.
119///
120/// See [`Dispatch::into_shared`].
121///
122/// [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
123/// [`Dispatch::into_shared`]: struct.Dispatch.html#method.into_shared
124#[derive(Clone)]
125pub struct SharedDispatch {
126    inner: Arc<log_impl::Dispatch>,
127    min_level: log::LevelFilter,
128}
129
130impl Dispatch {
131    /// Creates a dispatch, which will initially do nothing.
132    #[inline]
133    pub fn new() -> Self {
134        Dispatch {
135            format: None,
136            children: Vec::new(),
137            default_level: log::LevelFilter::Trace,
138            levels: Vec::new(),
139            filters: Vec::new(),
140        }
141    }
142
143    /// Sets the formatter of this dispatch. The closure should accept a
144    /// callback, a message and a log record, and write the resulting
145    /// format to the writer.
146    ///
147    /// The log record is passed for completeness, but the `args()` method of
148    /// the record should be ignored, and the [`fmt::Arguments`] given
149    /// should be used instead. `record.args()` may be used to retrieve the
150    /// _original_ log message, but in order to allow for true log
151    /// chaining, formatters should use the given message instead whenever
152    /// including the message in the output.
153    ///
154    /// To avoid all allocation of intermediate results, the formatter is
155    /// "completed" by calling a callback, which then calls the rest of the
156    /// logging chain with the new formatted message. The callback object keeps
157    /// track of if it was called or not via a stack boolean as well, so if
158    /// you don't use `out.finish` the log message will continue down
159    /// the logger chain unformatted.
160    ///
161    /// [`fmt::Arguments`]: https://doc.rust-lang.org/std/fmt/struct.Arguments.html
162    ///
163    /// Example usage:
164    ///
165    /// ```
166    /// fern::Dispatch::new().format(|out, message, record| {
167    ///     out.finish(format_args!(
168    ///         "[{} {}] {}",
169    ///         record.level(),
170    ///         record.target(),
171    ///         message
172    ///     ))
173    /// })
174    ///     # .into_log();
175    /// ```
176    #[inline]
177    pub fn format<F>(mut self, formatter: F) -> Self
178    where
179        F: Fn(FormatCallback, &fmt::Arguments, &log::Record) + Sync + Send + 'static,
180    {
181        self.format = Some(Box::new(formatter));
182        self
183    }
184
185    /// Adds a child to this dispatch.
186    ///
187    /// All log records which pass all filters will be formatted and then sent
188    /// to all child loggers in sequence.
189    ///
190    /// Note: If the child logger is also a Dispatch, and cannot accept any log
191    /// records, it will be dropped. This only happens if the child either
192    /// has no children itself, or has a minimum log level of
193    /// [`LevelFilter::Off`].
194    ///
195    /// [`LevelFilter::Off`]: https://docs.rs/log/0.4/log/enum.LevelFilter.html#variant.Off
196    ///
197    /// Example usage:
198    ///
199    /// ```
200    /// fern::Dispatch::new().chain(fern::Dispatch::new().chain(std::io::stdout()))
201    ///     # .into_log();
202    /// ```
203    #[inline]
204    pub fn chain<T: Into<Output>>(mut self, logger: T) -> Self {
205        self.children.push(logger.into().0);
206        self
207    }
208
209    /// Sets the overarching level filter for this logger. All messages not
210    /// already filtered by something set by [`Dispatch::level_for`] will
211    /// be affected.
212    ///
213    /// All messages filtered will be discarded if less severe than the given
214    /// level.
215    ///
216    /// Default level is [`LevelFilter::Trace`].
217    ///
218    /// [`Dispatch::level_for`]: #method.level_for
219    /// [`LevelFilter::Trace`]: https://docs.rs/log/0.4/log/enum.LevelFilter.html#variant.Trace
220    ///
221    /// Example usage:
222    ///
223    /// ```
224    /// # fn main() {
225    /// fern::Dispatch::new().level(log::LevelFilter::Info)
226    ///     # .into_log();
227    /// # }
228    /// ```
229    #[inline]
230    pub fn level(mut self, level: log::LevelFilter) -> Self {
231        self.default_level = level;
232        self
233    }
234
235    /// Sets a per-target log level filter. Default target for log messages is
236    /// `crate_name::module_name` or
237    /// `crate_name` for logs in the crate root. Targets can also be set with
238    /// `info!(target: "target-name", ...)`.
239    ///
240    /// For each log record fern will first try to match the most specific
241    /// level_for, and then progressively more general ones until either a
242    /// matching level is found, or the default level is used.
243    ///
244    /// For example, a log for the target `hyper::http::h1` will first test a
245    /// level_for for `hyper::http::h1`, then for `hyper::http`, then for
246    /// `hyper`, then use the default level.
247    ///
248    /// Examples:
249    ///
250    /// A program wants to include a lot of debugging output, but the library
251    /// "hyper" is known to work well, so debug output from it should be
252    /// excluded:
253    ///
254    /// ```
255    /// # fn main() {
256    /// fern::Dispatch::new()
257    ///     .level(log::LevelFilter::Trace)
258    ///     .level_for("hyper", log::LevelFilter::Info)
259    ///     # .into_log();
260    /// # }
261    /// ```
262    ///
263    /// A program has a ton of debug output per-module, but there is so much
264    /// that debugging more than one module at a time is not very useful.
265    /// The command line accepts a list of modules to debug, while keeping the
266    /// rest of the program at info level:
267    ///
268    /// ```
269    /// fn setup_logging<T, I>(verbose_modules: T) -> Result<(), fern::InitError>
270    /// where
271    ///     I: AsRef<str>,
272    ///     T: IntoIterator<Item = I>,
273    /// {
274    ///     let mut config = fern::Dispatch::new().level(log::LevelFilter::Info);
275    ///
276    ///     for module_name in verbose_modules {
277    ///         config = config.level_for(
278    ///             format!("my_crate_name::{}", module_name.as_ref()),
279    ///             log::LevelFilter::Debug,
280    ///         );
281    ///     }
282    ///
283    ///     config.chain(std::io::stdout()).apply()?;
284    ///
285    ///     Ok(())
286    /// }
287    /// #
288    /// # // we're ok with apply() failing.
289    /// # fn main() { let _ = setup_logging(&["hi"]); }
290    /// ```
291    #[inline]
292    pub fn level_for<T: Into<Cow<'static, str>>>(
293        mut self,
294        module: T,
295        level: log::LevelFilter,
296    ) -> Self {
297        let module = module.into();
298
299        if let Some((index, _)) = self
300            .levels
301            .iter()
302            .enumerate()
303            .find(|(_, (name, _))| *name == module)
304        {
305            self.levels.remove(index);
306        }
307
308        self.levels.push((module, level));
309        self
310    }
311
312    /// Adds a custom filter which can reject messages passing through this
313    /// logger.
314    ///
315    /// The logger will continue to process log records only if all filters
316    /// return `true`.
317    ///
318    /// [`Dispatch::level`] and [`Dispatch::level_for`] are preferred if
319    /// applicable.
320    ///
321    /// [`Dispatch::level`]: #method.level
322    /// [`Dispatch::level_for`]: #method.level_for
323    ///
324    /// Example usage:
325    ///
326    /// This sends error level messages to stderr and others to stdout.
327    ///
328    /// ```
329    /// # fn main() {
330    /// fern::Dispatch::new()
331    ///     .level(log::LevelFilter::Info)
332    ///     .chain(
333    ///         fern::Dispatch::new()
334    ///             .filter(|metadata| {
335    ///                 // Reject messages with the `Error` log level.
336    ///                 metadata.level() != log::LevelFilter::Error
337    ///             })
338    ///             .chain(std::io::stderr()),
339    ///     )
340    ///     .chain(
341    ///         fern::Dispatch::new()
342    ///             .level(log::LevelFilter::Error)
343    ///             .chain(std::io::stdout()),
344    ///     )
345    ///     # .into_log();
346    /// # }
347    #[inline]
348    pub fn filter<F>(mut self, filter: F) -> Self
349    where
350        F: Fn(&log::Metadata) -> bool + Send + Sync + 'static,
351    {
352        self.filters.push(Box::new(filter));
353        self
354    }
355
356    /// Builds this dispatch and stores it in a clonable structure containing
357    /// an [`Arc`].
358    ///
359    /// Once "shared", the dispatch can be used as an output for multiple other
360    /// dispatch loggers.
361    ///
362    /// Example usage:
363    ///
364    /// This separates info and warn messages, sending info to stdout + a log
365    /// file, and warn to stderr + the same log file. Shared is used so the
366    /// program only opens "file.log" once.
367    ///
368    /// ```no_run
369    /// # fn setup_logger() -> Result<(), fern::InitError> {
370    ///
371    /// let file_out = fern::Dispatch::new()
372    ///     .chain(fern::log_file("file.log")?)
373    ///     .into_shared();
374    ///
375    /// let info_out = fern::Dispatch::new()
376    ///     .level(log::LevelFilter::Debug)
377    ///     .filter(|metadata|
378    ///         // keep only info and debug (reject warn and error)
379    ///         metadata.level() <= log::Level::Info)
380    ///     .chain(std::io::stdout())
381    ///     .chain(file_out.clone());
382    ///
383    /// let warn_out = fern::Dispatch::new()
384    ///     .level(log::LevelFilter::Warn)
385    ///     .chain(std::io::stderr())
386    ///     .chain(file_out);
387    ///
388    /// fern::Dispatch::new()
389    ///     .chain(info_out)
390    ///     .chain(warn_out)
391    ///     .apply();
392    ///
393    /// # Ok(())
394    /// # }
395    /// #
396    /// # fn main() { setup_logger().expect("failed to set up logger"); }
397    /// ```
398    ///
399    /// [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
400    pub fn into_shared(self) -> SharedDispatch {
401        let (min_level, dispatch) = self.into_dispatch();
402
403        SharedDispatch {
404            inner: Arc::new(dispatch),
405            min_level,
406        }
407    }
408
409    /// Builds this into the actual logger implementation.
410    ///
411    /// This could probably be refactored, but having everything in one place
412    /// is also nice.
413    fn into_dispatch(self) -> (log::LevelFilter, log_impl::Dispatch) {
414        let Dispatch {
415            format,
416            children,
417            default_level,
418            levels,
419            mut filters,
420        } = self;
421
422        let mut max_child_level = log::LevelFilter::Off;
423
424        let output = children
425            .into_iter()
426            .flat_map(|child| match child {
427                OutputInner::Stdout { stream, line_sep } => {
428                    max_child_level = log::LevelFilter::Trace;
429                    Some(log_impl::Output::Stdout(log_impl::Stdout {
430                        stream,
431                        line_sep,
432                    }))
433                }
434                OutputInner::Stderr { stream, line_sep } => {
435                    max_child_level = log::LevelFilter::Trace;
436                    Some(log_impl::Output::Stderr(log_impl::Stderr {
437                        stream,
438                        line_sep,
439                    }))
440                }
441                OutputInner::File { stream, line_sep } => {
442                    max_child_level = log::LevelFilter::Trace;
443                    Some(log_impl::Output::File(log_impl::File {
444                        stream: Mutex::new(io::BufWriter::new(stream)),
445                        line_sep,
446                    }))
447                }
448                OutputInner::Writer { stream, line_sep } => {
449                    max_child_level = log::LevelFilter::Trace;
450                    Some(log_impl::Output::Writer(log_impl::Writer {
451                        stream: Mutex::new(stream),
452                        line_sep,
453                    }))
454                }
455                #[cfg(all(not(windows), feature = "reopen-03"))]
456                OutputInner::Reopen { stream, line_sep } => {
457                    max_child_level = log::LevelFilter::Trace;
458                    Some(log_impl::Output::Reopen(log_impl::Reopen {
459                        stream: Mutex::new(stream),
460                        line_sep,
461                    }))
462                }
463                #[cfg(all(not(windows), feature = "reopen-1"))]
464                OutputInner::Reopen1 { stream, line_sep } => {
465                    max_child_level = log::LevelFilter::Trace;
466                    Some(log_impl::Output::Reopen1(log_impl::Reopen1 {
467                        stream: Mutex::new(stream),
468                        line_sep,
469                    }))
470                }
471                OutputInner::Sender { stream, line_sep } => {
472                    max_child_level = log::LevelFilter::Trace;
473                    Some(log_impl::Output::Sender(log_impl::Sender {
474                        stream: Mutex::new(stream),
475                        line_sep,
476                    }))
477                }
478                #[cfg(all(not(windows), feature = "syslog-3"))]
479                OutputInner::Syslog3(log) => {
480                    max_child_level = log::LevelFilter::Trace;
481                    Some(log_impl::Output::Syslog3(log_impl::Syslog3 { inner: log }))
482                }
483                #[cfg(all(not(windows), feature = "syslog-4"))]
484                OutputInner::Syslog4Rfc3164(logger) => {
485                    max_child_level = log::LevelFilter::Trace;
486                    Some(log_impl::Output::Syslog4Rfc3164(log_impl::Syslog4Rfc3164 {
487                        inner: Mutex::new(logger),
488                    }))
489                }
490                #[cfg(all(not(windows), feature = "syslog-4"))]
491                OutputInner::Syslog4Rfc5424 { logger, transform } => {
492                    max_child_level = log::LevelFilter::Trace;
493                    Some(log_impl::Output::Syslog4Rfc5424(log_impl::Syslog4Rfc5424 {
494                        inner: Mutex::new(logger),
495                        transform,
496                    }))
497                }
498                #[cfg(all(not(windows), feature = "syslog-6"))]
499                OutputInner::Syslog6Rfc3164(logger) => {
500                    max_child_level = log::LevelFilter::Trace;
501                    Some(log_impl::Output::Syslog6Rfc3164(log_impl::Syslog6Rfc3164 {
502                        inner: Mutex::new(logger),
503                    }))
504                }
505                #[cfg(all(not(windows), feature = "syslog-6"))]
506                OutputInner::Syslog6Rfc5424 { logger, transform } => {
507                    max_child_level = log::LevelFilter::Trace;
508                    Some(log_impl::Output::Syslog6Rfc5424(log_impl::Syslog6Rfc5424 {
509                        inner: Mutex::new(logger),
510                        transform,
511                    }))
512                }
513                #[cfg(all(not(windows), feature = "syslog-7"))]
514                OutputInner::Syslog7Rfc3164(logger) => {
515                    max_child_level = log::LevelFilter::Trace;
516                    Some(log_impl::Output::Syslog7Rfc3164(log_impl::Syslog7Rfc3164 {
517                        inner: Mutex::new(logger),
518                    }))
519                }
520                #[cfg(all(not(windows), feature = "syslog-7"))]
521                OutputInner::Syslog7Rfc5424 { logger, transform } => {
522                    max_child_level = log::LevelFilter::Trace;
523                    Some(log_impl::Output::Syslog7Rfc5424(log_impl::Syslog7Rfc5424 {
524                        inner: Mutex::new(logger),
525                        transform,
526                    }))
527                }
528                OutputInner::Panic => {
529                    max_child_level = log::LevelFilter::Trace;
530                    Some(log_impl::Output::Panic(log_impl::Panic))
531                }
532                OutputInner::Dispatch(child_dispatch) => {
533                    let (child_level, child) = child_dispatch.into_dispatch();
534                    if child_level > log::LevelFilter::Off {
535                        max_child_level = cmp::max(max_child_level, child_level);
536                        Some(log_impl::Output::Dispatch(child))
537                    } else {
538                        None
539                    }
540                }
541                OutputInner::SharedDispatch(child_dispatch) => {
542                    let SharedDispatch {
543                        inner: child,
544                        min_level: child_level,
545                    } = child_dispatch;
546
547                    if child_level > log::LevelFilter::Off {
548                        max_child_level = cmp::max(max_child_level, child_level);
549                        Some(log_impl::Output::SharedDispatch(child))
550                    } else {
551                        None
552                    }
553                }
554                OutputInner::OtherBoxed(child_log) => {
555                    max_child_level = log::LevelFilter::Trace;
556                    Some(log_impl::Output::OtherBoxed(child_log))
557                }
558                OutputInner::OtherStatic(child_log) => {
559                    max_child_level = log::LevelFilter::Trace;
560                    Some(log_impl::Output::OtherStatic(child_log))
561                }
562                #[cfg(feature = "date-based")]
563                OutputInner::DateBased { config } => {
564                    max_child_level = log::LevelFilter::Trace;
565
566                    let config = log_impl::DateBasedConfig::new(
567                        config.line_sep,
568                        config.file_prefix,
569                        config.file_suffix,
570                        if config.utc_time {
571                            log_impl::ConfiguredTimezone::Utc
572                        } else {
573                            log_impl::ConfiguredTimezone::Local
574                        },
575                    );
576
577                    let computed_suffix = config.compute_current_suffix();
578
579                    // ignore errors - we'll just retry later.
580                    let initial_file = config.open_current_log_file(&computed_suffix).ok();
581
582                    Some(log_impl::Output::DateBased(log_impl::DateBased {
583                        config,
584                        state: Mutex::new(DateBasedState::new(computed_suffix, initial_file)),
585                    }))
586                }
587            })
588            .collect();
589
590        let min_level = levels
591            .iter()
592            .map(|t| t.1)
593            .max()
594            .map_or(default_level, |lvl| cmp::max(lvl, default_level));
595        let real_min = cmp::min(min_level, max_child_level);
596
597        filters.shrink_to_fit();
598
599        let dispatch = log_impl::Dispatch {
600            output,
601            default_level,
602            levels: levels.into(),
603            format,
604            filters,
605        };
606
607        (real_min, dispatch)
608    }
609
610    /// Builds this logger into a `Box<log::Log>` and calculates the minimum
611    /// log level needed to have any effect.
612    ///
613    /// While this method is exposed publicly, [`Dispatch::apply`] is typically
614    /// used instead.
615    ///
616    /// The returned LevelFilter is a calculation for all level filters of this
617    /// logger and child loggers, and is the minimum log level needed to
618    /// for a record to have any chance of passing through this logger.
619    ///
620    /// [`Dispatch::apply`]: #method.apply
621    ///
622    /// Example usage:
623    ///
624    /// ```
625    /// # fn main() {
626    /// let (min_level, log) = fern::Dispatch::new()
627    ///     .level(log::LevelFilter::Info)
628    ///     .chain(std::io::stdout())
629    ///     .into_log();
630    ///
631    /// assert_eq!(min_level, log::LevelFilter::Info);
632    /// # }
633    /// ```
634    pub fn into_log(self) -> (log::LevelFilter, Box<dyn log::Log>) {
635        let (level, logger) = self.into_dispatch();
636        if level == log::LevelFilter::Off {
637            (level, Box::new(log_impl::Null))
638        } else {
639            (level, Box::new(logger))
640        }
641    }
642
643    /// Builds this logger and instantiates it as the global [`log`] logger.
644    ///
645    /// # Errors:
646    ///
647    /// This function will return an error if a global logger has already been
648    /// set to a previous logger.
649    ///
650    /// [`log`]: https://github.com/rust-lang-nursery/log
651    pub fn apply(self) -> Result<(), log::SetLoggerError> {
652        let (max_level, log) = self.into_log();
653
654        log::set_boxed_logger(log)?;
655        log::set_max_level(max_level);
656
657        Ok(())
658    }
659}
660
661/// This enum contains various outputs that you can send messages to.
662enum OutputInner {
663    /// Prints all messages to stdout with `line_sep` separator.
664    Stdout {
665        stream: io::Stdout,
666        line_sep: Cow<'static, str>,
667    },
668    /// Prints all messages to stderr with `line_sep` separator.
669    Stderr {
670        stream: io::Stderr,
671        line_sep: Cow<'static, str>,
672    },
673    /// Writes all messages to file with `line_sep` separator.
674    File {
675        stream: fs::File,
676        line_sep: Cow<'static, str>,
677    },
678    /// Writes all messages to the writer with `line_sep` separator.
679    Writer {
680        stream: Box<dyn Write + Send>,
681        line_sep: Cow<'static, str>,
682    },
683    /// Writes all messages to the reopen::Reopen file with `line_sep`
684    /// separator.
685    #[cfg(all(not(windows), feature = "reopen-03"))]
686    Reopen {
687        stream: reopen03::Reopen<fs::File>,
688        line_sep: Cow<'static, str>,
689    },
690    /// Writes all messages to the reopen::Reopen file with `line_sep`
691    /// separator.
692    #[cfg(all(not(windows), feature = "reopen-1"))]
693    Reopen1 {
694        stream: reopen1::Reopen<fs::File>,
695        line_sep: Cow<'static, str>,
696    },
697    /// Writes all messages to mpst::Sender with `line_sep` separator.
698    Sender {
699        stream: Sender<String>,
700        line_sep: Cow<'static, str>,
701    },
702    /// Passes all messages to other dispatch.
703    Dispatch(Dispatch),
704    /// Passes all messages to other dispatch that's shared.
705    SharedDispatch(SharedDispatch),
706    /// Passes all messages to other logger.
707    OtherBoxed(Box<dyn Log>),
708    /// Passes all messages to other logger.
709    OtherStatic(&'static dyn Log),
710    /// Passes all messages to the syslog.
711    #[cfg(all(not(windows), feature = "syslog-3"))]
712    Syslog3(syslog3::Logger),
713    /// Passes all messages to the syslog.
714    #[cfg(all(not(windows), feature = "syslog-4"))]
715    Syslog4Rfc3164(Syslog4Rfc3164Logger),
716    /// Sends all messages through the transform then passes to the syslog.
717    #[cfg(all(not(windows), feature = "syslog-4"))]
718    Syslog4Rfc5424 {
719        logger: Syslog4Rfc5424Logger,
720        transform: Box<Syslog4TransformFn>,
721    },
722    #[cfg(all(not(windows), feature = "syslog-6"))]
723    Syslog6Rfc3164(Syslog6Rfc3164Logger),
724    /// Sends all messages through the transform then passes to the syslog.
725    #[cfg(all(not(windows), feature = "syslog-6"))]
726    Syslog6Rfc5424 {
727        logger: Syslog6Rfc5424Logger,
728        transform: Box<Syslog6TransformFn>,
729    },
730    #[cfg(all(not(windows), feature = "syslog-7"))]
731    Syslog7Rfc3164(Syslog7Rfc3164Logger),
732    /// Sends all messages through the transform then passes to the syslog.
733    #[cfg(all(not(windows), feature = "syslog-7"))]
734    Syslog7Rfc5424 {
735        logger: Syslog7Rfc5424Logger,
736        transform: Box<Syslog7TransformFn>,
737    },
738    /// Panics with messages text for all messages.
739    Panic,
740    /// File logger with custom date and timestamp suffix in file name.
741    #[cfg(feature = "date-based")]
742    DateBased { config: DateBased },
743}
744
745/// Logger which will panic whenever anything is logged. The panic
746/// will be exactly the message of the log.
747///
748/// `Panic` is useful primarily as a secondary logger, filtered by warning or
749/// error.
750///
751/// # Examples
752///
753/// This configuration will output all messages to stdout and panic if an Error
754/// message is sent.
755///
756/// ```
757/// fern::Dispatch::new()
758///     // format, etc.
759///     .chain(std::io::stdout())
760///     .chain(
761///         fern::Dispatch::new()
762///             .level(log::LevelFilter::Error)
763///             .chain(fern::Panic),
764///     )
765///     # /*
766///     .apply()?;
767///     # */ .into_log();
768/// ```
769///
770/// This sets up a "panic on warn+" logger, and ignores errors so it can be
771/// called multiple times.
772///
773/// This might be useful in test setup, for example, to disallow warn-level
774/// messages.
775///
776/// ```no_run
777/// fn setup_panic_logging() {
778///     fern::Dispatch::new()
779///         .level(log::LevelFilter::Warn)
780///         .chain(fern::Panic)
781///         .apply()
782///         // ignore errors from setting up logging twice
783///         .ok();
784/// }
785/// ```
786pub struct Panic;
787
788/// Configuration for a logger output.
789pub struct Output(OutputInner);
790
791impl From<Dispatch> for Output {
792    /// Creates an output logger forwarding all messages to the dispatch.
793    fn from(log: Dispatch) -> Self {
794        Output(OutputInner::Dispatch(log))
795    }
796}
797
798impl From<SharedDispatch> for Output {
799    /// Creates an output logger forwarding all messages to the dispatch.
800    fn from(log: SharedDispatch) -> Self {
801        Output(OutputInner::SharedDispatch(log))
802    }
803}
804
805impl From<Box<dyn Log>> for Output {
806    /// Creates an output logger forwarding all messages to the custom logger.
807    fn from(log: Box<dyn Log>) -> Self {
808        Output(OutputInner::OtherBoxed(log))
809    }
810}
811
812impl From<&'static dyn Log> for Output {
813    /// Creates an output logger forwarding all messages to the custom logger.
814    fn from(log: &'static dyn Log) -> Self {
815        Output(OutputInner::OtherStatic(log))
816    }
817}
818
819impl From<fs::File> for Output {
820    /// Creates an output logger which writes all messages to the file with
821    /// `\n` as the separator.
822    ///
823    /// File writes are buffered and flushed once per log record.
824    fn from(file: fs::File) -> Self {
825        Output(OutputInner::File {
826            stream: file,
827            line_sep: "\n".into(),
828        })
829    }
830}
831
832impl From<Box<dyn Write + Send>> for Output {
833    /// Creates an output logger which writes all messages to the writer with
834    /// `\n` as the separator.
835    ///
836    /// This does no buffering and it is up to the writer to do buffering as
837    /// needed (eg. wrap it in `BufWriter`). However, flush is called after
838    /// each log record.
839    fn from(writer: Box<dyn Write + Send>) -> Self {
840        Output(OutputInner::Writer {
841            stream: writer,
842            line_sep: "\n".into(),
843        })
844    }
845}
846
847#[cfg(all(not(windows), feature = "reopen-03"))]
848impl From<reopen03::Reopen<fs::File>> for Output {
849    /// Creates an output logger which writes all messages to the file contained
850    /// in the Reopen struct, using `\n` as the separator.
851    fn from(reopen: reopen03::Reopen<fs::File>) -> Self {
852        Output(OutputInner::Reopen {
853            stream: reopen,
854            line_sep: "\n".into(),
855        })
856    }
857}
858
859#[cfg(all(not(windows), feature = "reopen-1"))]
860impl From<reopen1::Reopen<fs::File>> for Output {
861    /// Creates an output logger which writes all messages to the file contained
862    /// in the Reopen struct, using `\n` as the separator.
863    fn from(reopen: reopen1::Reopen<fs::File>) -> Self {
864        Output(OutputInner::Reopen1 {
865            stream: reopen,
866            line_sep: "\n".into(),
867        })
868    }
869}
870
871impl From<io::Stdout> for Output {
872    /// Creates an output logger which writes all messages to stdout with the
873    /// given handle and `\n` as the separator.
874    fn from(stream: io::Stdout) -> Self {
875        Output(OutputInner::Stdout {
876            stream,
877            line_sep: "\n".into(),
878        })
879    }
880}
881
882impl From<io::Stderr> for Output {
883    /// Creates an output logger which writes all messages to stderr with the
884    /// given handle and `\n` as the separator.
885    fn from(stream: io::Stderr) -> Self {
886        Output(OutputInner::Stderr {
887            stream,
888            line_sep: "\n".into(),
889        })
890    }
891}
892
893impl From<Sender<String>> for Output {
894    /// Creates an output logger which writes all messages to the given
895    /// mpsc::Sender with  '\n' as the separator.
896    ///
897    /// All messages sent to the mpsc channel are suffixed with '\n'.
898    fn from(stream: Sender<String>) -> Self {
899        Output(OutputInner::Sender {
900            stream,
901            line_sep: "\n".into(),
902        })
903    }
904}
905
906#[cfg(all(not(windows), feature = "syslog-3"))]
907impl From<syslog3::Logger> for Output {
908    /// Creates an output logger which writes all messages to the given syslog
909    /// output.
910    ///
911    /// Log levels are translated trace => debug, debug => debug, info =>
912    /// informational, warn => warning, and error => error.
913    ///
914    /// This requires the `"syslog-3"` feature.
915    fn from(log: syslog3::Logger) -> Self {
916        Output(OutputInner::Syslog3(log))
917    }
918}
919
920#[cfg(all(not(windows), feature = "syslog-3"))]
921impl From<Box<syslog3::Logger>> for Output {
922    /// Creates an output logger which writes all messages to the given syslog
923    /// output.
924    ///
925    /// Log levels are translated trace => debug, debug => debug, info =>
926    /// informational, warn => warning, and error => error.
927    ///
928    /// Note that while this takes a `Box<Logger>` for convenience (syslog
929    /// methods return `Box`es), it will be immediately unboxed upon storage
930    /// in the configuration structure. This will create a configuration
931    /// identical to that created by passing a raw `syslog::Logger`.
932    ///
933    /// This requires the `"syslog-3"` feature.
934    fn from(log: Box<syslog3::Logger>) -> Self {
935        Output(OutputInner::Syslog3(*log))
936    }
937}
938
939#[cfg(all(not(windows), feature = "syslog-4"))]
940impl From<Syslog4Rfc3164Logger> for Output {
941    /// Creates an output logger which writes all messages to the given syslog.
942    ///
943    /// Log levels are translated trace => debug, debug => debug, info =>
944    /// informational, warn => warning, and error => error.
945    ///
946    /// Note that due to <https://github.com/Geal/rust-syslog/issues/41>,
947    /// logging to this backend requires one allocation per log call.
948    ///
949    /// This is for RFC 3164 loggers. To use an RFC 5424 logger, use the
950    /// [`Output::syslog_5424`] helper method.
951    ///
952    /// This requires the `"syslog-4"` feature.
953    fn from(log: Syslog4Rfc3164Logger) -> Self {
954        Output(OutputInner::Syslog4Rfc3164(log))
955    }
956}
957
958#[cfg(all(not(windows), feature = "syslog-6"))]
959impl From<Syslog6Rfc3164Logger> for Output {
960    /// Creates an output logger which writes all messages to the given syslog.
961    ///
962    /// Log levels are translated trace => debug, debug => debug, info =>
963    /// informational, warn => warning, and error => error.
964    ///
965    /// Note that due to <https://github.com/Geal/rust-syslog/issues/41>,
966    /// logging to this backend requires one allocation per log call.
967    ///
968    /// This is for RFC 3164 loggers. To use an RFC 5424 logger, use the
969    /// [`Output::syslog_5424`] helper method.
970    ///
971    /// This requires the `"syslog-6"` feature.
972    fn from(log: Syslog6Rfc3164Logger) -> Self {
973        Output(OutputInner::Syslog6Rfc3164(log))
974    }
975}
976
977#[cfg(all(not(windows), feature = "syslog-7"))]
978impl From<Syslog7Rfc3164Logger> for Output {
979    /// Creates an output logger which writes all messages to the given syslog.
980    ///
981    /// Log levels are translated trace => debug, debug => debug, info =>
982    /// informational, warn => warning, and error => error.
983    ///
984    /// Note that due to <https://github.com/Geal/rust-syslog/issues/41>,
985    /// logging to this backend requires one allocation per log call.
986    ///
987    /// This is for RFC 3164 loggers. To use an RFC 5424 logger, use the
988    /// [`Output::syslog_5424`] helper method.
989    ///
990    /// This requires the `"syslog-7"` feature.
991    fn from(log: Syslog7Rfc3164Logger) -> Self {
992        Output(OutputInner::Syslog7Rfc3164(log))
993    }
994}
995
996impl From<Panic> for Output {
997    /// Creates an output logger which will panic with message text for all
998    /// messages.
999    fn from(_: Panic) -> Self {
1000        Output(OutputInner::Panic)
1001    }
1002}
1003
1004impl Output {
1005    /// Returns a file logger using a custom separator.
1006    ///
1007    /// If the default separator of `\n` is acceptable, an [`fs::File`]
1008    /// instance can be passed into [`Dispatch::chain`] directly.
1009    ///
1010    /// ```no_run
1011    /// # fn setup_logger() -> Result<(), fern::InitError> {
1012    /// fern::Dispatch::new().chain(std::fs::File::create("log")?)
1013    ///     # .into_log();
1014    /// # Ok(())
1015    /// # }
1016    /// #
1017    /// # fn main() { setup_logger().expect("failed to set up logger"); }
1018    /// ```
1019    ///
1020    /// ```no_run
1021    /// # fn setup_logger() -> Result<(), fern::InitError> {
1022    /// fern::Dispatch::new().chain(fern::log_file("log")?)
1023    ///     # .into_log();
1024    /// # Ok(())
1025    /// # }
1026    /// #
1027    /// # fn main() { setup_logger().expect("failed to set up logger"); }
1028    /// ```
1029    ///
1030    /// Example usage (using [`fern::log_file`]):
1031    ///
1032    /// ```no_run
1033    /// # fn setup_logger() -> Result<(), fern::InitError> {
1034    /// fern::Dispatch::new().chain(fern::Output::file(fern::log_file("log")?, "\r\n"))
1035    ///     # .into_log();
1036    /// # Ok(())
1037    /// # }
1038    /// #
1039    /// # fn main() { setup_logger().expect("failed to set up logger"); }
1040    /// ```
1041    ///
1042    /// [`fs::File`]: https://doc.rust-lang.org/std/fs/struct.File.html
1043    /// [`Dispatch::chain`]: struct.Dispatch.html#method.chain
1044    /// [`fern::log_file`]: fn.log_file.html
1045    pub fn file<T: Into<Cow<'static, str>>>(file: fs::File, line_sep: T) -> Self {
1046        Output(OutputInner::File {
1047            stream: file,
1048            line_sep: line_sep.into(),
1049        })
1050    }
1051
1052    /// Returns a logger using arbitrary write object and custom separator.
1053    ///
1054    /// If the default separator of `\n` is acceptable, an `Box<Write + Send>`
1055    /// instance can be passed into [`Dispatch::chain`] directly.
1056    ///
1057    /// ```no_run
1058    /// # fn setup_logger() -> Result<(), fern::InitError> {
1059    /// // Anything implementing 'Write' works.
1060    /// let mut writer = std::io::Cursor::new(Vec::<u8>::new());
1061    ///
1062    /// fern::Dispatch::new()
1063    ///     // as long as we explicitly cast into a type-erased Box
1064    ///     .chain(Box::new(writer) as Box<std::io::Write + Send>)
1065    ///     # .into_log();
1066    /// #     Ok(())
1067    /// # }
1068    /// #
1069    /// # fn main() { setup_logger().expect("failed to set up logger"); }
1070    /// ```
1071    ///
1072    /// Example usage:
1073    ///
1074    /// ```no_run
1075    /// # fn setup_logger() -> Result<(), fern::InitError> {
1076    /// let writer = Box::new(std::io::Cursor::new(Vec::<u8>::new()));
1077    ///
1078    /// fern::Dispatch::new().chain(fern::Output::writer(writer, "\r\n"))
1079    ///     # .into_log();
1080    /// #     Ok(())
1081    /// # }
1082    /// #
1083    /// # fn main() { setup_logger().expect("failed to set up logger"); }
1084    /// ```
1085    ///
1086    /// [`Dispatch::chain`]: struct.Dispatch.html#method.chain
1087    pub fn writer<T: Into<Cow<'static, str>>>(writer: Box<dyn Write + Send>, line_sep: T) -> Self {
1088        Output(OutputInner::Writer {
1089            stream: writer,
1090            line_sep: line_sep.into(),
1091        })
1092    }
1093
1094    /// Returns a reopenable logger, i.e., handling SIGHUP.
1095    ///
1096    /// If the default separator of `\n` is acceptable, a `Reopen`
1097    /// instance can be passed into [`Dispatch::chain`] directly.
1098    ///
1099    /// This function is not available on Windows, and it requires the `reopen-03`
1100    /// feature to be enabled.
1101    ///
1102    /// ```no_run
1103    /// use std::fs::OpenOptions;
1104    /// # fn setup_logger() -> Result<(), fern::InitError> {
1105    /// let reopenable = reopen03::Reopen::new(Box::new(|| {
1106    ///     OpenOptions::new()
1107    ///         .create(true)
1108    ///         .write(true)
1109    ///         .append(true)
1110    ///         .open("/tmp/output.log")
1111    /// }))
1112    /// .unwrap();
1113    ///
1114    /// fern::Dispatch::new().chain(fern::Output::reopen(reopenable, "\n"))
1115    ///     # .into_log();
1116    /// #     Ok(())
1117    /// # }
1118    /// #
1119    /// # fn main() { setup_logger().expect("failed to set up logger"); }
1120    /// ```
1121    /// [`Dispatch::chain`]: struct.Dispatch.html#method.chain
1122    #[cfg(all(not(windows), feature = "reopen-03"))]
1123    pub fn reopen<T: Into<Cow<'static, str>>>(
1124        reopen: reopen03::Reopen<fs::File>,
1125        line_sep: T,
1126    ) -> Self {
1127        Output(OutputInner::Reopen {
1128            stream: reopen,
1129            line_sep: line_sep.into(),
1130        })
1131    }
1132
1133    /// Returns a reopenable logger, i.e., handling SIGHUP.
1134    ///
1135    /// If the default separator of `\n` is acceptable, a `Reopen`
1136    /// instance can be passed into [`Dispatch::chain`] directly.
1137    ///
1138    /// This function is not available on Windows, and it requires the `reopen-03`
1139    /// feature to be enabled.
1140    ///
1141    /// ```no_run
1142    /// use std::fs::OpenOptions;
1143    /// # fn setup_logger() -> Result<(), fern::InitError> {
1144    /// let reopenable = reopen1::Reopen::new(Box::new(|| {
1145    ///     OpenOptions::new()
1146    ///         .create(true)
1147    ///         .write(true)
1148    ///         .append(true)
1149    ///         .open("/tmp/output.log")
1150    /// }))
1151    /// .unwrap();
1152    ///
1153    /// fern::Dispatch::new().chain(fern::Output::reopen1(reopenable, "\n"))
1154    ///     # .into_log();
1155    /// #     Ok(())
1156    /// # }
1157    /// #
1158    /// # fn main() { setup_logger().expect("failed to set up logger"); }
1159    /// ```
1160    /// [`Dispatch::chain`]: struct.Dispatch.html#method.chain
1161    #[cfg(all(not(windows), feature = "reopen-1"))]
1162    pub fn reopen1<T: Into<Cow<'static, str>>>(
1163        reopen: reopen1::Reopen<fs::File>,
1164        line_sep: T,
1165    ) -> Self {
1166        Output(OutputInner::Reopen1 {
1167            stream: reopen,
1168            line_sep: line_sep.into(),
1169        })
1170    }
1171
1172    /// Returns an stdout logger using a custom separator.
1173    ///
1174    /// If the default separator of `\n` is acceptable, an `io::Stdout`
1175    /// instance can be passed into `Dispatch::chain()` directly.
1176    ///
1177    /// ```
1178    /// fern::Dispatch::new().chain(std::io::stdout())
1179    ///     # .into_log();
1180    /// ```
1181    ///
1182    /// Example usage:
1183    ///
1184    /// ```
1185    /// fern::Dispatch::new()
1186    ///     // some unix tools use null bytes as message terminators so
1187    ///     // newlines in messages can be treated differently.
1188    ///     .chain(fern::Output::stdout("\0"))
1189    ///     # .into_log();
1190    /// ```
1191    pub fn stdout<T: Into<Cow<'static, str>>>(line_sep: T) -> Self {
1192        Output(OutputInner::Stdout {
1193            stream: io::stdout(),
1194            line_sep: line_sep.into(),
1195        })
1196    }
1197
1198    /// Returns an stderr logger using a custom separator.
1199    ///
1200    /// If the default separator of `\n` is acceptable, an `io::Stderr`
1201    /// instance can be passed into `Dispatch::chain()` directly.
1202    ///
1203    /// ```
1204    /// fern::Dispatch::new().chain(std::io::stderr())
1205    ///     # .into_log();
1206    /// ```
1207    ///
1208    /// Example usage:
1209    ///
1210    /// ```
1211    /// fern::Dispatch::new().chain(fern::Output::stderr("\n\n\n"))
1212    ///     # .into_log();
1213    /// ```
1214    pub fn stderr<T: Into<Cow<'static, str>>>(line_sep: T) -> Self {
1215        Output(OutputInner::Stderr {
1216            stream: io::stderr(),
1217            line_sep: line_sep.into(),
1218        })
1219    }
1220
1221    /// Returns a mpsc::Sender logger using a custom separator.
1222    ///
1223    /// If the default separator of `\n` is acceptable, an
1224    /// `mpsc::Sender<String>` instance can be passed into `Dispatch::
1225    /// chain()` directly.
1226    ///
1227    /// Each log message will be suffixed with the separator, then sent as a
1228    /// single String to the given sender.
1229    ///
1230    /// ```
1231    /// use std::sync::mpsc::channel;
1232    ///
1233    /// let (tx, rx) = channel();
1234    /// fern::Dispatch::new().chain(tx)
1235    ///     # .into_log();
1236    /// ```
1237    pub fn sender<T: Into<Cow<'static, str>>>(sender: Sender<String>, line_sep: T) -> Self {
1238        Output(OutputInner::Sender {
1239            stream: sender,
1240            line_sep: line_sep.into(),
1241        })
1242    }
1243
1244    /// Returns a logger which logs into an RFC5424 syslog.
1245    ///
1246    /// This method takes an additional transform method to turn the log data
1247    /// into RFC5424 data.
1248    ///
1249    /// I've honestly got no clue what the expected keys and values are for
1250    /// this kind of logging, so I'm just going to link [the rfc] instead.
1251    ///
1252    /// If you're an expert on syslog logging and would like to contribute
1253    /// an example to put here, it would be gladly accepted!
1254    ///
1255    /// This requires the `"syslog-4"` feature.
1256    ///
1257    /// [the rfc]: https://tools.ietf.org/html/rfc5424
1258    #[cfg(all(not(windows), feature = "syslog-4"))]
1259    pub fn syslog_5424<F>(logger: Syslog4Rfc5424Logger, transform: F) -> Self
1260    where
1261        F: Fn(&log::Record) -> (i32, HashMap<String, HashMap<String, String>>, String)
1262            + Sync
1263            + Send
1264            + 'static,
1265    {
1266        Output(OutputInner::Syslog4Rfc5424 {
1267            logger,
1268            transform: Box::new(transform),
1269        })
1270    }
1271
1272    /// Returns a logger which logs into an RFC5424 syslog (using syslog version 6)
1273    ///
1274    /// This method takes an additional transform method to turn the log data
1275    /// into RFC5424 data.
1276    ///
1277    /// I've honestly got no clue what the expected keys and values are for
1278    /// this kind of logging, so I'm just going to link [the rfc] instead.
1279    ///
1280    /// If you're an expert on syslog logging and would like to contribute
1281    /// an example to put here, it would be gladly accepted!
1282    ///
1283    /// This requires the `"syslog-6"` feature.
1284    ///
1285    /// [the rfc]: https://tools.ietf.org/html/rfc5424
1286    #[cfg(all(not(windows), feature = "syslog-6"))]
1287    pub fn syslog6_5424<F>(logger: Syslog6Rfc5424Logger, transform: F) -> Self
1288    where
1289        F: Fn(&log::Record) -> (u32, HashMap<String, HashMap<String, String>>, String)
1290            + Sync
1291            + Send
1292            + 'static,
1293    {
1294        Output(OutputInner::Syslog6Rfc5424 {
1295            logger,
1296            transform: Box::new(transform),
1297        })
1298    }
1299
1300    /// Returns a logger which logs into an RFC5424 syslog (using syslog version 6)
1301    ///
1302    /// This method takes an additional transform method to turn the log data
1303    /// into RFC5424 data.
1304    ///
1305    /// I've honestly got no clue what the expected keys and values are for
1306    /// this kind of logging, so I'm just going to link [the rfc] instead.
1307    ///
1308    /// If you're an expert on syslog logging and would like to contribute
1309    /// an example to put here, it would be gladly accepted!
1310    ///
1311    /// This requires the `"syslog-7"` feature.
1312    ///
1313    /// [the rfc]: https://tools.ietf.org/html/rfc5424
1314    #[cfg(all(not(windows), feature = "syslog-7"))]
1315    pub fn syslog7_5424<F>(logger: Syslog7Rfc5424Logger, transform: F) -> Self
1316    where
1317        F: Fn(&log::Record) -> (u32, BTreeMap<String, BTreeMap<String, String>>, String)
1318            + Sync
1319            + Send
1320            + 'static,
1321    {
1322        Output(OutputInner::Syslog7Rfc5424 {
1323            logger,
1324            transform: Box::new(transform),
1325        })
1326    }
1327
1328    /// Returns a logger which simply calls the given function with each
1329    /// message.
1330    ///
1331    /// The function will be called inline in the thread the log occurs on.
1332    ///
1333    /// Example usage:
1334    ///
1335    /// ```
1336    /// fern::Dispatch::new().chain(fern::Output::call(|record| {
1337    ///     // this is mundane, but you can do anything here.
1338    ///     println!("{}", record.args());
1339    /// }))
1340    ///     # .into_log();
1341    /// ```
1342    pub fn call<F>(func: F) -> Self
1343    where
1344        F: Fn(&log::Record) + Sync + Send + 'static,
1345    {
1346        struct CallShim<F>(F);
1347        impl<F> log::Log for CallShim<F>
1348        where
1349            F: Fn(&log::Record) + Sync + Send + 'static,
1350        {
1351            fn enabled(&self, _: &log::Metadata) -> bool {
1352                true
1353            }
1354            fn log(&self, record: &log::Record) {
1355                (self.0)(record)
1356            }
1357            fn flush(&self) {}
1358        }
1359
1360        Self::from(Box::new(CallShim(func)) as Box<dyn log::Log>)
1361    }
1362}
1363
1364impl Default for Dispatch {
1365    /// Returns a logger configuration that does nothing with log records.
1366    ///
1367    /// Equivalent to [`Dispatch::new`].
1368    ///
1369    /// [`Dispatch::new`]: #method.new
1370    fn default() -> Self {
1371        Self::new()
1372    }
1373}
1374
1375impl fmt::Debug for Dispatch {
1376    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1377        struct LevelsDebug<'a>(&'a [(Cow<'static, str>, log::LevelFilter)]);
1378        impl<'a> fmt::Debug for LevelsDebug<'a> {
1379            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1380                f.debug_map()
1381                    .entries(self.0.iter().map(|t| (t.0.as_ref(), t.1)))
1382                    .finish()
1383            }
1384        }
1385        struct FiltersDebug<'a>(&'a [Box<Filter>]);
1386        impl<'a> fmt::Debug for FiltersDebug<'a> {
1387            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1388                f.debug_list()
1389                    .entries(self.0.iter().map(|_| "<filter closure>"))
1390                    .finish()
1391            }
1392        }
1393        f.debug_struct("Dispatch")
1394            .field(
1395                "format",
1396                &self.format.as_ref().map(|_| "<formatter closure>"),
1397            )
1398            .field("children", &self.children)
1399            .field("default_level", &self.default_level)
1400            .field("levels", &LevelsDebug(&self.levels))
1401            .field("filters", &FiltersDebug(&self.filters))
1402            .finish()
1403    }
1404}
1405
1406impl fmt::Debug for OutputInner {
1407    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1408        match *self {
1409            OutputInner::Stdout {
1410                ref stream,
1411                ref line_sep,
1412            } => f
1413                .debug_struct("Output::Stdout")
1414                .field("stream", stream)
1415                .field("line_sep", line_sep)
1416                .finish(),
1417            OutputInner::Stderr {
1418                ref stream,
1419                ref line_sep,
1420            } => f
1421                .debug_struct("Output::Stderr")
1422                .field("stream", stream)
1423                .field("line_sep", line_sep)
1424                .finish(),
1425            OutputInner::File {
1426                ref stream,
1427                ref line_sep,
1428            } => f
1429                .debug_struct("Output::File")
1430                .field("stream", stream)
1431                .field("line_sep", line_sep)
1432                .finish(),
1433            OutputInner::Writer { ref line_sep, .. } => f
1434                .debug_struct("Output::Writer")
1435                .field("stream", &"<unknown writer>")
1436                .field("line_sep", line_sep)
1437                .finish(),
1438            #[cfg(all(not(windows), feature = "reopen-03"))]
1439            OutputInner::Reopen { ref line_sep, .. } => f
1440                .debug_struct("Output::Reopen")
1441                .field("stream", &"<unknown reopen file>")
1442                .field("line_sep", line_sep)
1443                .finish(),
1444            #[cfg(all(not(windows), feature = "reopen-1"))]
1445            OutputInner::Reopen1 {
1446                ref line_sep,
1447                ref stream,
1448            } => f
1449                .debug_struct("Output::Reopen1")
1450                .field("stream", stream)
1451                .field("line_sep", line_sep)
1452                .finish(),
1453            OutputInner::Sender {
1454                ref stream,
1455                ref line_sep,
1456            } => f
1457                .debug_struct("Output::Sender")
1458                .field("stream", stream)
1459                .field("line_sep", line_sep)
1460                .finish(),
1461            #[cfg(all(not(windows), feature = "syslog-3"))]
1462            OutputInner::Syslog3(_) => f
1463                .debug_tuple("Output::Syslog3")
1464                .field(&"<unprintable syslog::Logger>")
1465                .finish(),
1466            #[cfg(all(not(windows), feature = "syslog-4"))]
1467            OutputInner::Syslog4Rfc3164 { .. } => f
1468                .debug_tuple("Output::Syslog4Rfc3164")
1469                .field(&"<unprintable syslog::Logger>")
1470                .finish(),
1471            #[cfg(all(not(windows), feature = "syslog-4"))]
1472            OutputInner::Syslog4Rfc5424 { .. } => f
1473                .debug_tuple("Output::Syslog4Rfc5424")
1474                .field(&"<unprintable syslog::Logger>")
1475                .finish(),
1476            #[cfg(all(not(windows), feature = "syslog-6"))]
1477            OutputInner::Syslog6Rfc3164 { .. } => f
1478                .debug_tuple("Output::Syslog6Rfc3164")
1479                .field(&"<unprintable syslog::Logger>")
1480                .finish(),
1481            #[cfg(all(not(windows), feature = "syslog-6"))]
1482            OutputInner::Syslog6Rfc5424 { .. } => f
1483                .debug_tuple("Output::Syslog6Rfc5424")
1484                .field(&"<unprintable syslog::Logger>")
1485                .finish(),
1486            #[cfg(all(not(windows), feature = "syslog-7"))]
1487            OutputInner::Syslog7Rfc3164 { .. } => f
1488                .debug_tuple("Output::Syslog7Rfc3164")
1489                .field(&"<unprintable syslog::Logger>")
1490                .finish(),
1491            #[cfg(all(not(windows), feature = "syslog-7"))]
1492            OutputInner::Syslog7Rfc5424 { .. } => f
1493                .debug_tuple("Output::Syslog7Rfc5424")
1494                .field(&"<unprintable syslog::Logger>")
1495                .finish(),
1496            OutputInner::Dispatch(ref dispatch) => {
1497                f.debug_tuple("Output::Dispatch").field(dispatch).finish()
1498            }
1499            OutputInner::SharedDispatch(_) => f
1500                .debug_tuple("Output::SharedDispatch")
1501                .field(&"<built Dispatch logger>")
1502                .finish(),
1503            OutputInner::OtherBoxed { .. } => f
1504                .debug_tuple("Output::OtherBoxed")
1505                .field(&"<boxed logger>")
1506                .finish(),
1507            OutputInner::OtherStatic { .. } => f
1508                .debug_tuple("Output::OtherStatic")
1509                .field(&"<boxed logger>")
1510                .finish(),
1511            OutputInner::Panic => f.debug_tuple("Output::Panic").finish(),
1512            #[cfg(feature = "date-based")]
1513            OutputInner::DateBased { ref config } => f
1514                .debug_struct("Output::DateBased")
1515                .field("config", config)
1516                .finish(),
1517        }
1518    }
1519}
1520
1521impl fmt::Debug for Output {
1522    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1523        self.0.fmt(f)
1524    }
1525}
1526
1527/// This is used to generate log file suffixed based on date, hour, and minute.
1528///
1529/// The log file will be rotated automatically when the date changes.
1530#[derive(Debug)]
1531#[cfg(feature = "date-based")]
1532pub struct DateBased {
1533    file_prefix: PathBuf,
1534    file_suffix: Cow<'static, str>,
1535    line_sep: Cow<'static, str>,
1536    utc_time: bool,
1537}
1538
1539#[cfg(feature = "date-based")]
1540impl DateBased {
1541    /// Create new date-based file logger with the given file prefix and
1542    /// strftime-based suffix pattern.
1543    ///
1544    /// On initialization, fern will create a file with the suffix formatted
1545    /// with the current time (either utc or local, see below). Each time a
1546    /// record is logged, the format is checked against the current time, and if
1547    /// the time has changed, the old file is closed and a new one opened.
1548    ///
1549    /// `file_suffix` will be interpreted as an `strftime` format. See
1550    /// [`chrono::format::strftime`] for more information.
1551    ///
1552    /// `file_prefix` may be a full file path, and will be prepended to the
1553    /// suffix to create the final file.
1554    ///
1555    /// Note that no separator will be placed in between `file_name` and
1556    /// `file_suffix_pattern`. So if you call `DateBased::new("hello",
1557    /// "%Y")`, the result will be a filepath `hello2019`.
1558    ///
1559    /// By default, this will use local time. For UTC time instead, use the
1560    /// [`.utc_time()`][DateBased::utc_time] method after creating.
1561    ///
1562    /// By default, this will use `\n` as a line separator. For a custom
1563    /// separator, use the [`.line_sep`][DateBased::line_sep] method
1564    /// after creating.
1565    ///
1566    /// # Examples
1567    ///
1568    /// Containing the date (year, month and day):
1569    ///
1570    /// ```
1571    /// // logs/2019-10-23-my-program.log
1572    /// let log = fern::DateBased::new("logs/", "%Y-%m-%d-my-program.log");
1573    ///
1574    /// // program.log.23102019
1575    /// let log = fern::DateBased::new("my-program.log.", "%d%m%Y");
1576    /// ```
1577    ///
1578    /// Containing the hour:
1579    ///
1580    /// ```
1581    /// // logs/2019-10-23 13 my-program.log
1582    /// let log = fern::DateBased::new("logs/", "%Y-%m-%d %H my-program.log");
1583    ///
1584    /// // program.log.2310201913
1585    /// let log = fern::DateBased::new("my-program.log.", "%d%m%Y%H");
1586    /// ```
1587    ///
1588    /// Containing the minute:
1589    ///
1590    /// ```
1591    /// // logs/2019-10-23 13 my-program.log
1592    /// let log = fern::DateBased::new("logs/", "%Y-%m-%d %H my-program.log");
1593    ///
1594    /// // program.log.2310201913
1595    /// let log = fern::DateBased::new("my-program.log.", "%d%m%Y%H");
1596    /// ```
1597    ///
1598    /// UNIX time, or seconds since 00:00 Jan 1st 1970:
1599    ///
1600    /// ```
1601    /// // logs/1571822854-my-program.log
1602    /// let log = fern::DateBased::new("logs/", "%s-my-program.log");
1603    ///
1604    /// // program.log.1571822854
1605    /// let log = fern::DateBased::new("my-program.log.", "%s");
1606    /// ```
1607    ///
1608    /// Hourly, using UTC time:
1609    ///
1610    /// ```
1611    /// // logs/2019-10-23 23 my-program.log
1612    /// let log = fern::DateBased::new("logs/", "%Y-%m-%d %H my-program.log").utc_time();
1613    ///
1614    /// // program.log.2310201923
1615    /// let log = fern::DateBased::new("my-program.log.", "%d%m%Y%H").utc_time();
1616    /// ```
1617    ///
1618    /// [`chrono::format::strftime`]: https://docs.rs/chrono/0.4.6/chrono/format/strftime/index.html
1619    pub fn new<T, U>(file_prefix: T, file_suffix: U) -> Self
1620    where
1621        T: AsRef<Path>,
1622        U: Into<Cow<'static, str>>,
1623    {
1624        DateBased {
1625            utc_time: false,
1626            file_prefix: file_prefix.as_ref().to_owned(),
1627            file_suffix: file_suffix.into(),
1628            line_sep: "\n".into(),
1629        }
1630    }
1631
1632    /// Changes the line separator this logger will use.
1633    ///
1634    /// The default line separator is `\n`.
1635    ///
1636    /// # Examples
1637    ///
1638    /// Using a windows line separator:
1639    ///
1640    /// ```
1641    /// let log = fern::DateBased::new("logs", "%s.log").line_sep("\r\n");
1642    /// ```
1643    pub fn line_sep<T>(mut self, line_sep: T) -> Self
1644    where
1645        T: Into<Cow<'static, str>>,
1646    {
1647        self.line_sep = line_sep.into();
1648        self
1649    }
1650
1651    /// Orients this log file suffix formatting to use UTC time.
1652    ///
1653    /// The default is local time.
1654    ///
1655    /// # Examples
1656    ///
1657    /// This will use UTC time to determine the date:
1658    ///
1659    /// ```
1660    /// // program.log.2310201923
1661    /// let log = fern::DateBased::new("my-program.log.", "%d%m%Y%H").utc_time();
1662    /// ```
1663    pub fn utc_time(mut self) -> Self {
1664        self.utc_time = true;
1665        self
1666    }
1667
1668    /// Orients this log file suffix formatting to use local time.
1669    ///
1670    /// This is the default option.
1671    ///
1672    /// # Examples
1673    ///
1674    /// This log file will use local time - the latter method call overrides the
1675    /// former.
1676    ///
1677    /// ```
1678    /// // program.log.2310201923
1679    /// let log = fern::DateBased::new("my-program.log.", "%d%m%Y%H")
1680    ///     .utc_time()
1681    ///     .local_time();
1682    /// ```
1683    pub fn local_time(mut self) -> Self {
1684        self.utc_time = false;
1685        self
1686    }
1687}
1688
1689#[cfg(feature = "date-based")]
1690impl From<DateBased> for Output {
1691    /// Create an output logger which defers to the given date-based logger. Use
1692    /// configuration methods on [DateBased] to set line separator and filename.
1693    fn from(config: DateBased) -> Self {
1694        Output(OutputInner::DateBased { config })
1695    }
1696}