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