Skip to main content

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