env_logger/fmt/
mod.rs

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
32use std::cell::RefCell;
33use std::fmt::Display;
34use std::io::prelude::*;
35use std::rc::Rc;
36use std::{fmt, io, mem};
37
38#[cfg(feature = "color")]
39use log::Level;
40use log::Record;
41
42#[cfg(feature = "humantime")]
43mod humantime;
44pub(crate) mod writer;
45
46#[cfg(feature = "color")]
47mod style;
48#[cfg(feature = "color")]
49pub use style::{Color, Style, StyledValue};
50
51#[cfg(feature = "humantime")]
52pub use self::humantime::Timestamp;
53pub use self::writer::glob::*;
54
55use self::writer::{Buffer, Writer};
56
57pub(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)]
67pub 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.
79impl Default for TimestampPrecision {
80    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
106pub struct Formatter {
107    buf: Rc<RefCell<Buffer>>,
108    write_style: WriteStyle,
109}
110
111impl Formatter {
112    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
119    pub(crate) fn write_style(&self) -> WriteStyle {
120        self.write_style
121    }
122
123    pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> {
124        writer.print(&self.buf.borrow())
125    }
126
127    pub(crate) fn clear(&mut self) {
128        self.buf.borrow_mut().clear()
129    }
130}
131
132#[cfg(feature = "color")]
133impl 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
158    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.
168    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.
183    pub fn default_styled_level(&self, level: Level) -> StyledValue<'static, Level> {
184        self.default_level_style(level).into_value(level)
185    }
186}
187
188impl Write for Formatter {
189    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
190        self.buf.borrow_mut().write(buf)
191    }
192
193    fn flush(&mut self) -> io::Result<()> {
194        self.buf.borrow_mut().flush()
195    }
196}
197
198impl fmt::Debug for Formatter {
199    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200        f.debug_struct("Formatter").finish()
201    }
202}
203
204pub(crate) type FormatFn = Box<dyn Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send>;
205
206pub(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
217impl 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.
223    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
255impl Default for Builder {
256    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")]
271type SubtleStyle = StyledValue<'static, &'static str>;
272#[cfg(not(feature = "color"))]
273type SubtleStyle = &'static str;
274
275/// The default format.
276///
277/// This format needs to work with any combination of crate features.
278struct 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
289impl<'a> DefaultFormat<'a> {
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
300    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
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
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
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
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
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
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
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)]
460mod tests {
461    use super::*;
462
463    use log::{Level, Record};
464
465    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
474    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
488    fn write(fmt: DefaultFormat) -> String {
489        write_target("", fmt)
490    }
491
492    #[test]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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}