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 { default() -> Self71 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 { new(writer: &Writer) -> Self103 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 write_style(&self) -> WriteStyle110 pub(crate) fn write_style(&self) -> WriteStyle { 111 self.write_style 112 } 113 print(&self, writer: &Writer) -> io::Result<()>114 pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> { 115 writer.print(&self.buf.borrow()) 116 } 117 clear(&mut self)118 pub(crate) fn clear(&mut self) { 119 self.buf.borrow_mut().clear() 120 } 121 } 122 123 impl Write for Formatter { write(&mut self, buf: &[u8]) -> io::Result<usize>124 fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 125 self.buf.borrow_mut().write(buf) 126 } 127 flush(&mut self) -> io::Result<()>128 fn flush(&mut self) -> io::Result<()> { 129 self.buf.borrow_mut().flush() 130 } 131 } 132 133 impl fmt::Debug for Formatter { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result134 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_level: bool, 145 pub format_indent: Option<usize>, 146 pub custom_format: Option<FormatFn>, 147 pub format_suffix: &'static str, 148 built: bool, 149 } 150 151 impl Default for Builder { default() -> Self152 fn default() -> Self { 153 Builder { 154 format_timestamp: Some(Default::default()), 155 format_module_path: true, 156 format_level: true, 157 format_indent: Some(4), 158 custom_format: None, 159 format_suffix: "\n", 160 built: false, 161 } 162 } 163 } 164 165 impl Builder { 166 /// Convert the format into a callable function. 167 /// 168 /// If the `custom_format` is `Some`, then any `default_format` switches are ignored. 169 /// If the `custom_format` is `None`, then a default format is returned. 170 /// Any `default_format` switches set to `false` won't be written by the format. build(&mut self) -> FormatFn171 pub fn build(&mut self) -> FormatFn { 172 assert!(!self.built, "attempt to re-use consumed builder"); 173 174 let built = mem::replace( 175 self, 176 Builder { 177 built: true, 178 ..Default::default() 179 }, 180 ); 181 182 if let Some(fmt) = built.custom_format { 183 fmt 184 } else { 185 Box::new(move |buf, record| { 186 let fmt = DefaultFormat { 187 timestamp: built.format_timestamp, 188 module_path: built.format_module_path, 189 level: built.format_level, 190 written_header_value: false, 191 indent: built.format_indent, 192 suffix: built.format_suffix, 193 buf, 194 }; 195 196 fmt.write(record) 197 }) 198 } 199 } 200 } 201 202 #[cfg(feature = "termcolor")] 203 type SubtleStyle = StyledValue<'static, &'static str>; 204 #[cfg(not(feature = "termcolor"))] 205 type SubtleStyle = &'static str; 206 207 /// The default format. 208 /// 209 /// This format needs to work with any combination of crate features. 210 struct DefaultFormat<'a> { 211 timestamp: Option<TimestampPrecision>, 212 module_path: bool, 213 level: bool, 214 written_header_value: bool, 215 indent: Option<usize>, 216 buf: &'a mut Formatter, 217 suffix: &'a str, 218 } 219 220 impl<'a> DefaultFormat<'a> { write(mut self, record: &Record) -> io::Result<()>221 fn write(mut self, record: &Record) -> io::Result<()> { 222 self.write_timestamp()?; 223 self.write_level(record)?; 224 self.write_module_path(record)?; 225 self.finish_header()?; 226 227 self.write_args(record) 228 } 229 subtle_style(&self, text: &'static str) -> SubtleStyle230 fn subtle_style(&self, text: &'static str) -> SubtleStyle { 231 #[cfg(feature = "termcolor")] 232 { 233 self.buf 234 .style() 235 .set_color(Color::Black) 236 .set_intense(true) 237 .clone() 238 .into_value(text) 239 } 240 #[cfg(not(feature = "termcolor"))] 241 { 242 text 243 } 244 } 245 write_header_value<T>(&mut self, value: T) -> io::Result<()> where T: Display,246 fn write_header_value<T>(&mut self, value: T) -> io::Result<()> 247 where 248 T: Display, 249 { 250 if !self.written_header_value { 251 self.written_header_value = true; 252 253 let open_brace = self.subtle_style("["); 254 write!(self.buf, "{}{}", open_brace, value) 255 } else { 256 write!(self.buf, " {}", value) 257 } 258 } 259 write_level(&mut self, record: &Record) -> io::Result<()>260 fn write_level(&mut self, record: &Record) -> io::Result<()> { 261 if !self.level { 262 return Ok(()); 263 } 264 265 let level = { 266 #[cfg(feature = "termcolor")] 267 { 268 self.buf.default_styled_level(record.level()) 269 } 270 #[cfg(not(feature = "termcolor"))] 271 { 272 record.level() 273 } 274 }; 275 276 self.write_header_value(format_args!("{:<5}", level)) 277 } 278 write_timestamp(&mut self) -> io::Result<()>279 fn write_timestamp(&mut self) -> io::Result<()> { 280 #[cfg(feature = "humantime")] 281 { 282 use self::TimestampPrecision::*; 283 let ts = match self.timestamp { 284 None => return Ok(()), 285 Some(Seconds) => self.buf.timestamp_seconds(), 286 Some(Millis) => self.buf.timestamp_millis(), 287 Some(Micros) => self.buf.timestamp_micros(), 288 Some(Nanos) => self.buf.timestamp_nanos(), 289 }; 290 291 self.write_header_value(ts) 292 } 293 #[cfg(not(feature = "humantime"))] 294 { 295 // Trick the compiler to think we have used self.timestamp 296 // Workaround for "field is never used: `timestamp`" compiler nag. 297 let _ = self.timestamp; 298 Ok(()) 299 } 300 } 301 write_module_path(&mut self, record: &Record) -> io::Result<()>302 fn write_module_path(&mut self, record: &Record) -> io::Result<()> { 303 if !self.module_path { 304 return Ok(()); 305 } 306 307 if let Some(module_path) = record.module_path() { 308 self.write_header_value(module_path) 309 } else { 310 Ok(()) 311 } 312 } 313 finish_header(&mut self) -> io::Result<()>314 fn finish_header(&mut self) -> io::Result<()> { 315 if self.written_header_value { 316 let close_brace = self.subtle_style("]"); 317 write!(self.buf, "{} ", close_brace) 318 } else { 319 Ok(()) 320 } 321 } 322 write_args(&mut self, record: &Record) -> io::Result<()>323 fn write_args(&mut self, record: &Record) -> io::Result<()> { 324 match self.indent { 325 // Fast path for no indentation 326 None => write!(self.buf, "{}{}", record.args(), self.suffix), 327 328 Some(indent_count) => { 329 // Create a wrapper around the buffer only if we have to actually indent the message 330 331 struct IndentWrapper<'a, 'b: 'a> { 332 fmt: &'a mut DefaultFormat<'b>, 333 indent_count: usize, 334 } 335 336 impl<'a, 'b> Write for IndentWrapper<'a, 'b> { 337 fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 338 let mut first = true; 339 for chunk in buf.split(|&x| x == b'\n') { 340 if !first { 341 write!( 342 self.fmt.buf, 343 "{}{:width$}", 344 self.fmt.suffix, 345 "", 346 width = self.indent_count 347 )?; 348 } 349 self.fmt.buf.write_all(chunk)?; 350 first = false; 351 } 352 353 Ok(buf.len()) 354 } 355 356 fn flush(&mut self) -> io::Result<()> { 357 self.fmt.buf.flush() 358 } 359 } 360 361 // The explicit scope here is just to make older versions of Rust happy 362 { 363 let mut wrapper = IndentWrapper { 364 fmt: self, 365 indent_count, 366 }; 367 write!(wrapper, "{}", record.args())?; 368 } 369 370 write!(self.buf, "{}", self.suffix)?; 371 372 Ok(()) 373 } 374 } 375 } 376 } 377 378 #[cfg(test)] 379 mod tests { 380 use super::*; 381 382 use log::{Level, Record}; 383 write(fmt: DefaultFormat) -> String384 fn write(fmt: DefaultFormat) -> String { 385 let buf = fmt.buf.buf.clone(); 386 387 let record = Record::builder() 388 .args(format_args!("log\nmessage")) 389 .level(Level::Info) 390 .file(Some("test.rs")) 391 .line(Some(144)) 392 .module_path(Some("test::path")) 393 .build(); 394 395 fmt.write(&record).expect("failed to write record"); 396 397 let buf = buf.borrow(); 398 String::from_utf8(buf.bytes().to_vec()).expect("failed to read record") 399 } 400 401 #[test] format_with_header()402 fn format_with_header() { 403 let writer = writer::Builder::new() 404 .write_style(WriteStyle::Never) 405 .build(); 406 407 let mut f = Formatter::new(&writer); 408 409 let written = write(DefaultFormat { 410 timestamp: None, 411 module_path: true, 412 level: true, 413 written_header_value: false, 414 indent: None, 415 suffix: "\n", 416 buf: &mut f, 417 }); 418 419 assert_eq!("[INFO test::path] log\nmessage\n", written); 420 } 421 422 #[test] format_no_header()423 fn format_no_header() { 424 let writer = writer::Builder::new() 425 .write_style(WriteStyle::Never) 426 .build(); 427 428 let mut f = Formatter::new(&writer); 429 430 let written = write(DefaultFormat { 431 timestamp: None, 432 module_path: false, 433 level: false, 434 written_header_value: false, 435 indent: None, 436 suffix: "\n", 437 buf: &mut f, 438 }); 439 440 assert_eq!("log\nmessage\n", written); 441 } 442 443 #[test] format_indent_spaces()444 fn format_indent_spaces() { 445 let writer = writer::Builder::new() 446 .write_style(WriteStyle::Never) 447 .build(); 448 449 let mut f = Formatter::new(&writer); 450 451 let written = write(DefaultFormat { 452 timestamp: None, 453 module_path: true, 454 level: true, 455 written_header_value: false, 456 indent: Some(4), 457 suffix: "\n", 458 buf: &mut f, 459 }); 460 461 assert_eq!("[INFO test::path] log\n message\n", written); 462 } 463 464 #[test] format_indent_zero_spaces()465 fn format_indent_zero_spaces() { 466 let writer = writer::Builder::new() 467 .write_style(WriteStyle::Never) 468 .build(); 469 470 let mut f = Formatter::new(&writer); 471 472 let written = write(DefaultFormat { 473 timestamp: None, 474 module_path: true, 475 level: true, 476 written_header_value: false, 477 indent: Some(0), 478 suffix: "\n", 479 buf: &mut f, 480 }); 481 482 assert_eq!("[INFO test::path] log\nmessage\n", written); 483 } 484 485 #[test] format_indent_spaces_no_header()486 fn format_indent_spaces_no_header() { 487 let writer = writer::Builder::new() 488 .write_style(WriteStyle::Never) 489 .build(); 490 491 let mut f = Formatter::new(&writer); 492 493 let written = write(DefaultFormat { 494 timestamp: None, 495 module_path: false, 496 level: false, 497 written_header_value: false, 498 indent: Some(4), 499 suffix: "\n", 500 buf: &mut f, 501 }); 502 503 assert_eq!("log\n message\n", written); 504 } 505 506 #[test] format_suffix()507 fn format_suffix() { 508 let writer = writer::Builder::new() 509 .write_style(WriteStyle::Never) 510 .build(); 511 512 let mut f = Formatter::new(&writer); 513 514 let written = write(DefaultFormat { 515 timestamp: None, 516 module_path: false, 517 level: false, 518 written_header_value: false, 519 indent: None, 520 suffix: "\n\n", 521 buf: &mut f, 522 }); 523 524 assert_eq!("log\nmessage\n\n", written); 525 } 526 527 #[test] format_suffix_with_indent()528 fn format_suffix_with_indent() { 529 let writer = writer::Builder::new() 530 .write_style(WriteStyle::Never) 531 .build(); 532 533 let mut f = Formatter::new(&writer); 534 535 let written = write(DefaultFormat { 536 timestamp: None, 537 module_path: false, 538 level: false, 539 written_header_value: false, 540 indent: Some(4), 541 suffix: "\n\n", 542 buf: &mut f, 543 }); 544 545 assert_eq!("log\n\n message\n\n", written); 546 } 547 } 548