Coverage Report

Created: 2024-11-19 11:03

/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
470
    fn default() -> Self {
80
470
        TimestampPrecision::Seconds
81
470
    }
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
234
    pub(crate) fn new(writer: &Writer) -> Self {
111
234
        Formatter {
112
234
            buf: Rc::new(RefCell::new(writer.buffer())),
113
234
            write_style: writer.write_style(),
114
234
        }
115
234
    }
116
117
114k
    pub(crate) fn write_style(&self) -> WriteStyle {
118
114k
        self.write_style
119
114k
    }
120
121
115k
    pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> {
122
115k
        writer.print(&self.buf.borrow())
123
115k
    }
124
125
115k
    pub(crate) fn clear(&mut self) {
126
115k
        self.buf.borrow_mut().clear()
127
115k
    }
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
1.21M
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
156
1.21M
        self.buf.borrow_mut().write(buf)
157
1.21M
    }
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
235
    pub fn build(&mut self) -> FormatFn {
194
235
        assert!(!self.built, 
"attempt to re-use consumed builder"0
);
195
196
235
        let built = mem::replace(
197
235
            self,
198
235
            Builder {
199
235
                built: true,
200
235
                ..Default::default()
201
235
            },
202
235
        );
203
204
235
        if let Some(
fmt0
) = built.custom_format {
205
0
            fmt
206
        } else {
207
115k
            
Box::new(235
move |buf, record| {
208
115k
                let fmt = DefaultFormat {
209
115k
                    timestamp: built.format_timestamp,
210
115k
                    module_path: built.format_module_path,
211
115k
                    target: built.format_target,
212
115k
                    level: built.format_level,
213
115k
                    written_header_value: false,
214
115k
                    indent: built.format_indent,
215
115k
                    suffix: built.format_suffix,
216
115k
                    buf,
217
115k
                };
218
115k
219
115k
                fmt.write(record)
220
115k
            }
)235
221
        }
222
235
    }
