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