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