223
}
224
225
impl Default for Builder {
226
470
    fn default() -> Self {
227
470
        Builder {
228
470
            format_timestamp: Some(Default::default()),
229
470
            format_module_path: false,
230
470
            format_target: true,
231
470
            format_level: true,
232
470
            format_indent: Some(4),
233
470
            custom_format: None,
234
470
            format_suffix: "\n",
235
470
            built: false,
236
470
        }
237
470
    }
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
115k
    fn write(mut self, record: &Record) -> io::Result<()> {
282
115k
        self.write_timestamp()
?0
;
283
115k
        self.write_level(record)
?0
;
284
115k
        self.write_module_path(record)
?0
;
285
115k
        self.write_target(record)
?0
;
286
115k
        self.finish_header()
?0
;
287
288
115k
        self.write_args(record)
289
115k
    }
290
291
230k
    fn subtle_style(&self, text: &'static str) -> SubtleStyle {
292
230k
        #[cfg(feature = "color")]
293
230k
        {
294
230k
            StyledValue {
295
230k
                style: if self.buf.write_style == WriteStyle::Never {
296
230k
                    style::Style::new()
297
230k
                } else {
298
230k
                    style::AnsiColor::BrightBlack.on_default()
299
230k
                },
300
230k
                value: text,
301
230k
            }
302
230k
        }
303
230k
        #[cfg(not(feature = "color"))]
304
230k
        {
305
230k
            text
306
230k
        }
307
230k
    }
308
309
230k
    fn write_header_value<T>(&mut self, value: T) -> io::Result<()>
310
230k
    where
311
230k
        T: Display,
312
230k
    {
313
230k
        if !self.written_header_value {
314
115k
            self.written_header_value = true;
315
115k
316
115k
            let open_brace = self.subtle_style("[");
317
115k
            write!(self.buf, "{}{}", open_brace, value)
318
        } else {
319
115k
            write!(self.buf, " {}", value)
320
        }
321
230k
    }
322
323
115k
    fn write_level(&mut self, record: &Record) -> io::Result<()> {
324
115k
        if !self.level {
325
0
            return Ok(());
326
115k
        }
327
115k
328
115k
        let level = {
329
115k
            let level = record.level();
330
115k
            #[cfg(feature = "color")]
331
115k
            {
332
115k
                StyledValue {
333
115k
                    style: self.buf.default_level_style(level),
334
115k
                    value: level,
335
115k
                }
336
115k
            }
337
115k
            #[cfg(not(feature = "color"))]
338
115k
            {
339
115k
                level
340
115k
            }
341
115k
        };
342
115k
343
115k
        self.write_header_value(format_args!("{:<5}", level))
344
115k
    }
345
346
115k
    fn write_timestamp(&mut self) -> io::Result<()> {
347
115k
        #[cfg(feature = "humantime")]
348
115k
        {
349
115k
            use self::TimestampPrecision::*;
350
115k
            let ts = match self.timestamp {
351
115k
                None => return Ok(()),
352
115k
                Some(Seconds) => self.buf.timestamp_seconds(),
353
115k
                Some(Millis) => self.buf.timestamp_millis(),
354
115k
                Some(Micros) => self.buf.timestamp_micros(),
355
115k
                Some(Nanos) => self.buf.timestamp_nanos(),
356
115k
            };
357
115k
358
115k
            self.write_header_value(ts)
359
115k
        }
360
115k
        #[cfg(not(feature = "humantime"))]
361
115k
        {
362
115k
            // Trick the compiler to think we have used self.timestamp
363
115k
            // Workaround for "field is never used: `timestamp`" compiler nag.
364
115k
            let _ = self.timestamp;
365
115k
            Ok(())
366
115k
        }
367
115k
    }
368
369
115k
    fn write_module_path(&mut self, record: &Record) -> io::Result<()> {
370
115k
        if !self.module_path {
371
115k
            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
115k
    }
380
381
115k
    fn write_target(&mut self, record: &Record) -> io::Result<()> {
382
115k
        if !self.target {
383
0
            return Ok(());
384
115k
        }
385
115k
386
115k
        match record.target() {
387
115k
            "" => 
Ok(())0
,
388
115k
            target => self.write_header_value(target),
389
        }
390
115k
    }
391
392
115k
    fn finish_header(&mut self) -> io::Result<()> {
393
115k
        if self.written_header_value {
394
115k
            let close_brace = self.subtle_style("]");
395
115k
            write!(self.buf, "{} ", close_brace)
396
        } else {
397
0
            Ok(())
398
        }
399
115k
    }
400
401
115k
    fn write_args(&mut self, record: &Record) -> io::Result<()> {
402
115k
        match self.indent {
403
            // Fast path for no indentation
404
0
            None => write!(self.buf, "{}{}", record.args(), self.suffix),
405
406
115k
            Some(indent_count) => {
407
115k
                // Create a wrapper around the buffer only if we have to actually indent the message
408
115k
409
115k
                struct IndentWrapper<'a, 'b: 'a> {
410
115k
                    fmt: &'a mut DefaultFormat<'b>,
411
115k
                    indent_count: usize,
412
115k
                }
413
115k
414
115k
                impl<'a, 'b> Write for IndentWrapper<'a, 'b> {
415
411k
                    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
416
411k
                        let mut first = true;
417
4.18M
                        for 
chunk411k
in
buf.split(411k
|&x| x == b'\n'
)411k
{
418
411k
                            if !first {
419
115k
                                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
411k
                            }
427
411k
                            self.fmt.buf.write_all(chunk)
?0
;
428
411k
                            first = false;
429
115k
                        }
430
115k
431
411k
                        Ok(buf.len())
432
411k
                    }
433
115k
434
115k
                    fn flush(&mut self) -> io::Result<()> {
435
0
                        self.fmt.buf.flush()
436
0
                    }
437
115k
                }
438
115k
439
115k
                // The explicit scope here is just to make older versions of Rust happy
440
115k
                {
441
115k
                    let mut wrapper = IndentWrapper {
442
115k
                        fmt: self,
443
115k
                        indent_count,
444
115k
                    };
445
115k
                    write!(wrapper, "{}", record.args())
?0
;
446
                }
447
448
115k
                write!(self.buf, "{}", self.suffix)
?0
;
449
450
115k
                Ok(())
451
            }
452
        }
453
115k
    }
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
}