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 #[cfg(feature = "color")]
39 use log::Level;
40 use log::Record;
41 
42 #[cfg(feature = "humantime")]
43 mod humantime;
44 pub(crate) mod writer;
45 
46 #[cfg(feature = "color")]
47 mod style;
48 #[cfg(feature = "color")]
49 pub use style::{Color, Style, StyledValue};
50 
51 #[cfg(feature = "humantime")]
52 pub use self::humantime::Timestamp;
53 pub use self::writer::glob::*;
54 
55 use self::writer::{Buffer, Writer};
56 
57 pub(crate) mod glob {
58     pub use super::{Target, TimestampPrecision, WriteStyle};
59 }
60 
61 /// Formatting precision of timestamps.
62 ///
63 /// Seconds give precision of full seconds, milliseconds give thousands of a
64 /// second (3 decimal digits), microseconds are millionth of a second (6 decimal
65 /// digits) and nanoseconds are billionth of a second (9 decimal digits).
66 #[derive(Copy, Clone, Debug)]
67 pub enum TimestampPrecision {
68     /// Full second precision (0 decimal digits)
69     Seconds,
70     /// Millisecond precision (3 decimal digits)
71     Millis,
72     /// Microsecond precision (6 decimal digits)
73     Micros,
74     /// Nanosecond precision (9 decimal digits)
75     Nanos,
76 }
77 
78 /// The default timestamp precision is seconds.
79 impl Default for TimestampPrecision {
default() -> Self80     fn default() -> Self {
81         TimestampPrecision::Seconds
82     }
83 }
84 
85 /// A formatter to write logs into.
86 ///
87 /// `Formatter` implements the standard [`Write`] trait for writing log records.
88 /// It also supports terminal colors, through the [`style`] method.
89 ///
90 /// # Examples
91 ///
92 /// Use the [`writeln`] macro to format a log record.
93 /// An instance of a `Formatter` is passed to an `env_logger` format as `buf`:
94 ///
95 /// ```
96 /// use std::io::Write;
97 ///
98 /// let mut builder = env_logger::Builder::new();
99 ///
100 /// builder.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()));
101 /// ```
102 ///
103 /// [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
104 /// [`writeln`]: https://doc.rust-lang.org/stable/std/macro.writeln.html
105 /// [`style`]: #method.style
106 pub struct Formatter {
107     buf: Rc<RefCell<Buffer>>,
108     write_style: WriteStyle,
109 }
110 
111 impl Formatter {
new(writer: &Writer) -> Self112     pub(crate) fn new(writer: &Writer) -> Self {
113         Formatter {
114             buf: Rc::new(RefCell::new(writer.buffer())),
115             write_style: writer.write_style(),
116         }
117     }
118 
write_style(&self) -> WriteStyle119     pub(crate) fn write_style(&self) -> WriteStyle {
120         self.write_style
121     }
122 
print(&self, writer: &Writer) -> io::Result<()>123     pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> {
124         writer.print(&self.buf.borrow())
125     }
126 
clear(&mut self)127     pub(crate) fn clear(&mut self) {
128         self.buf.borrow_mut().clear()
129     }
130 }
131 
132 #[cfg(feature = "color")]
133 impl Formatter {
134     /// Begin a new [`Style`].
135     ///
136     /// # Examples
137     ///
138     /// Create a bold, red colored style and use it to print the log level:
139     ///
140     /// ```
141     /// use std::io::Write;
142     /// use env_logger::fmt::Color;
143     ///
144     /// let mut builder = env_logger::Builder::new();
145     ///
146     /// builder.format(|buf, record| {
147     ///     let mut level_style = buf.style();
148     ///
149     ///     level_style.set_color(Color::Red).set_bold(true);
150     ///
151     ///     writeln!(buf, "{}: {}",
152     ///         level_style.value(record.level()),
153     ///         record.args())
154     /// });
155     /// ```
156     ///
157     /// [`Style`]: struct.Style.html
style(&self) -> Style158     pub fn style(&self) -> Style {
159         Style {
160             buf: self.buf.clone(),
161             spec: termcolor::ColorSpec::new(),
162         }
163     }
164 
165     /// Get the default [`Style`] for the given level.
166     ///
167     /// The style can be used to print other values besides the level.
default_level_style(&self, level: Level) -> Style168     pub fn default_level_style(&self, level: Level) -> Style {
169         let mut level_style = self.style();
170         match level {
171             Level::Trace => level_style.set_color(Color::Cyan),
172             Level::Debug => level_style.set_color(Color::Blue),
173             Level::Info => level_style.set_color(Color::Green),
174             Level::Warn => level_style.set_color(Color::Yellow),
175             Level::Error => level_style.set_color(Color::Red).set_bold(true),
176         };
177         level_style
178     }
179 
180     /// Get a printable [`Style`] for the given level.
181     ///
182     /// The style can only be used to print the level.
default_styled_level(&self, level: Level) -> StyledValue<'static, Level>183     pub fn default_styled_level(&self, level: Level) -> StyledValue<'static, Level> {
184         self.default_level_style(level).into_value(level)
185     }
186 }
187 
188 impl Write for Formatter {
write(&mut self, buf: &[u8]) -> io::Result<usize>189     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
190         self.buf.borrow_mut().write(buf)
191     }
192 
flush(&mut self) -> io::Result<()>193     fn flush(&mut self) -> io::Result<()> {
194         self.buf.borrow_mut().flush()
195     }
196 }
197 
198 impl fmt::Debug for Formatter {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result199     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200         f.debug_struct("Formatter").finish()
201     }
202 }
203 
204 pub(crate) type FormatFn = Box<dyn Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send>;
205 
206 pub(crate) struct Builder {
207     pub format_timestamp: Option<TimestampPrecision>,
208     pub format_module_path: bool,
209     pub format_target: bool,
210     pub format_level: bool,
211     pub format_indent: Option<usize>,
212     pub custom_format: Option<FormatFn>,
213     pub format_suffix: &'static str,
214     built: bool,
215 }
216 
217 impl Builder {
218     /// Convert the format into a callable function.
219     ///
220     /// If the `custom_format` is `Some`, then any `default_format` switches are ignored.
221     /// If the `custom_format` is `None`, then a default format is returned.
222     /// Any `default_format` switches set to `false` won't be written by the format.
build(&mut self) -> FormatFn223     pub fn build(&mut self) -> FormatFn {
224         assert!(!self.built, "attempt to re-use consumed builder");
225 
226         let built = mem::replace(
227             self,
228             Builder {
229                 built: true,
230                 ..Default::default()
231             },
232         );
233 
234         if let Some(fmt) = built.custom_format {
235             fmt
236         } else {
237             Box::new(move |buf, record| {
238                 let fmt = DefaultFormat {
239                     timestamp: built.format_timestamp,
240                     module_path: built.format_module_path,
241                     target: built.format_target,
242                     level: built.format_level,
243                     written_header_value: false,
244                     indent: built.format_indent,
245                     suffix: built.format_suffix,
246                     buf,
247                 };
248 
249                 fmt.write(record)
250             })
251         }
252     }
253 }
254 
255 impl Default for Builder {
default() -> Self256     fn default() -> Self {
257         Builder {
258             format_timestamp: Some(Default::default()),
259             format_module_path: false,
260             format_target: true,
261             format_level: true,
262             format_indent: Some(4),
263             custom_format: None,
264             format_suffix: "\n",
265             built: false,
266         }
267     }
268 }
269 
270 #[cfg(feature = "color")]
271 type SubtleStyle = StyledValue<'static, &'static str>;
272 #[cfg(not(feature = "color"))]
273 type SubtleStyle = &'static str;
274 
275 /// The default format.
276 ///
277 /// This format needs to work with any combination of crate features.
278 struct DefaultFormat<'a> {
279     timestamp: Option<TimestampPrecision>,
280     module_path: bool,
281     target: bool,
282     level: bool,
283     written_header_value: bool,
284     indent: Option<usize>,
285     buf: &'a mut Formatter,
286     suffix: &'a str,
287 }
288 
289 impl<'a> DefaultFormat<'a> {
write(mut self, record: &Record) -> io::Result<()>290     fn write(mut self, record: &Record) -> io::Result<()> {
291         self.write_timestamp()?;
292         self.write_level(record)?;
293         self.write_module_path(record)?;
294         self.write_target(record)?;
295         self.finish_header()?;
296 
297         self.write_args(record)
298     }
299 
subtle_style(&self, text: &'static str) -> SubtleStyle300     fn subtle_style(&self, text: &'static str) -> SubtleStyle {
301         #[cfg(feature = "color")]
302         {
303             self.buf
304                 .style()
305                 .set_color(Color::Black)
306                 .set_intense(true)
307                 .clone()
308                 .into_value(text)
309         }
310         #[cfg(not(feature = "color"))]
311         {
312             text
313         }
314     }
315 
write_header_value<T>(&mut self, value: T) -> io::Result<()> where T: Display,316     fn write_header_value<T>(&mut self, value: T) -> io::Result<()>
317     where
318         T: Display,
319     {
320         if !self.written_header_value {
321             self.written_header_value = true;
322 
323             let open_brace = self.subtle_style("[");
324             write!(self.buf, "{}{}", open_brace, value)
325         } else {
326             write!(self.buf, " {}", value)
327         }
328     }
329 
write_level(&mut self, record: &Record) -> io::Result<()>330     fn write_level(&mut self, record: &Record) -> io::Result<()> {
331         if !self.level {
332             return Ok(());
333         }
334 
335         let level = {
336             #[cfg(feature = "color")]
337             {
338                 self.buf.default_styled_level(record.level())
339             }
340             #[cfg(not(feature = "color"))]
341             {
342                 record.level()
343             }
344         };
345 
346         self.write_header_value(format_args!("{:<5}", level))
347     }
348 
write_timestamp(&mut self) -> io::Result<()>349     fn write_timestamp(&mut self) -> io::Result<()> {
350         #[cfg(feature = "humantime")]
351         {
352             use self::TimestampPrecision::*;
353             let ts = match self.timestamp {
354                 None => return Ok(()),
355                 Some(Seconds) => self.buf.timestamp_seconds(),
356                 Some(Millis) => self.buf.timestamp_millis(),
357                 Some(Micros) => self.buf.timestamp_micros(),
358                 Some(Nanos) => self.buf.timestamp_nanos(),
359             };
360 
361             self.write_header_value(ts)
362         }
363         #[cfg(not(feature = "humantime"))]
364         {
365             // Trick the compiler to think we have used self.timestamp
366             // Workaround for "field is never used: `timestamp`" compiler nag.
367             let _ = self.timestamp;
368             Ok(())
369         }
370     }
371 
write_module_path(&mut self, record: &Record) -> io::Result<()>372     fn write_module_path(&mut self, record: &Record) -> io::Result<()> {
373         if !self.module_path {
374             return Ok(());
375         }
376 
377         if let Some(module_path) = record.module_path() {
378             self.write_header_value(module_path)
379         } else {
380             Ok(())
381         }
382     }
383 
write_target(&mut self, record: &Record) -> io::Result<()>384     fn write_target(&mut self, record: &Record) -> io::Result<()> {
385         if !self.target {
386             return Ok(());
387         }
388 
389         match record.target() {
390             "" => Ok(()),
391             target => self.write_header_value(target),
392         }
393     }
394 
finish_header(&mut self) -> io::Result<()>395     fn finish_header(&mut self) -> io::Result<()> {
396         if self.written_header_value {
397             let close_brace = self.subtle_style("]");
398             write!(self.buf, "{} ", close_brace)
399         } else {
400             Ok(())
401         }
402     }
403 
write_args(&mut self, record: &Record) -> io::Result<()>404     fn write_args(&mut self, record: &Record) -> io::Result<()> {
405         match self.indent {
406             // Fast path for no indentation
407             None => write!(self.buf, "{}{}", record.args(), self.suffix),
408 
409             Some(indent_count) => {
410                 // Create a wrapper around the buffer only if we have to actually indent the message
411 
412                 struct IndentWrapper<'a, 'b: 'a> {
413                     fmt: &'a mut DefaultFormat<'b>,
414                     indent_count: usize,
415                 }
416 
417                 impl<'a, 'b> Write for IndentWrapper<'a, 'b> {
418                     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
419                         let mut first = true;
420                         for chunk in buf.split(|&x| x == b'\n') {
421                             if !first {
422                                 write!(
423                                     self.fmt.buf,
424                                     "{}{:width$}",
425                                     self.fmt.suffix,
426                                     "",
427                                     width = self.indent_count
428                                 )?;
429                             }
430                             self.fmt.buf.write_all(chunk)?;
431                             first = false;
432                         }
433 
434                         Ok(buf.len())
435                     }
436 
437                     fn flush(&mut self) -> io::Result<()> {
438                         self.fmt.buf.flush()
439                     }
440                 }
441 
442                 // The explicit scope here is just to make older versions of Rust happy
443                 {
444                     let mut wrapper = IndentWrapper {
445                         fmt: self,
446                         indent_count,
447                     };
448                     write!(wrapper, "{}", record.args())?;
449                 }
450 
451                 write!(self.buf, "{}", self.suffix)?;
452 
453                 Ok(())
454             }
455         }
456     }
457 }
458 
459 #[cfg(test)]
460 mod tests {
461     use super::*;
462 
463     use log::{Level, Record};
464 
write_record(record: Record, fmt: DefaultFormat) -> String465     fn write_record(record: Record, fmt: DefaultFormat) -> String {
466         let buf = fmt.buf.buf.clone();
467 
468         fmt.write(&record).expect("failed to write record");
469 
470         let buf = buf.borrow();
471         String::from_utf8(buf.as_bytes().to_vec()).expect("failed to read record")
472     }
473 
write_target(target: &str, fmt: DefaultFormat) -> String474     fn write_target(target: &str, fmt: DefaultFormat) -> String {
475         write_record(
476             Record::builder()
477                 .args(format_args!("log\nmessage"))
478                 .level(Level::Info)
479                 .file(Some("test.rs"))
480                 .line(Some(144))
481                 .module_path(Some("test::path"))
482                 .target(target)
483                 .build(),
484             fmt,
485         )
486     }
487 
write(fmt: DefaultFormat) -> String488     fn write(fmt: DefaultFormat) -> String {
489         write_target("", fmt)
490     }
491 
492     #[test]
format_with_header()493     fn format_with_header() {
494         let writer = writer::Builder::new()
495             .write_style(WriteStyle::Never)
496             .build();
497 
498         let mut f = Formatter::new(&writer);
499 
500         let written = write(DefaultFormat {
501             timestamp: None,
502             module_path: true,
503             target: false,
504             level: true,
505             written_header_value: false,
506             indent: None,
507             suffix: "\n",
508             buf: &mut f,
509         });
510 
511         assert_eq!("[INFO  test::path] log\nmessage\n", written);
512     }
513 
514     #[test]
format_no_header()515     fn format_no_header() {
516         let writer = writer::Builder::new()
517             .write_style(WriteStyle::Never)
518             .build();
519 
520         let mut f = Formatter::new(&writer);
521 
522         let written = write(DefaultFormat {
523             timestamp: None,
524             module_path: false,
525             target: false,
526             level: false,
527             written_header_value: false,
528             indent: None,
529             suffix: "\n",
530             buf: &mut f,
531         });
532 
533         assert_eq!("log\nmessage\n", written);
534     }
535 
536     #[test]
format_indent_spaces()537     fn format_indent_spaces() {
538         let writer = writer::Builder::new()
539             .write_style(WriteStyle::Never)
540             .build();
541 
542         let mut f = Formatter::new(&writer);
543 
544         let written = write(DefaultFormat {
545             timestamp: None,
546             module_path: true,
547             target: false,
548             level: true,
549             written_header_value: false,
550             indent: Some(4),
551             suffix: "\n",
552             buf: &mut f,
553         });
554 
555         assert_eq!("[INFO  test::path] log\n    message\n", written);
556     }
557 
558     #[test]
format_indent_zero_spaces()559     fn format_indent_zero_spaces() {
560         let writer = writer::Builder::new()
561             .write_style(WriteStyle::Never)
562             .build();
563 
564         let mut f = Formatter::new(&writer);
565 
566         let written = write(DefaultFormat {
567             timestamp: None,
568             module_path: true,
569             target: false,
570             level: true,
571             written_header_value: false,
572             indent: Some(0),
573             suffix: "\n",
574             buf: &mut f,
575         });
576 
577         assert_eq!("[INFO  test::path] log\nmessage\n", written);
578     }
579 
580     #[test]
format_indent_spaces_no_header()581     fn format_indent_spaces_no_header() {
582         let writer = writer::Builder::new()
583             .write_style(WriteStyle::Never)
584             .build();
585 
586         let mut f = Formatter::new(&writer);
587 
588         let written = write(DefaultFormat {
589             timestamp: None,
590             module_path: false,
591             target: false,
592             level: false,
593             written_header_value: false,
594             indent: Some(4),
595             suffix: "\n",
596             buf: &mut f,
597         });
598 
599         assert_eq!("log\n    message\n", written);
600     }
601 
602     #[test]
format_suffix()603     fn format_suffix() {
604         let writer = writer::Builder::new()
605             .write_style(WriteStyle::Never)
606             .build();
607 
608         let mut f = Formatter::new(&writer);
609 
610         let written = write(DefaultFormat {
611             timestamp: None,
612             module_path: false,
613             target: false,
614             level: false,
615             written_header_value: false,
616             indent: None,
617             suffix: "\n\n",
618             buf: &mut f,
619         });
620 
621         assert_eq!("log\nmessage\n\n", written);
622     }
623 
624     #[test]
format_suffix_with_indent()625     fn format_suffix_with_indent() {
626         let writer = writer::Builder::new()
627             .write_style(WriteStyle::Never)
628             .build();
629 
630         let mut f = Formatter::new(&writer);
631 
632         let written = write(DefaultFormat {
633             timestamp: None,
634             module_path: false,
635             target: false,
636             level: false,
637             written_header_value: false,
638             indent: Some(4),
639             suffix: "\n\n",
640             buf: &mut f,
641         });
642 
643         assert_eq!("log\n\n    message\n\n", written);
644     }
645 
646     #[test]
format_target()647     fn format_target() {
648         let writer = writer::Builder::new()
649             .write_style(WriteStyle::Never)
650             .build();
651 
652         let mut f = Formatter::new(&writer);
653 
654         let written = write_target(
655             "target",
656             DefaultFormat {
657                 timestamp: None,
658                 module_path: true,
659                 target: true,
660                 level: true,
661                 written_header_value: false,
662                 indent: None,
663                 suffix: "\n",
664                 buf: &mut f,
665             },
666         );
667 
668         assert_eq!("[INFO  test::path target] log\nmessage\n", written);
669     }
670 
671     #[test]
format_empty_target()672     fn format_empty_target() {
673         let writer = writer::Builder::new()
674             .write_style(WriteStyle::Never)
675             .build();
676 
677         let mut f = Formatter::new(&writer);
678 
679         let written = write(DefaultFormat {
680             timestamp: None,
681             module_path: true,
682             target: true,
683             level: true,
684             written_header_value: false,
685             indent: None,
686             suffix: "\n",
687             buf: &mut f,
688         });
689 
690         assert_eq!("[INFO  test::path] log\nmessage\n", written);
691     }
692 
693     #[test]
format_no_target()694     fn format_no_target() {
695         let writer = writer::Builder::new()
696             .write_style(WriteStyle::Never)
697             .build();
698 
699         let mut f = Formatter::new(&writer);
700 
701         let written = write_target(
702             "target",
703             DefaultFormat {
704                 timestamp: None,
705                 module_path: true,
706                 target: false,
707                 level: true,
708                 written_header_value: false,
709                 indent: None,
710                 suffix: "\n",
711                 buf: &mut f,
712             },
713         );
714 
715         assert_eq!("[INFO  test::path] log\nmessage\n", written);
716     }
717 }
718