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
38use log::Record;
39
40mod humantime;
41pub(crate) mod writer;
42
43pub use self::humantime::glob::*;
44pub use self::writer::glob::*;
45
46use self::writer::{Buffer, Writer};
47
48pub(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)]
58pub 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.
70impl Default for TimestampPrecision {
71    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
97pub struct Formatter {
98    buf: Rc<RefCell<Buffer>>,
99    write_style: WriteStyle,
100}
101
102impl 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
123impl Write for Formatter {
124    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
125        self.buf.borrow_mut().write(buf)
126    }
127
128    fn flush(&mut self) -> io::Result<()> {
129        self.buf.borrow_mut().flush()
130    }
131}
132
133impl fmt::Debug for Formatter {
134    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135        f.debug_struct("Formatter").finish()
136    }
137}
138
139pub(crate) type FormatFn = Box<dyn Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send>;
140
141pub(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
152impl Default for Builder {
153    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
167impl 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.
173    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")]
206type SubtleStyle = StyledValue<'static, &'static str>;
207#[cfg(not(feature = "termcolor"))]
208type SubtleStyle = &'static str;
209
210/// The default format.
211///
212/// This format needs to work with any combination of crate features.
213struct 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
224impl<'a> DefaultFormat<'a> {
225    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
235    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
251    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
265    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
284    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
307    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
319    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
330    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
339    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)]
395mod tests {
396    use super::*;
397
398    use log::{Level, Record};
399
400    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
409    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
423    fn write(fmt: DefaultFormat) -> String {
424        write_target("", fmt)
425    }
426
427    #[test]
428    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]
450    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]
472    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]
494    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]
516    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]
538    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]
560    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]
582    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]
607    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]
629    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}