Coverage Report

Created: 2024-09-10 12:50

/build/cargo-vendor-dir/env_logger-0.11.2/src/fmt/mod.rs
Line
Count
Source (jump to first uncovered line)
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
//!
13
//! Terminal styling is done through ANSI escape codes and will be adapted to the capabilities of
14
//! the target stream.
15
//! For example, you could use one of:
16
//! - [anstyle](https://docs.rs/anstyle) is a minimal, runtime string styling API and is re-exported as [`style`]
17
//! - [owo-colors](https://docs.rs/owo-colors) is a feature rich runtime string styling API
18
//! - [color-print](https://docs.rs/color-print) for feature-rich compile-time styling API
19
//! See also [`Formatter::default_level_style`]
20
//!
21
//! ```
22
//! use std::io::Write;
23
//!
24
//! let mut builder = env_logger::Builder::new();
25
//!
26
//! builder.format(|buf, record| {
27
//!     writeln!(buf, "{}: {}",
28
//!         record.level(),
29
//!         record.args())
30
//! });
31
//! ```
32
//!
33
//! [`Builder::format`]: crate::Builder::format
34
//! [`Write`]: std::io::Write
35
36
use std::cell::RefCell;
37
use std::fmt::Display;
38
use std::io::prelude::*;
39
use std::rc::Rc;
40
use std::{fmt, io, mem};
41
42
#[cfg(feature = "color")]
43
use log::Level;
44
use log::Record;
45
46
#[cfg(feature = "humantime")]
47
mod humantime;
48
pub(crate) mod writer;
49
50
#[cfg(feature = "color")]
51
pub use anstyle as style;
52
53
#[cfg(feature = "humantime")]
54
pub use self::humantime::Timestamp;
55
pub use self::writer::Target;
56
pub use self::writer::WriteStyle;
57
58
use self::writer::{Buffer, Writer};
59
60
/// Formatting precision of timestamps.
61
///
62
/// Seconds give precision of full seconds, milliseconds give thousands of a
63
/// second (3 decimal digits), microseconds are millionth of a second (6 decimal
64
/// digits) and nanoseconds are billionth of a second (9 decimal digits).
65
#[derive(Copy, Clone, Debug)]
66
pub enum TimestampPrecision {
67
    /// Full second precision (0 decimal digits)
68
    Seconds,
69
    /// Millisecond precision (3 decimal digits)
70
    Millis,
71
    /// Microsecond precision (6 decimal digits)
72
    Micros,
73
    /// Nanosecond precision (9 decimal digits)
74
    Nanos,
75
}
76
77
/// The default timestamp precision is seconds.
78
impl Default for TimestampPrecision {
79
342
    fn default() -> Self {
80
342
        TimestampPrecision::Seconds
81
342
    }
82
}
83
84
/// A formatter to write logs into.
85
///
86
/// `Formatter` implements the standard [`Write`] trait for writing log records.
87
/// It also supports terminal styling using ANSI escape codes.
88
///
89
/// # Examples
90
///
91
/// Use the [`writeln`] macro to format a log record.
92
/// An instance of a `Formatter` is passed to an `env_logger` format as `buf`:
93
///
94
/// ```
95
/// use std::io::Write;
96
///
97
/// let mut builder = env_logger::Builder::new();
98
///
99
/// builder.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()));
100
/// ```
101
///
102
/// [`Write`]: std::io::Write
103
/// [`writeln`]: std::writeln
104
pub struct Formatter {
105
    buf: Rc<RefCell<Buffer>>,
106
    write_style: WriteStyle,
107
}
108
109
impl Formatter {
110
170
    pub(crate) fn new(writer: &Writer) -> Self {
111
170
        Formatter {
112
170
            buf: Rc::new(RefCell::new(writer.buffer())),
113
170
            write_style: writer.write_style(),
114
170
        }
115
170
    }
116
117
4.93k
    pub(crate) fn write_style(&self) -> WriteStyle {
118
4.93k
        self.write_style
119
4.93k
    }
120
121
5.10k
    pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> {
122
5.10k
        writer.print(&self.buf.borrow())
123
5.10k
    }
124
125
5.10k
    pub(crate) fn clear(&mut self) {
126
5.10k
        self.buf.borrow_mut().clear()
127
5.10k
    }
128
}
129
130
#[cfg(feature = "color")]
131
impl Formatter {
132
    /// Get the default [`style::Style`] for the given level.
133
    ///
134
    /// The style can be used to print other values besides the level.
135
    ///
136
    /// See [`style`] for how to adapt it to the styling crate of your choice
137
    pub fn default_level_style(&self, level: Level) -> style::Style {
138
        if self.write_style == WriteStyle::Never {
139
            style::Style::new()
140
        } else {
141
            match level {
142
                Level::Trace => style::AnsiColor::Cyan.on_default(),
143
                Level::Debug => style::AnsiColor::Blue.on_default(),
144
                Level::Info => style::AnsiColor::Green.on_default(),
145
                Level::Warn => style::AnsiColor::Yellow.on_default(),
146
                Level::Error => style::AnsiColor::Red
147
                    .on_default()
148
                    .effects(style::Effects::BOLD),
149
            }
150
        }
151
    }
152
}
153
154
impl Write for Formatter {
155
77.5k
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
156
77.5k
        self.buf.borrow_mut().write(buf)
157
77.5k
    }
