1 use 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")]
9 use std::path::{Path, PathBuf};
10 
11 #[cfg(all(not(windows), feature = "syslog-4"))]
12 use std::collections::HashMap;
13 
14 use log::Log;
15 
16 use crate::{log_impl, Filter, FormatCallback, Formatter};
17 
18 #[cfg(feature = "date-based")]
19 use crate::log_impl::DateBasedState;
20 
21 #[cfg(all(not(windows), feature = "syslog-4"))]
22 use 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()"]
98 pub 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)]
116 pub struct SharedDispatch {
117     inner: Arc<log_impl::Dispatch>,
118     min_level: log::LevelFilter,
119 }
120 
121 impl Dispatch {
122     /// Creates a dispatch, which will initially do nothing.
123     #[inline]
new() -> Self124     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]
format<F>(mut self, formatter: F) -> Self where F: Fn(FormatCallback, &fmt::Arguments, &log::Record) + Sync + Send + 'static,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]
chain<T: Into<Output>>(mut self, logger: T) -> Self195     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]
level(mut self, level: log::LevelFilter) -> Self221     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]
level_for<T: Into<Cow<'static, str>>>( mut self, module: T, level: log::LevelFilter, ) -> Self283     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]
filter<F>(mut self, filter: F) -> Self where F: Fn(&log::Metadata) -> bool + Send + Sync + 'static,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
into_shared(self) -> SharedDispatch391     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.
into_dispatch(self) -> (log::LevelFilter, log_impl::Dispatch)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     /// ```
into_log(self) -> (log::LevelFilter, Box<dyn log::Log>)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
apply(self) -> Result<(), log::SetLoggerError>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.
615 enum 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 /// ```
720 pub struct Panic;
721 
722 /// Configuration for a logger output.
723 pub struct Output(OutputInner);
724 
725 impl From<Dispatch> for Output {
726     /// Creates an output logger forwarding all messages to the dispatch.
from(log: Dispatch) -> Self727     fn from(log: Dispatch) -> Self {
728         Output(OutputInner::Dispatch(log))
729     }
730 }
731 
732 impl From<SharedDispatch> for Output {
733     /// Creates an output logger forwarding all messages to the dispatch.
from(log: SharedDispatch) -> Self734     fn from(log: SharedDispatch) -> Self {
735         Output(OutputInner::SharedDispatch(log))
736     }
737 }
738 
739 impl From<Box<dyn Log>> for Output {
740     /// Creates an output logger forwarding all messages to the custom logger.
from(log: Box<dyn Log>) -> Self741     fn from(log: Box<dyn Log>) -> Self {
742         Output(OutputInner::OtherBoxed(log))
743     }
744 }
745 
746 impl From<&'static dyn Log> for Output {
747     /// Creates an output logger forwarding all messages to the custom logger.
from(log: &'static dyn Log) -> Self748     fn from(log: &'static dyn Log) -> Self {
749         Output(OutputInner::OtherStatic(log))
750     }
751 }
752 
753 impl 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.
from(file: fs::File) -> Self758     fn from(file: fs::File) -> Self {
759         Output(OutputInner::File {
760             stream: file,
761             line_sep: "\n".into(),
762         })
763     }
764 }
765 
766 impl 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.
from(writer: Box<dyn Write + Send>) -> Self773     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"))]
782 impl 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.
from(reopen: reopen::Reopen<fs::File>) -> Self785     fn from(reopen: reopen::Reopen<fs::File>) -> Self {
786         Output(OutputInner::Reopen {
787             stream: reopen,
788             line_sep: "\n".into(),
789         })
790     }
791 }
792 
793 impl 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.
from(stream: io::Stdout) -> Self796     fn from(stream: io::Stdout) -> Self {
797         Output(OutputInner::Stdout {
798             stream,
799             line_sep: "\n".into(),
800         })
801     }
802 }
803 
804 impl 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.
from(stream: io::Stderr) -> Self807     fn from(stream: io::Stderr) -> Self {
808         Output(OutputInner::Stderr {
809             stream,
810             line_sep: "\n".into(),
811         })
812     }
813 }
814 
815 impl 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'.
from(stream: Sender<String>) -> Self820     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"))]
829 impl 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.
from(log: syslog3::Logger) -> Self837     fn from(log: syslog3::Logger) -> Self {
838         Output(OutputInner::Syslog3(log))
839     }
840 }
841 
842 #[cfg(all(not(windows), feature = "syslog-3"))]
843 impl 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.
from(log: Box<syslog3::Logger>) -> Self856     fn from(log: Box<syslog3::Logger>) -> Self {
857         Output(OutputInner::Syslog3(*log))
858     }
859 }
860 
861 #[cfg(all(not(windows), feature = "syslog-4"))]
862 impl 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.
from(log: Syslog4Rfc3164Logger) -> Self875     fn from(log: Syslog4Rfc3164Logger) -> Self {
876         Output(OutputInner::Syslog4Rfc3164(log))
877     }
878 }
879 
880 impl From<Panic> for Output {
881     /// Creates an output logger which will panic with message text for all
882     /// messages.
from(_: Panic) -> Self883     fn from(_: Panic) -> Self {
884         Output(OutputInner::Panic)
885     }
886 }
887 
888 impl 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
file<T: Into<Cow<'static, str>>>(file: fs::File, line_sep: T) -> Self929     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
writer<T: Into<Cow<'static, str>>>(writer: Box<dyn Write + Send>, line_sep: T) -> Self971     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     /// ```no_run
984     /// use std::fs::OpenOptions;
985     /// # fn setup_logger() -> Result<(), fern::InitError> {
986     /// let reopenable = reopen::Reopen::new(Box::new(|| {
987     ///     OpenOptions::new()
988     ///         .create(true)
989     ///         .write(true)
990     ///         .append(true)
991     ///         .open("/tmp/output.log")
992     /// }))
993     /// .unwrap();
994     ///
995     /// fern::Dispatch::new().chain(fern::Output::reopen(reopenable, "\n"))
996     ///     # .into_log();
997     /// #     Ok(())
998     /// # }
999     /// #
1000     /// # fn main() { setup_logger().expect("failed to set up logger"); }
1001     /// ```
1002     /// [`Dispatch::chain`]: struct.Dispatch.html#method.chain
1003     #[cfg(all(not(windows), feature = "reopen-03"))]
reopen<T: Into<Cow<'static, str>>>( reopen: reopen::Reopen<fs::File>, line_sep: T, ) -> Self1004     pub fn reopen<T: Into<Cow<'static, str>>>(
1005         reopen: reopen::Reopen<fs::File>,
1006         line_sep: T,
1007     ) -> Self {
1008         Output(OutputInner::Reopen {
1009             stream: reopen,
1010             line_sep: line_sep.into(),
1011         })
1012     }
1013 
1014     /// Returns an stdout logger using a custom separator.
1015     ///
1016     /// If the default separator of `\n` is acceptable, an `io::Stdout`
1017     /// instance can be passed into `Dispatch::chain()` directly.
1018     ///
1019     /// ```
1020     /// fern::Dispatch::new().chain(std::io::stdout())
1021     ///     # .into_log();
1022     /// ```
1023     ///
1024     /// Example usage:
1025     ///
1026     /// ```
1027     /// fern::Dispatch::new()
1028     ///     // some unix tools use null bytes as message terminators so
1029     ///     // newlines in messages can be treated differently.
1030     ///     .chain(fern::Output::stdout("\0"))
1031     ///     # .into_log();
1032     /// ```
stdout<T: Into<Cow<'static, str>>>(line_sep: T) -> Self1033     pub fn stdout<T: Into<Cow<'static, str>>>(line_sep: T) -> Self {
1034         Output(OutputInner::Stdout {
1035             stream: io::stdout(),
1036             line_sep: line_sep.into(),
1037         })
1038     }
1039 
1040     /// Returns an stderr logger using a custom separator.
1041     ///
1042     /// If the default separator of `\n` is acceptable, an `io::Stderr`
1043     /// instance can be passed into `Dispatch::chain()` directly.
1044     ///
1045     /// ```
1046     /// fern::Dispatch::new().chain(std::io::stderr())
1047     ///     # .into_log();
1048     /// ```
1049     ///
1050     /// Example usage:
1051     ///
1052     /// ```
1053     /// fern::Dispatch::new().chain(fern::Output::stderr("\n\n\n"))
1054     ///     # .into_log();
1055     /// ```
stderr<T: Into<Cow<'static, str>>>(line_sep: T) -> Self1056     pub fn stderr<T: Into<Cow<'static, str>>>(line_sep: T) -> Self {
1057         Output(OutputInner::Stderr {
1058             stream: io::stderr(),
1059             line_sep: line_sep.into(),
1060         })
1061     }
1062 
1063     /// Returns a mpsc::Sender logger using a custom separator.
1064     ///
1065     /// If the default separator of `\n` is acceptable, an
1066     /// `mpsc::Sender<String>` instance can be passed into `Dispatch::
1067     /// chain()` directly.
1068     ///
1069     /// Each log message will be suffixed with the separator, then sent as a
1070     /// single String to the given sender.
1071     ///
1072     /// ```
1073     /// use std::sync::mpsc::channel;
1074     ///
1075     /// let (tx, rx) = channel();
1076     /// fern::Dispatch::new().chain(tx)
1077     ///     # .into_log();
1078     /// ```
sender<T: Into<Cow<'static, str>>>(sender: Sender<String>, line_sep: T) -> Self1079     pub fn sender<T: Into<Cow<'static, str>>>(sender: Sender<String>, line_sep: T) -> Self {
1080         Output(OutputInner::Sender {
1081             stream: sender,
1082             line_sep: line_sep.into(),
1083         })
1084     }
1085 
1086     /// Returns a logger which logs into an RFC5424 syslog.
1087     ///
1088     /// This method takes an additional transform method to turn the log data
1089     /// into RFC5424 data.
1090     ///
1091     /// I've honestly got no clue what the expected keys and values are for
1092     /// this kind of logging, so I'm just going to link [the rfc] instead.
1093     ///
1094     /// If you're an expert on syslog logging and would like to contribute
1095     /// an example to put here, it would be gladly accepted!
1096     ///
1097     /// This requires the `"syslog-4"` feature.
1098     ///
1099     /// [the rfc]: https://tools.ietf.org/html/rfc5424
1100     #[cfg(all(not(windows), feature = "syslog-4"))]
syslog_5424<F>(logger: Syslog4Rfc5424Logger, transform: F) -> Self where F: Fn(&log::Record) -> (i32, HashMap<String, HashMap<String, String>>, String) + Sync + Send + 'static,1101     pub fn syslog_5424<F>(logger: Syslog4Rfc5424Logger, transform: F) -> Self
1102     where
1103         F: Fn(&log::Record) -> (i32, HashMap<String, HashMap<String, String>>, String)
1104             + Sync
1105             + Send
1106             + 'static,
1107     {
1108         Output(OutputInner::Syslog4Rfc5424 {
1109             logger,
1110             transform: Box::new(transform),
1111         })
1112     }
1113 
1114     /// Returns a logger which simply calls the given function with each
1115     /// message.
1116     ///
1117     /// The function will be called inline in the thread the log occurs on.
1118     ///
1119     /// Example usage:
1120     ///
1121     /// ```
1122     /// fern::Dispatch::new().chain(fern::Output::call(|record| {
1123     ///     // this is mundane, but you can do anything here.
1124     ///     println!("{}", record.args());
1125     /// }))
1126     ///     # .into_log();
1127     /// ```
call<F>(func: F) -> Self where F: Fn(&log::Record) + Sync + Send + 'static,1128     pub fn call<F>(func: F) -> Self
1129     where
1130         F: Fn(&log::Record) + Sync + Send + 'static,
1131     {
1132         struct CallShim<F>(F);
1133         impl<F> log::Log for CallShim<F>
1134         where
1135             F: Fn(&log::Record) + Sync + Send + 'static,
1136         {
1137             fn enabled(&self, _: &log::Metadata) -> bool {
1138                 true
1139             }
1140             fn log(&self, record: &log::Record) {
1141                 (self.0)(record)
1142             }
1143             fn flush(&self) {}
1144         }
1145 
1146         Self::from(Box::new(CallShim(func)) as Box<dyn log::Log>)
1147     }
1148 }
1149 
1150 impl Default for Dispatch {
1151     /// Returns a logger configuration that does nothing with log records.
1152     ///
1153     /// Equivalent to [`Dispatch::new`].
1154     ///
1155     /// [`Dispatch::new`]: #method.new
default() -> Self1156     fn default() -> Self {
1157         Self::new()
1158     }
1159 }
1160 
1161 impl fmt::Debug for Dispatch {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1162     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1163         struct LevelsDebug<'a>(&'a [(Cow<'static, str>, log::LevelFilter)]);
1164         impl<'a> fmt::Debug for LevelsDebug<'a> {
1165             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1166                 f.debug_map()
1167                     .entries(self.0.iter().map(|t| (t.0.as_ref(), t.1)))
1168                     .finish()
1169             }
1170         }
1171         struct FiltersDebug<'a>(&'a [Box<Filter>]);
1172         impl<'a> fmt::Debug for FiltersDebug<'a> {
1173             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1174                 f.debug_list()
1175                     .entries(self.0.iter().map(|_| "<filter closure>"))
1176                     .finish()
1177             }
1178         }
1179         f.debug_struct("Dispatch")
1180             .field(
1181                 "format",
1182                 &self.format.as_ref().map(|_| "<formatter closure>"),
1183             )
1184             .field("children", &self.children)
1185             .field("default_level", &self.default_level)
1186             .field("levels", &LevelsDebug(&self.levels))
1187             .field("filters", &FiltersDebug(&self.filters))
1188             .finish()
1189     }
1190 }
1191 
1192 impl fmt::Debug for OutputInner {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1193     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1194         match *self {
1195             OutputInner::Stdout {
1196                 ref stream,
1197                 ref line_sep,
1198             } => f
1199                 .debug_struct("Output::Stdout")
1200                 .field("stream", stream)
1201                 .field("line_sep", line_sep)
1202                 .finish(),
1203             OutputInner::Stderr {
1204                 ref stream,
1205                 ref line_sep,
1206             } => f
1207                 .debug_struct("Output::Stderr")
1208                 .field("stream", stream)
1209                 .field("line_sep", line_sep)
1210                 .finish(),
1211             OutputInner::File {
1212                 ref stream,
1213                 ref line_sep,
1214             } => f
1215                 .debug_struct("Output::File")
1216                 .field("stream", stream)
1217                 .field("line_sep", line_sep)
1218                 .finish(),
1219             OutputInner::Writer { ref line_sep, .. } => f
1220                 .debug_struct("Output::Writer")
1221                 .field("stream", &"<unknown writer>")
1222                 .field("line_sep", line_sep)
1223                 .finish(),
1224             #[cfg(all(not(windows), feature = "reopen-03"))]
1225             OutputInner::Reopen { ref line_sep, .. } => f
1226                 .debug_struct("Output::Reopen")
1227                 .field("stream", &"<unknown reopen file>")
1228                 .field("line_sep", line_sep)
1229                 .finish(),
1230             OutputInner::Sender {
1231                 ref stream,
1232                 ref line_sep,
1233             } => f
1234                 .debug_struct("Output::Sender")
1235                 .field("stream", stream)
1236                 .field("line_sep", line_sep)
1237                 .finish(),
1238             #[cfg(all(not(windows), feature = "syslog-3"))]
1239             OutputInner::Syslog3(_) => f
1240                 .debug_tuple("Output::Syslog3")
1241                 .field(&"<unprintable syslog::Logger>")
1242                 .finish(),
1243             #[cfg(all(not(windows), feature = "syslog-4"))]
1244             OutputInner::Syslog4Rfc3164 { .. } => f
1245                 .debug_tuple("Output::Syslog4Rfc3164")
1246                 .field(&"<unprintable syslog::Logger>")
1247                 .finish(),
1248             #[cfg(all(not(windows), feature = "syslog-4"))]
1249             OutputInner::Syslog4Rfc5424 { .. } => f
1250                 .debug_tuple("Output::Syslog4Rfc5424")
1251                 .field(&"<unprintable syslog::Logger>")
1252                 .finish(),
1253             OutputInner::Dispatch(ref dispatch) => {
1254                 f.debug_tuple("Output::Dispatch").field(dispatch).finish()
1255             }
1256             OutputInner::SharedDispatch(_) => f
1257                 .debug_tuple("Output::SharedDispatch")
1258                 .field(&"<built Dispatch logger>")
1259                 .finish(),
1260             OutputInner::OtherBoxed { .. } => f
1261                 .debug_tuple("Output::OtherBoxed")
1262                 .field(&"<boxed logger>")
1263                 .finish(),
1264             OutputInner::OtherStatic { .. } => f
1265                 .debug_tuple("Output::OtherStatic")
1266                 .field(&"<boxed logger>")
1267                 .finish(),
1268             OutputInner::Panic => f.debug_tuple("Output::Panic").finish(),
1269             #[cfg(feature = "date-based")]
1270             OutputInner::DateBased { ref config } => f
1271                 .debug_struct("Output::DateBased")
1272                 .field("config", config)
1273                 .finish(),
1274         }
1275     }
1276 }
1277 
1278 impl fmt::Debug for Output {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result1279     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1280         self.0.fmt(f)
1281     }
1282 }
1283 
1284 /// This is used to generate log file suffixed based on date, hour, and minute.
1285 ///
1286 /// The log file will be rotated automatically when the date changes.
1287 #[derive(Debug)]
1288 #[cfg(feature = "date-based")]
1289 pub struct DateBased {
1290     file_prefix: PathBuf,
1291     file_suffix: Cow<'static, str>,
1292     line_sep: Cow<'static, str>,
1293     utc_time: bool,
1294 }
1295 
1296 #[cfg(feature = "date-based")]
1297 impl DateBased {
1298     /// Create new date-based file logger with the given file prefix and
1299     /// strftime-based suffix pattern.
1300     ///
1301     /// On initialization, fern will create a file with the suffix formatted
1302     /// with the current time (either utc or local, see below). Each time a
1303     /// record is logged, the format is checked against the current time, and if
1304     /// the time has changed, the old file is closed and a new one opened.
1305     ///
1306     /// `file_suffix` will be interpreted as an `strftime` format. See
1307     /// [`chrono::format::strftime`] for more information.
1308     ///
1309     /// `file_prefix` may be a full file path, and will be prepended to the
1310     /// suffix to create the final file.
1311     ///
1312     /// Note that no separator will be placed in between `file_name` and
1313     /// `file_suffix_pattern`. So if you call `DateBased::new("hello",
1314     /// "%Y")`, the result will be a filepath `hello2019`.
1315     ///
1316     /// By default, this will use local time. For UTC time instead, use the
1317     /// [`.utc_time()`][DateBased::utc_time] method after creating.
1318     ///
1319     /// By default, this will use `\n` as a line separator. For a custom
1320     /// separator, use the [`.line_sep`][DateBased::line_sep] method
1321     /// after creating.
1322     ///
1323     /// # Examples
1324     ///
1325     /// Containing the date (year, month and day):
1326     ///
1327     /// ```
1328     /// // logs/2019-10-23-my-program.log
1329     /// let log = fern::DateBased::new("logs/", "%Y-%m-%d-my-program.log");
1330     ///
1331     /// // program.log.23102019
1332     /// let log = fern::DateBased::new("my-program.log.", "%d%m%Y");
1333     /// ```
1334     ///
1335     /// Containing the hour:
1336     ///
1337     /// ```
1338     /// // logs/2019-10-23 13 my-program.log
1339     /// let log = fern::DateBased::new("logs/", "%Y-%m-%d %H my-program.log");
1340     ///
1341     /// // program.log.2310201913
1342     /// let log = fern::DateBased::new("my-program.log.", "%d%m%Y%H");
1343     /// ```
1344     ///
1345     /// Containing the minute:
1346     ///
1347     /// ```
1348     /// // logs/2019-10-23 13 my-program.log
1349     /// let log = fern::DateBased::new("logs/", "%Y-%m-%d %H my-program.log");
1350     ///
1351     /// // program.log.2310201913
1352     /// let log = fern::DateBased::new("my-program.log.", "%d%m%Y%H");
1353     /// ```
1354     ///
1355     /// UNIX time, or seconds since 00:00 Jan 1st 1970:
1356     ///
1357     /// ```
1358     /// // logs/1571822854-my-program.log
1359     /// let log = fern::DateBased::new("logs/", "%s-my-program.log");
1360     ///
1361     /// // program.log.1571822854
1362     /// let log = fern::DateBased::new("my-program.log.", "%s");
1363     /// ```
1364     ///
1365     /// Hourly, using UTC time:
1366     ///
1367     /// ```
1368     /// // logs/2019-10-23 23 my-program.log
1369     /// let log = fern::DateBased::new("logs/", "%Y-%m-%d %H my-program.log").utc_time();
1370     ///
1371     /// // program.log.2310201923
1372     /// let log = fern::DateBased::new("my-program.log.", "%d%m%Y%H").utc_time();
1373     /// ```
1374     ///
1375     /// [`chrono::format::strftime`]: https://docs.rs/chrono/0.4.6/chrono/format/strftime/index.html
new<T, U>(file_prefix: T, file_suffix: U) -> Self where T: AsRef<Path>, U: Into<Cow<'static, str>>,1376     pub fn new<T, U>(file_prefix: T, file_suffix: U) -> Self
1377     where
1378         T: AsRef<Path>,
1379         U: Into<Cow<'static, str>>,
1380     {
1381         DateBased {
1382             utc_time: false,
1383             file_prefix: file_prefix.as_ref().to_owned(),
1384             file_suffix: file_suffix.into(),
1385             line_sep: "\n".into(),
1386         }
1387     }
1388 
1389     /// Changes the line separator this logger will use.
1390     ///
1391     /// The default line separator is `\n`.
1392     ///
1393     /// # Examples
1394     ///
1395     /// Using a windows line separator:
1396     ///
1397     /// ```
1398     /// let log = fern::DateBased::new("logs", "%s.log").line_sep("\r\n");
1399     /// ```
line_sep<T>(mut self, line_sep: T) -> Self where T: Into<Cow<'static, str>>,1400     pub fn line_sep<T>(mut self, line_sep: T) -> Self
1401     where
1402         T: Into<Cow<'static, str>>,
1403     {
1404         self.line_sep = line_sep.into();
1405         self
1406     }
1407 
1408     /// Orients this log file suffix formatting to use UTC time.
1409     ///
1410     /// The default is local time.
1411     ///
1412     /// # Examples
1413     ///
1414     /// This will use UTC time to determine the date:
1415     ///
1416     /// ```
1417     /// // program.log.2310201923
1418     /// let log = fern::DateBased::new("my-program.log.", "%d%m%Y%H").utc_time();
1419     /// ```
utc_time(mut self) -> Self1420     pub fn utc_time(mut self) -> Self {
1421         self.utc_time = true;
1422         self
1423     }
1424 
1425     /// Orients this log file suffix formatting to use local time.
1426     ///
1427     /// This is the default option.
1428     ///
1429     /// # Examples
1430     ///
1431     /// This log file will use local time - the latter method call overrides the
1432     /// former.
1433     ///
1434     /// ```
1435     /// // program.log.2310201923
1436     /// let log = fern::DateBased::new("my-program.log.", "%d%m%Y%H")
1437     ///     .utc_time()
1438     ///     .local_time();
1439     /// ```
local_time(mut self) -> Self1440     pub fn local_time(mut self) -> Self {
1441         self.utc_time = false;
1442         self
1443     }
1444 }
1445 
1446 #[cfg(feature = "date-based")]
1447 impl From<DateBased> for Output {
1448     /// Create an output logger which defers to the given date-based logger. Use
1449     /// configuration methods on [DateBased] to set line separator and filename.
from(config: DateBased) -> Self1450     fn from(config: DateBased) -> Self {
1451         Output(OutputInner::DateBased { config })
1452     }
1453 }
1454