1 //! Formatting for log records. 2 //! 3 //! This module contains a [`Formatter`] that can be used to format log records 4 //! into without needing temporary allocations. Usually you won't need to worry 5 //! about the contents of this module and can use the `Formatter` like an ordinary 6 //! [`Write`]. 7 //! 8 //! # Formatting log records 9 //! 10 //! The format used to print log records can be customised using the [`Builder::format`] 11 //! method. 12 //! Custom formats can apply different color and weight to printed values using 13 //! [`Style`] builders. 14 //! 15 //! ``` 16 //! use std::io::Write; 17 //! 18 //! let mut builder = env_logger::Builder::new(); 19 //! 20 //! builder.format(|buf, record| { 21 //! writeln!(buf, "{}: {}", 22 //! record.level(), 23 //! record.args()) 24 //! }); 25 //! ``` 26 //! 27 //! [`Formatter`]: struct.Formatter.html 28 //! [`Style`]: struct.Style.html 29 //! [`Builder::format`]: ../struct.Builder.html#method.format 30 //! [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html 31 32 use std::cell::RefCell; 33 use std::fmt::Display; 34 use std::io::prelude::*; 35 use std::rc::Rc; 36 use std::{fmt, io, mem}; 37 38 use log::Record; 39 40 mod humantime; 41 pub(crate) mod writer; 42 43 pub use self::humantime::glob::*; 44 pub use self::writer::glob::*; 45 46 use self::writer::{Buffer, Writer}; 47 48 pub(crate) mod glob { 49 pub use super::{Target, TimestampPrecision, WriteStyle}; 50 } 51 52 /// Formatting precision of timestamps. 53 /// 54 /// Seconds give precision of full seconds, milliseconds give thousands of a 55 /// second (3 decimal digits), microseconds are millionth of a second (6 decimal 56 /// digits) and nanoseconds are billionth of a second (9 decimal digits). 57 #[derive(Copy, Clone, Debug)] 58 pub enum TimestampPrecision { 59 /// Full second precision (0 decimal digits) 60 Seconds, 61 /// Millisecond precision (3 decimal digits) 62 Millis, 63 /// Microsecond precision (6 decimal digits) 64 Micros, 65 /// Nanosecond precision (9 decimal digits) 66 Nanos, 67 } 68 69 /// The default timestamp precision is seconds. 70 impl Default for TimestampPrecision { defaultnull71 fn default() -> Self { 72 TimestampPrecision::Seconds 73 } 74 } 75 76 /// A formatter to write logs into. 77 /// 78 /// `Formatter` implements the standard [`Write`] trait for writing log records. 79 /// It also supports terminal colors, through the [`style`] method. 80 /// 81 /// # Examples 82 /// 83 /// Use the [`writeln`] macro to format a log record. 84 /// An instance of a `Formatter` is passed to an `env_logger` format as `buf`: 85 /// 86 /// ``` 87 /// use std::io::Write; 88 /// 89 /// let mut builder = env_logger::Builder::new(); 90 /// 91 /// builder.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args())); 92 /// ``` 93 /// 94 /// [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html 95 /// [`writeln`]: https://doc.rust-lang.org/stable/std/macro.writeln.html 96 /// [`style`]: #method.style 97 pub struct Formatter { 98 buf: Rc<RefCell<Buffer>>, 99 write_style: WriteStyle, 100 } 101 102 impl Formatter { 103 pub(crate) fn new(writer: &Writer) -> Self { 104 Formatter { 105 buf: Rc::new(RefCell::new(writer.buffer())), 106 write_style: writer.write_style(), 107 } 108 } 109 110 pub(crate) fn write_style(&self) -> WriteStyle { 111 self.write_style 112 } 113 114 pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> { 115 writer.print(&self.buf.borrow()) 116 } 117 118 pub(crate) fn clear(&mut self) { 119 self.buf.borrow_mut().clear() 120 } 121 } 122 123 impl Write for Formatter { writenull124 fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 125 self.buf.borrow_mut().write(buf) 126 } 127 flushnull128 fn flush(&mut self) -> io::Result<()> { 129 self.buf.borrow_mut().flush() 130 } 131 } 132 133 impl fmt::Debug for Formatter { fmtnull134 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 135 f.debug_struct("Formatter").finish() 136 } 137 } 138 139 pub(crate) type FormatFn = Box<dyn Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send>; 140 141 pub(crate) struct Builder { 142 pub format_timestamp: Option<TimestampPrecision>, 143 pub format_module_path: bool, 144 pub format_target: bool, 145 pub format_level: bool, 146 pub format_indent: Option<usize>, 147 pub custom_format: Option<FormatFn>, 148 pub format_suffix: &'static str, 149 built: bool, 150 } 151 152 impl Default for Builder { defaultnull153 fn default() -> Self { 154 Builder { 155 format_timestamp: Some(Default::default()), 156 format_module_path: false, 157 format_target: true, 158 format_level: true, 159 format_indent: Some(4), 160 custom_format: None, 161 format_suffix: "\n", 162 built: false, 163 } 164 } 165 } 166 167 impl Builder { 168 /// Convert the format into a callable function. 169 /// 170 /// If the `custom_format` is `Some`, then any `default_format` switches are ignored. 171 /// If the `custom_format` is `None`, then a default format is returned. 172 /// Any `default_format` switches set to `false` won't be written by the format. buildnull173 pub fn build(&mut self) -> FormatFn { 174 assert!(!self.built, "attempt to re-use consumed builder"); 175 176 let built = mem::replace( 177 self, 178 Builder { 179 built: true, 180 ..Default::default() 181 }, 182 ); 183 184 if let Some(fmt) = built.custom_format { 185 fmt 186 } else { 187 Box::new(move |buf, record| { 188 let fmt = DefaultFormat { 189 timestamp: built.format_timestamp, 190 module_path: built.format_module_path, 191 target: built.format_target, 192 level: built.format_level, 193 written_header_value: false, 194 indent: built.format_indent, 195 suffix: built.format_suffix, 196 buf, 197 }; 198 199 fmt.write(record) 200 }) 201 } 202 } 203 } 204 205 #[cfg(feature = "termcolor")] 206 type SubtleStyle = StyledValue<'static, &'static str>; 207 #[cfg(not(feature = "termcolor"))] 208 type SubtleStyle = &'static str; 209 210 /// The default format. 211 /// 212 /// This format needs to work with any combination of crate features. 213 struct DefaultFormat<'a> { 214 timestamp: Option<TimestampPrecision>, 215 module_path: bool, 216 target: bool, 217 level: bool, 218 written_header_value: bool, 219 indent: Option<usize>, 220 buf: &'a mut Formatter, 221 suffix: &'a str, 222 } 223 224 impl<'a> DefaultFormat<'a> { writenull225 fn write(mut self, record: &Record) -> io::Result<()> { 226 self.write_timestamp()?; 227 self.write_level(record)?; 228 self.write_module_path(record)?; 229 self.write_target(record)?; 230 self.finish_header()?; 231 232 self.write_args(record) 233 } 234 subtle_stylenull235 fn subtle_style(&self, text: &'static str) -> SubtleStyle { 236 #[cfg(feature = "termcolor")] 237 { 238 self.buf 239 .style() 240 .set_color(Color::Black) 241 .set_intense(true) 242 .clone() 243 .into_value(text) 244 } 245 #[cfg(not(feature = "termcolor"))] 246 { 247 text 248 } 249 } 250 write_header_valuenull251 fn write_header_value<T>(&mut self, value: T) -> io::Result<()> 252 where 253 T: Display, 254 { 255 if !self.written_header_value { 256 self.written_header_value = true; 257 258 let open_brace = self.subtle_style("["); 259 write!(self.buf, "{}{}", open_brace, value) 260 } else { 261 write!(self.buf, " {}", value) 262 } 263 } 264 write_levelnull265 fn write_level(&mut self, record: &Record) -> io::Result<()> { 266 if !self.level { 267 return Ok(()); 268 } 269 270 let level = { 271 #[cfg(feature = "termcolor")] 272 { 273 self.buf.default_styled_level(record.level()) 274 } 275 #[cfg(not(feature = "termcolor"))] 276 { 277 record.level() 278 } 279 }; 280 281 self.write_header_value(format_args!("{:<5}", level)) 282 } 283 write_timestampnull284 fn write_timestamp(&mut self) -> io::Result<()> { 285 #[cfg(feature = "humantime")] 286 { 287 use self::TimestampPrecision::*; 288 let ts = match self.timestamp { 289 None => return Ok(()), 290 Some(Seconds) => self.buf.timestamp_seconds(), 291 Some(Millis) => self.buf.timestamp_millis(), 292 Some(Micros) => self.buf.timestamp_micros(), 293 Some(Nanos) => self.buf.timestamp_nanos(), 294 }; 295 296 self.write_header_value(ts) 297 } 298 #[cfg(not(feature = "humantime"))] 299 { 300 // Trick the compiler to think we have used self.timestamp 301 // Workaround for "field is never used: `timestamp`" compiler nag. 302 let _ = self.timestamp; 303 Ok(()) 304 } 305 } 306 write_module_pathnull307 fn write_module_path(&mut self, record: &Record) -> io::Result<()> { 308 if !self.module_path { 309 return Ok(()); 310 } 311 312 if let Some(module_path) = record.module_path() { 313 self.write_header_value(module_path) 314 } else { 315 Ok(()) 316 } 317 } 318 write_targetnull319 fn write_target(&mut self, record: &Record) -> io::Result<()> { 320 if !self.target { 321 return Ok(()); 322 } 323 324 match record.target() { 325 "" => Ok(()), 326 target => self.write_header_value(target), 327 } 328 } 329 finish_headernull330 fn finish_header(&mut self) -> io::Result<()> { 331 if self.written_header_value { 332 let close_brace = self.subtle_style("]"); 333 write!(self.buf, "{} ", close_brace) 334 } else { 335 Ok(()) 336 } 337 } 338 write_argsnull339 fn write_args(&mut self, record: &Record) -> io::Result<()> { 340 match self.indent { 341 // Fast path for no indentation 342 None => write!(self.buf, "{}{}", record.args(), self.suffix), 343 344 Some(indent_count) => { 345 // Create a wrapper around the buffer only if we have to actually indent the message 346 347 struct IndentWrapper<'a, 'b: 'a> { 348 fmt: &'a mut DefaultFormat<'b>, 349 indent_count: usize, 350 } 351 352 impl<'a, 'b> Write for IndentWrapper<'a, 'b> { 353 fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 354 let mut first = true; 355 for chunk in buf.split(|&x| x == b'\n') { 356 if !first { 357 write!( 358 self.fmt.buf, 359 "{}{:width$}", 360 self.fmt.suffix, 361 "", 362 width = self.indent_count 363 )?; 364 } 365 self.fmt.buf.write_all(chunk)?; 366 first = false; 367 } 368 369 Ok(buf.len()) 370 } 371 372 fn flush(&mut self) -> io::Result<()> { 373 self.fmt.buf.flush() 374 } 375 } 376 377 // The explicit scope here is just to make older versions of Rust happy 378 { 379 let mut wrapper = IndentWrapper { 380 fmt: self, 381 indent_count, 382 }; 383 write!(wrapper, "{}", record.args())?; 384 } 385 386 write!(self.buf, "{}", self.suffix)?; 387 388 Ok(()) 389 } 390 } 391 } 392 } 393 394 #[cfg(test)] 395 mod tests { 396 use super::*; 397 398 use log::{Level, Record}; 399 write_recordnull400 fn write_record(record: Record, fmt: DefaultFormat) -> String { 401 let buf = fmt.buf.buf.clone(); 402 403 fmt.write(&record).expect("failed to write record"); 404 405 let buf = buf.borrow(); 406 String::from_utf8(buf.bytes().to_vec()).expect("failed to read record") 407 } 408 write_targetnull409 fn write_target(target: &str, fmt: DefaultFormat) -> String { 410 write_record( 411 Record::builder() 412 .args(format_args!("log\nmessage")) 413 .level(Level::Info) 414 .file(Some("test.rs")) 415 .line(Some(144)) 416 .module_path(Some("test::path")) 417 .target(target) 418 .build(), 419 fmt, 420 ) 421 } 422 writenull423 fn write(fmt: DefaultFormat) -> String { 424 write_target("", fmt) 425 } 426 427 #[test] format_with_headernull428 fn format_with_header() { 429 let writer = writer::Builder::new() 430 .write_style(WriteStyle::Never) 431 .build(); 432 433 let mut f = Formatter::new(&writer); 434 435 let written = write(DefaultFormat { 436 timestamp: None, 437 module_path: true, 438 target: false, 439 level: true, 440 written_header_value: false, 441 indent: None, 442 suffix: "\n", 443 buf: &mut f, 444 }); 445 446 assert_eq!("[INFO test::path] log\nmessage\n", written); 447 } 448 449 #[test] format_no_headernull450 fn format_no_header() { 451 let writer = writer::Builder::new() 452 .write_style(WriteStyle::Never) 453 .build(); 454 455 let mut f = Formatter::new(&writer); 456 457 let written = write(DefaultFormat { 458 timestamp: None, 459 module_path: false, 460 target: false, 461 level: false, 462 written_header_value: false, 463 indent: None, 464 suffix: "\n", 465 buf: &mut f, 466 }); 467 468 assert_eq!("log\nmessage\n", written); 469 } 470 471 #[test] format_indent_spacesnull472 fn format_indent_spaces() { 473 let writer = writer::Builder::new() 474 .write_style(WriteStyle::Never) 475 .build(); 476 477 let mut f = Formatter::new(&writer); 478 479 let written = write(DefaultFormat { 480 timestamp: None, 481 module_path: true, 482 target: false, 483 level: true, 484 written_header_value: false, 485 indent: Some(4), 486 suffix: "\n", 487 buf: &mut f, 488 }); 489 490 assert_eq!("[INFO test::path] log\n message\n", written); 491 } 492 493 #[test] format_indent_zero_spacesnull494 fn format_indent_zero_spaces() { 495 let writer = writer::Builder::new() 496 .write_style(WriteStyle::Never) 497 .build(); 498 499 let mut f = Formatter::new(&writer); 500 501 let written = write(DefaultFormat { 502 timestamp: None, 503 module_path: true, 504 target: false, 505 level: true, 506 written_header_value: false, 507 indent: Some(0), 508 suffix: "\n", 509 buf: &mut f, 510 }); 511 512 assert_eq!("[INFO test::path] log\nmessage\n", written); 513 } 514 515 #[test] format_indent_spaces_no_headernull516 fn format_indent_spaces_no_header() { 517 let writer = writer::Builder::new() 518 .write_style(WriteStyle::Never) 519 .build(); 520 521 let mut f = Formatter::new(&writer); 522 523 let written = write(DefaultFormat { 524 timestamp: None, 525 module_path: false, 526 target: false, 527 level: false, 528 written_header_value: false, 529 indent: Some(4), 530 suffix: "\n", 531 buf: &mut f, 532 }); 533 534 assert_eq!("log\n message\n", written); 535 } 536 537 #[test] format_suffixnull538 fn format_suffix() { 539 let writer = writer::Builder::new() 540 .write_style(WriteStyle::Never) 541 .build(); 542 543 let mut f = Formatter::new(&writer); 544 545 let written = write(DefaultFormat { 546 timestamp: None, 547 module_path: false, 548 target: false, 549 level: false, 550 written_header_value: false, 551 indent: None, 552 suffix: "\n\n", 553 buf: &mut f, 554 }); 555 556 assert_eq!("log\nmessage\n\n", written); 557 } 558 559 #[test] format_suffix_with_indentnull560 fn format_suffix_with_indent() { 561 let writer = writer::Builder::new() 562 .write_style(WriteStyle::Never) 563 .build(); 564 565 let mut f = Formatter::new(&writer); 566 567 let written = write(DefaultFormat { 568 timestamp: None, 569 module_path: false, 570 target: false, 571 level: false, 572 written_header_value: false, 573 indent: Some(4), 574 suffix: "\n\n", 575 buf: &mut f, 576 }); 577 578 assert_eq!("log\n\n message\n\n", written); 579 } 580 581 #[test] format_targetnull582 fn format_target() { 583 let writer = writer::Builder::new() 584 .write_style(WriteStyle::Never) 585 .build(); 586 587 let mut f = Formatter::new(&writer); 588 589 let written = write_target( 590 "target", 591 DefaultFormat { 592 timestamp: None, 593 module_path: true, 594 target: true, 595 level: true, 596 written_header_value: false, 597 indent: None, 598 suffix: "\n", 599 buf: &mut f, 600 }, 601 ); 602 603 assert_eq!("[INFO test::path target] log\nmessage\n", written); 604 } 605 606 #[test] format_empty_targetnull607 fn format_empty_target() { 608 let writer = writer::Builder::new() 609 .write_style(WriteStyle::Never) 610 .build(); 611 612 let mut f = Formatter::new(&writer); 613 614 let written = write(DefaultFormat { 615 timestamp: None, 616 module_path: true, 617 target: true, 618 level: true, 619 written_header_value: false, 620 indent: None, 621 suffix: "\n", 622 buf: &mut f, 623 }); 624 625 assert_eq!("[INFO test::path] log\nmessage\n", written); 626 } 627 628 #[test] format_no_targetnull629 fn format_no_target() { 630 let writer = writer::Builder::new() 631 .write_style(WriteStyle::Never) 632 .build(); 633 634 let mut f = Formatter::new(&writer); 635 636 let written = write_target( 637 "target", 638 DefaultFormat { 639 timestamp: None, 640 module_path: true, 641 target: false, 642 level: true, 643 written_header_value: false, 644 indent: None, 645 suffix: "\n", 646 buf: &mut f, 647 }, 648 ); 649 650 assert_eq!("[INFO test::path] log\nmessage\n", written); 651 } 652 } 653