158
159
0
    fn flush(&mut self) -> io::Result<()> {
160
0
        self.buf.borrow_mut().flush()
161
0
    }
162
}
163
164
impl fmt::Debug for Formatter {
165
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
166
0
        let buf = self.buf.borrow();
167
0
        f.debug_struct("Formatter")
168
0
            .field("buf", &buf)
169
0
            .field("write_style", &self.write_style)
170
0
            .finish()
171
0
    }
172
}
173
174
pub(crate) type FormatFn = Box<dyn Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send>;
175
176
pub(crate) struct Builder {
177
    pub format_timestamp: Option<TimestampPrecision>,
178
    pub format_module_path: bool,
179
    pub format_target: bool,
180
    pub format_level: bool,
181
    pub format_indent: Option<usize>,
182
    pub custom_format: Option<FormatFn>,
183
    pub format_suffix: &'static str,
184
    built: bool,
185
}
186
187
impl Builder {
188
    /// Convert the format into a callable function.
189
    ///
190
    /// If the `custom_format` is `Some`, then any `default_format` switches are ignored.
191
    /// If the `custom_format` is `None`, then a default format is returned.
192
    /// Any `default_format` switches set to `false` won't be written by the format.
193
171
    pub fn build(&mut self) -> FormatFn {
194
171
        assert!(!self.built, 
"attempt to re-use consumed builder"0
);
195
196
171
        let built = mem::replace(
197
171
            self,
198
171
            Builder {
199
171
                built: true,
200
171
                ..Default::default()
201
171
            },
202
171
        );
203
204
171
        if let Some(
fmt0
) = built.custom_format {
205
0
            fmt
206
        } else {
207
5.10k
            
Box::new(171
move |buf, record| {
208
5.10k
                let fmt = DefaultFormat {
209
5.10k
                    timestamp: built.format_timestamp,
210
5.10k
                    module_path: built.format_module_path,
211
5.10k
                    target: built.format_target,
212
5.10k
                    level: built.format_level,
213
5.10k
                    written_header_value: false,
214
5.10k
                    indent: built.format_indent,
215
5.10k
                    suffix: built.format_suffix,
216
5.10k
                    buf,
217
5.10k
                };
218
5.10k
219
5.10k
                fmt.write(record)
220
5.10k
            }
)171
221
        }
222
171
    }
223
}
224
225
impl Default for Builder {
226
342
    fn default() -> Self {
227
342
        Builder {
228
342
            format_timestamp: Some(Default::default()),
229
342
            format_module_path: false,
230
342
            format_target: true,
231
342
            format_level: true,
232
342
            format_indent: Some(4),
233
342
            custom_format: None,
234
342
            format_suffix: "\n",
235
342
            built: false,
236
342
        }
237
342
    }
238
}
239
240
#[cfg(feature = "color")]
241
type SubtleStyle = StyledValue<&'static str>;
242
#[cfg(not(feature = "color"))]
243
type SubtleStyle = &'static str;
244
245
/// A value that can be printed using the given styles.
246
#[cfg(feature = "color")]
247
struct StyledValue<T> {
248
    style: style::Style,
249
    value: T,
250
}
251
252
#[cfg(feature = "color")]
253
impl<T: std::fmt::Display> std::fmt::Display for StyledValue<T> {
254
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
255
        let style = self.style;
256
257
        // We need to make sure `f`s settings don't get passed onto the styling but do get passed
258
        // to the value
259
        write!(f, "{style}")?;
260
        self.value.fmt(f)?;
261
        write!(f, "{style:#}")?;
262
        Ok(())
263
    }
