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