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