264
}
265
266
/// The default format.
267
///
268
/// This format needs to work with any combination of crate features.
269
struct DefaultFormat<'a> {
270
    timestamp: Option<TimestampPrecision>,
271
    module_path: bool,
272
    target: bool,
273
    level: bool,
274
    written_header_value: bool,
275
    indent: Option<usize>,
276
    buf: &'a mut Formatter,
277
    suffix: &'a str,
278
}
279
280
impl<'a> DefaultFormat<'a> {
281
5.10k
    fn write(mut self, record: &Record) -> io::Result<()> {
282
5.10k
        self.write_timestamp()
?0
;
283
5.10k
        self.write_level(record)
?0
;
284
5.10k
        self.write_module_path(record)
?0
;
285
5.10k
        self.write_target(record)
?0
;
286
5.10k
        self.finish_header()
?0
;
287
288
5.10k
        self.write_args(record)
289
5.10k
    }
290
291
10.2k
    fn subtle_style(&self, text: &'static str) -> SubtleStyle {
292
10.2k
        #[cfg(feature = "color")]
293
10.2k
        {
294
10.2k
            StyledValue {
295
10.2k
                style: if self.buf.write_style == WriteStyle::Never {
296
10.2k
                    style::Style::new()
297
10.2k
                } else {
298
10.2k
                    style::AnsiColor::BrightBlack.on_default()
299
10.2k
                },
300
10.2k
                value: text,
301
10.2k
            }
302
10.2k
        }
303
10.2k
        #[cfg(not(feature = "color"))]
304
10.2k
        {
305
10.2k
            text
306
10.2k
        }
307
10.2k
    }
308
309
10.2k
    fn write_header_value<T>(&mut self, value: T) -> io::Result<()>
310
10.2k
    where
311
10.2k
        T: Display,
312
10.2k
    {
313
10.2k
        if !self.written_header_value {
314
5.10k
            self.written_header_value = true;
315
5.10k
316
5.10k
            let open_brace = self.subtle_style("[");
317
5.10k
            write!(self.buf, "{}{}", open_brace, value)
318
        } else {
319
5.10k
            write!(self.buf, " {}", value)
320
        }
321
10.2k
    }
322
323
5.10k
    fn write_level(&mut self, record: &Record) -> io::Result<()> {
324
5.10k
        if !self.level {
325
0
            return Ok(());
326
5.10k
        }
327
5.10k
328
5.10k
        let level = {
329
5.10k
            let level = record.level();
330
5.10k
            #[cfg(feature = "color")]
331
5.10k
            {
332
5.10k
                StyledValue {
333
5.10k
                    style: self.buf.default_level_style(level),
334
5.10k
                    value: level,
335
5.10k
                }
336
5.10k
            }
337
5.10k
            #[cfg(not(feature = "color"))]
338
5.10k
            {
339
5.10k
                level
340
5.10k
            }
341
5.10k
        };
342
5.10k
343
5.10k
        self.write_header_value(format_args!("{:<5}", level))
344
5.10k
    }
345
346
5.10k
    fn write_timestamp(&mut self) -> io::Result<()> {
347
5.10k
        #[cfg(feature = "humantime")]
348
5.10k
        {
349
5.10k
            use self::TimestampPrecision::*;
350
5.10k
            let ts = match self.timestamp {
351
5.10k
                None => return Ok(()),
352
5.10k
                Some(Seconds) => self.buf.timestamp_seconds(),
353
5.10k
                Some(Millis) => self.buf.timestamp_millis(),
354
5.10k
                Some(Micros) => self.buf.timestamp_micros(),
355
5.10k
                Some(Nanos) => self.buf.timestamp_nanos(),
356
5.10k
            };
357
5.10k
358
5.10k
            self.write_header_value(ts)
359
5.10k
        }
360
5.10k
        #[cfg(not(feature = "humantime"))]
361
5.10k
        {
362
5.10k
            // Trick the compiler to think we have used self.timestamp
363
5.10k
            // Workaround for "field is never used: `timestamp`" compiler nag.
364
5.10k
            let _ = self.timestamp;
365
5.10k
            Ok(())
366
5.10k
        }
367
5.10k
    }
368
369
5.10k
    fn write_module_path(&mut self, record: &Record) -> io::Result<()> {
370
5.10k
        if !self.module_path {
371
5.10k
            return Ok(());
372
0
        }
373
374
0
        if let Some(module_path) = record.module_path() {
375
0
            self.write_header_value(module_path)
376
        } else {
377
0
            Ok(())
378
        }
379
5.10k
    }
380
381
5.10k
    fn write_target(&mut self, record: &Record) -> io::Result<()> {
382
5.10k
        if !self.target {
383
0
            return Ok(());
384
5.10k
        }
385
5.10k
386
5.10k
        match record.target() {
387
5.10k
            "" => 
Ok(())0
,
388
5.10k
            target => self.write_header_value(target),
389
        }
390
5.10k
    }
391
392
5.10k
    fn finish_header(&mut self) -> io::Result<()> {
393
5.10k
        if self.written_header_value {
394
5.10k
            let close_brace = self.subtle_style("]");
395
5.10k
            write!(self.buf, "{} ", close_brace)
396
        } else {
397
0
            Ok(())
398
        }
399
5.10k
    }
400
401
5.10k
    fn write_args(&mut self, record: &Record) -> io::Result<()> {
402
5.10k
        match self.indent {
403
            // Fast path for no indentation
404
0
            None => write!(self.buf, "{}{}", record.args(), self.suffix),
405
406
5.10k
            Some(indent_count) => {
407
5.10k
                // Create a wrapper around the buffer only if we have to actually indent the message
408
5.10k
409
5.10k
                struct IndentWrapper<'a, 'b: 'a> {
410
5.10k
                    fmt: &'a mut DefaultFormat<'b>,
411
5.10k
                    indent_count: usize,
412
5.10k
                }
413
5.10k
414
5.10k
                impl<'a, 'b> Write for IndentWrapper<'a, 'b> {
415
41.8k
                    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
416
41.8k
                        let mut first = true;
417
216k
                        for 
chunk41.8k
in
buf.split(41.8k
|&x| x == b'\n'
)41.8k
{
418
41.8k
                            if !first {
419
5.10k
                                write!(
420
0
                                    self.fmt.buf,
421
0
                                    "{}{:width$}",
422
0
                                    self.fmt.suffix,
423
0
                                    "",
424
0
                                    width = self.indent_count
425
0
                                )?;
426
41.8k
                            }
427
41.8k
                            self.fmt.buf.write_all(chunk)
?0
;
428
41.8k
                            first = false;
429
5.10k
                        }
430
5.10k
431
41.8k
                        Ok(buf.len())
432
41.8k
                    }
433
5.10k
434
5.10k
                    fn flush(&mut self) -> io::Result<()> {
435
0
                        self.fmt.buf.flush()
436
0
                    }
437
5.10k
                }
438
5.10k
439
5.10k
                // The explicit scope here is just to make older versions of Rust happy
440
5.10k
                {
441
5.10k
                    let mut wrapper = IndentWrapper {
442
5.10k
                        fmt: self,
443
5.10k
                        indent_count,
444
5.10k
                    };
445
5.10k
                    write!(wrapper, "{}", record.args())
?0
;
446
                }
447
448
5.10k
                write!(self.buf, "{}", self.suffix)
?0
;
449
450
5.10k
                Ok(())
451
            }
452
        }
453
5.10k
    }
454
}
455
456
#[cfg(test)]
457
mod tests {
458
    use super::*;
459
460
    use log::{Level, Record};
461
462
    fn write_record(record: Record, fmt: DefaultFormat) -> String {
463
        let buf = fmt.buf.buf.clone();
464
465
        fmt.write(&record).expect("failed to write record");
466
467
        let buf = buf.borrow();
468
        String::from_utf8(buf.as_bytes().to_vec()).expect("failed to read record")
469
    }
470
471
    fn write_target(target: &str, fmt: DefaultFormat) -> String {
472
        write_record(
473
            Record::builder()
474
                .args(format_args!("log\nmessage"))
475
                .level(Level::Info)
476
                .file(Some("test.rs"))
477
                .line(Some(144))
478
                .module_path(Some("test::path"))
479
                .target(target)
480
                .build(),
481
            fmt,
482
        )
483
    }
484
485
    fn write(fmt: DefaultFormat) -> String {
486
        write_target("", fmt)
487
    }
488
489
    #[test]
490
    fn format_with_header() {
491
        let writer = writer::Builder::new()
492
            .write_style(WriteStyle::Never)
493
            .build();
494
495
        let mut f = Formatter::new(&writer);
496
497
        let written = write(DefaultFormat {
498
            timestamp: None,
499
            module_path: true,
500
            target: false,
501
            level: true,
502
            written_header_value: false,
503
            indent: None,
504
            suffix: "\n",
505
            buf: &mut f,
506
        });
507
508
        assert_eq!("[INFO  test::path] log\nmessage\n", written);
509
    }
510
511
    #[test]
512
    fn format_no_header() {
513
        let writer = writer::Builder::new()
514
            .write_style(WriteStyle::Never)
515
            .build();
516
517
        let mut f = Formatter::new(&writer);
518
519
        let written = write(DefaultFormat {
520
            timestamp: None,
521
            module_path: false,
522
            target: false,
523
            level: false,
524
            written_header_value: false,
525
            indent: None,
526
            suffix: "\n",
527
            buf: &mut f,
528
        });
529
530
        assert_eq!("log\nmessage\n", written);
531
    }
532
533
    #[test]
534
    fn format_indent_spaces() {
535
        let writer = writer::Builder::new()
536
            .write_style(WriteStyle::Never)
537
            .build();
538
539
        let mut f = Formatter::new(&writer);
540
541
        let written = write(DefaultFormat {
542
            timestamp: None,
543
            module_path: true,
544
            target: false,
545
            level: true,
546
            written_header_value: false,
547
            indent: Some(4),
548
            suffix: "\n",
549
            buf: &mut f,
550
        });
551
552
        assert_eq!("[INFO  test::path] log\n    message\n", written);
553
    }
554
555
    #[test]
556
    fn format_indent_zero_spaces() {
557
        let writer = writer::Builder::new()
558
            .write_style(WriteStyle::Never)
559
            .build();
560
561
        let mut f = Formatter::new(&writer);
562
563
        let written = write(DefaultFormat {
564
            timestamp: None,
565
            module_path: true,
566
            target: false,
567
            level: true,
568
            written_header_value: false,
569
            indent: Some(0),
570
            suffix: "\n",
571
            buf: &mut f,
572
        });
573
574
        assert_eq!("[INFO  test::path] log\nmessage\n", written);
575
    }
576
577
    #[test]
578
    fn format_indent_spaces_no_header() {
579
        let writer = writer::Builder::new()
580
            .write_style(WriteStyle::Never)
581
            .build();
582
583
        let mut f = Formatter::new(&writer);
584
585
        let written = write(DefaultFormat {
586
            timestamp: None,
587
            module_path: false,
588
            target: false,
589
            level: false,
590
            written_header_value: false,
591
            indent: Some(4),
592
            suffix: "\n",
593
            buf: &mut f,
594
        });
595
596
        assert_eq!("log\n    message\n", written);
597
    }
598
599
    #[test]
600
    fn format_suffix() {
601
        let writer = writer::Builder::new()
602
            .write_style(WriteStyle::Never)
603
            .build();
604
605
        let mut f = Formatter::new(&writer);
606
607
        let written = write(DefaultFormat {
608
            timestamp: None,
609
            module_path: false,
610
            target: false,
611
            level: false,
612
            written_header_value: false,
613
            indent: None,
614
            suffix: "\n\n",
615
            buf: &mut f,
616
        });
617
618
        assert_eq!("log\nmessage\n\n", written);
619
    }
620
621
    #[test]
622
    fn format_suffix_with_indent() {
623
        let writer = writer::Builder::new()
624
            .write_style(WriteStyle::Never)
625
            .build();
626
627
        let mut f = Formatter::new(&writer);
628
629
        let written = write(DefaultFormat {
630
            timestamp: None,
631
            module_path: false,
632
            target: false,
633
            level: false,
634
            written_header_value: false,
635
            indent: Some(4),
636
            suffix: "\n\n",
637
            buf: &mut f,
638
        });
639
640
        assert_eq!("log\n\n    message\n\n", written);
641
    }
642
643
    #[test]
644
    fn format_target() {
645
        let writer = writer::Builder::new()
646
            .write_style(WriteStyle::Never)
647
            .build();
648
649
        let mut f = Formatter::new(&writer);
650
651
        let written = write_target(
652
            "target",
653
            DefaultFormat {
654
                timestamp: None,
655
                module_path: true,
656
                target: true,
657
                level: true,
658
                written_header_value: false,
659
                indent: None,
660
                suffix: "\n",
661
                buf: &mut f,
662
            },
663
        );
664
665
        assert_eq!("[INFO  test::path target] log\nmessage\n", written);
666
    }
667
668
    #[test]
669
    fn format_empty_target() {
670
        let writer = writer::Builder::new()
671
            .write_style(WriteStyle::Never)
672
            .build();
673
674
        let mut f = Formatter::new(&writer);
675
676
        let written = write(DefaultFormat {
677
            timestamp: None,
678
            module_path: true,
679
            target: true,
680
            level: true,
681
            written_header_value: false,
682
            indent: None,
683
            suffix: "\n",
684
            buf: &mut f,
685
        });
686
687
        assert_eq!("[INFO  test::path] log\nmessage\n", written);
688
    }
689
690
    #[test]
691
    fn format_no_target() {
692
        let writer = writer::Builder::new()
693
            .write_style(WriteStyle::Never)
694
            .build();
695
696
        let mut f = Formatter::new(&writer);
697
698
        let written = write_target(
699
            "target",
700
            DefaultFormat {
701
                timestamp: None,
702
                module_path: true,
703
                target: false,
704
                level: true,
705
                written_header_value: false,
706
                indent: None,
707
                suffix: "\n",
708
                buf: &mut f,
709
            },
710
        );
711
712
        assert_eq!("[INFO  test::path] log\nmessage\n", written);
713
    }
714
}