Coverage Report

Created: 2025-06-23 13:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/cargo-vendor-dir/env_logger-0.11.8/src/fmt/mod.rs
Line
Count
Source
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.s
15
//!
16
//! For example, you could use one of:
17
//! - [anstyle](https://docs.rs/anstyle) is a minimal, runtime string styling API and is re-exported as [`style`]
18
//! - [owo-colors](https://docs.rs/owo-colors) is a feature rich runtime string styling API
19
//! - [color-print](https://docs.rs/color-print) for feature-rich compile-time styling API
20
//!
21
//! See also [`Formatter::default_level_style`]
22
//!
23
//! ```
24
//! use std::io::Write;
25
//!
26
//! let mut builder = env_logger::Builder::new();
27
//!
28
//! builder.format(|buf, record| {
29
//!     writeln!(buf, "{}: {}",
30
//!         record.level(),
31
//!         record.args())
32
//! });
33
//! ```
34
//!
35
//! # Key Value arguments
36
//!
37
//! If the `kv` feature is enabled, then the default format will include key values from
38
//! the log by default, but this can be disabled by calling [`Builder::format_key_values`]
39
//! with [`hidden_kv_format`] as the format function.
40
//!
41
//! The way these keys and values are formatted can also be customized with a separate format
42
//! function that is called by the default format with [`Builder::format_key_values`].
43
//!
44
//! ```
45
//! # #[cfg(feature= "kv")]
46
//! # {
47
//! use log::info;
48
//! env_logger::init();
49
//! info!(x="45"; "Some message");
50
//! info!(x="12"; "Another message {x}", x="12");
51
//! # }
52
//! ```
53
//!
54
//! See <https://docs.rs/log/latest/log/#structured-logging>.
55
//!
56
//! [`Builder::format`]: crate::Builder::format
57
//! [`Write`]: std::io::Write
58
//! [`Builder::format_key_values`]: crate::Builder::format_key_values
59
60
use std::cell::RefCell;
61
use std::fmt::Display;
62
use std::io::prelude::Write;
63
use std::rc::Rc;
64
use std::{fmt, io, mem};
65
66
#[cfg(feature = "color")]
67
use log::Level;
68
use log::Record;
69
70
#[cfg(feature = "humantime")]
71
mod humantime;
72
#[cfg(feature = "kv")]
73
mod kv;
74
pub(crate) mod writer;
75
76
#[cfg(feature = "color")]
77
pub use anstyle as style;
78
79
#[cfg(feature = "humantime")]
80
pub use self::humantime::Timestamp;
81
#[cfg(feature = "kv")]
82
pub use self::kv::*;
83
pub use self::writer::Target;
84
pub use self::writer::WriteStyle;
85
86
use self::writer::{Buffer, Writer};
87
88
/// Formatting precision of timestamps.
89
///
90
/// Seconds give precision of full seconds, milliseconds give thousands of a
91
/// second (3 decimal digits), microseconds are millionth of a second (6 decimal
92
/// digits) and nanoseconds are billionth of a second (9 decimal digits).
93
#[allow(clippy::exhaustive_enums)] // compatibility
94
#[derive(Copy, Clone, Debug)]
95
pub enum TimestampPrecision {
96
    /// Full second precision (0 decimal digits)
97
    Seconds,
98
    /// Millisecond precision (3 decimal digits)
99
    Millis,
100
    /// Microsecond precision (6 decimal digits)
101
    Micros,
102
    /// Nanosecond precision (9 decimal digits)
103
    Nanos,
104
}
105
106
/// The default timestamp precision is seconds.
107
impl Default for TimestampPrecision {
108
770
    fn default() -> Self {
109
770
        TimestampPrecision::Seconds
110
770
    }
111
}
112
113
/// A formatter to write logs into.
114
///
115
/// `Formatter` implements the standard [`Write`] trait for writing log records.
116
/// It also supports terminal styling using ANSI escape codes.
117
///
118
/// # Examples
119
///
120
/// Use the [`writeln`] macro to format a log record.
121
/// An instance of a `Formatter` is passed to an `env_logger` format as `buf`:
122
///
123
/// ```
124
/// use std::io::Write;
125
///
126
/// let mut builder = env_logger::Builder::new();
127
///
128
/// builder.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()));
129
/// ```
130
///
131
/// [`Write`]: std::io::Write
132
/// [`writeln`]: std::writeln
133
pub struct Formatter {
134
    buf: Rc<RefCell<Buffer>>,
135
    write_style: WriteStyle,
136
}
137
138
impl Formatter {
139
0
    pub(crate) fn new(writer: &Writer) -> Self {
140
0
        Formatter {
141
0
            buf: Rc::new(RefCell::new(writer.buffer())),
142
0
            write_style: writer.write_style(),
143
0
        }
144
0
    }
145
146
0
    pub(crate) fn write_style(&self) -> WriteStyle {
147
0
        self.write_style
148
0
    }
149
150
0
    pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> {
151
0
        writer.print(&self.buf.borrow())
152
0
    }
153
154
0
    pub(crate) fn clear(&mut self) {
155
0
        self.buf.borrow_mut().clear();
156
0
    }
157
}
158
159
#[cfg(feature = "color")]
160
impl Formatter {
161
    /// Get the default [`style::Style`] for the given level.
162
    ///
163
    /// The style can be used to print other values besides the level.
164
    ///
165
    /// See [`style`] for how to adapt it to the styling crate of your choice
166
0
    pub fn default_level_style(&self, level: Level) -> style::Style {
167
0
        if self.write_style == WriteStyle::Never {
168
0
            style::Style::new()
169
        } else {
170
0
            match level {
171
0
                Level::Trace => style::AnsiColor::Cyan.on_default(),
172
0
                Level::Debug => style::AnsiColor::Blue.on_default(),
173
0
                Level::Info => style::AnsiColor::Green.on_default(),
174
0
                Level::Warn => style::AnsiColor::Yellow.on_default(),
175
0
                Level::Error => style::AnsiColor::Red
176
0
                    .on_default()
177
0
                    .effects(style::Effects::BOLD),
178
            }
179
        }
180
0
    }
181
}
182
183
impl Write for Formatter {
184
0
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
185
0
        self.buf.borrow_mut().write(buf)
186
0
    }
187
188
0
    fn flush(&mut self) -> io::Result<()> {
189
0
        self.buf.borrow_mut().flush()
190
0
    }
191
}
192
193
impl fmt::Debug for Formatter {
194
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195
0
        let buf = self.buf.borrow();
196
0
        f.debug_struct("Formatter")
197
0
            .field("buf", &buf)
198
0
            .field("write_style", &self.write_style)
199
0
            .finish()
200
0
    }
201
}
202
203
pub(crate) trait RecordFormat {
204
    fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()>;
205
}
206
207
impl<F> RecordFormat for F
208
where
209
    F: Fn(&mut Formatter, &Record<'_>) -> io::Result<()>,
210
{
211
0
    fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()> {
212
0
        (self)(formatter, record)
213
0
    }
214
}
215
216
pub(crate) type FormatFn = Box<dyn RecordFormat + Sync + Send>;
217
218
#[derive(Default)]
219
pub(crate) struct Builder {
220
    pub(crate) default_format: ConfigurableFormat,
221
    pub(crate) custom_format: Option<FormatFn>,
222
    built: bool,
223
}
224
225
impl Builder {
226
    /// Convert the format into a callable function.
227
    ///
228
    /// If the `custom_format` is `Some`, then any `default_format` switches are ignored.
229
    /// If the `custom_format` is `None`, then a default format is returned.
230
    /// Any `default_format` switches set to `false` won't be written by the format.
231
385
    pub(crate) fn build(&mut self) -> FormatFn {
232
385
        assert!(!self.built, 
"attempt to re-use consumed builder"0
);
233
234
385
        let built = mem::replace(
235
385
            self,
236
385
            Builder {
237
385
                built: true,
238
385
                ..Default::default()
239
385
            },
240
385
        );
241
242
385
        if let Some(
fmt0
) = built.custom_format {
243
0
            fmt
244
        } else {
245
385
            Box::new(built.default_format)
246
        }
247
385
    }
248
}
249
250
#[cfg(feature = "color")]
251
type SubtleStyle = StyledValue<&'static str>;
252
#[cfg(not(feature = "color"))]
253
type SubtleStyle = &'static str;
254
255
/// A value that can be printed using the given styles.
256
#[cfg(feature = "color")]
257
struct StyledValue<T> {
258
    style: style::Style,
259
    value: T,
260
}
261
262
#[cfg(feature = "color")]
263
impl<T: Display> Display for StyledValue<T> {
264
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265
0
        let style = self.style;
266
0
267
0
        // We need to make sure `f`s settings don't get passed onto the styling but do get passed
268
0
        // to the value
269
0
        write!(f, "{style}")?;
270
0
        self.value.fmt(f)?;
271
0
        write!(f, "{style:#}")?;
272
0
        Ok(())
273
0
    }
274
}
275
276
#[cfg(not(feature = "color"))]
277
type StyledValue<T> = T;
278
279
/// A [custom format][crate::Builder::format] with settings for which fields to show
280
pub struct ConfigurableFormat {
281
    // This format needs to work with any combination of crate features.
282
    pub(crate) timestamp: Option<TimestampPrecision>,
283
    pub(crate) module_path: bool,
284
    pub(crate) target: bool,
285
    pub(crate) level: bool,
286
    pub(crate) source_file: bool,
287
    pub(crate) source_line_number: bool,
288
    pub(crate) indent: Option<usize>,
289
    pub(crate) suffix: &'static str,
290
    #[cfg(feature = "kv")]
291
    pub(crate) kv_format: Option<Box<KvFormatFn>>,
292
}
293
294
impl ConfigurableFormat {
295
    /// Format the [`Record`] as configured for outputting
296
0
    pub fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()> {
297
0
        let fmt = ConfigurableFormatWriter {
298
0
            format: self,
299
0
            buf: formatter,
300
0
            written_header_value: false,
301
0
        };
302
0
303
0
        fmt.write(record)
304
0
    }
305
}
306
307
impl ConfigurableFormat {
308
    /// Whether or not to write the level in the default format.
309
0
    pub fn level(&mut self, write: bool) -> &mut Self {
310
0
        self.level = write;
311
0
        self
312
0
    }
313
314
    /// Whether or not to write the source file path in the default format.
315
0
    pub fn file(&mut self, write: bool) -> &mut Self {
316
0
        self.source_file = write;
317
0
        self
318
0
    }
319
320
    /// Whether or not to write the source line number path in the default format.
321
    ///
322
    /// Only has effect if `format_file` is also enabled
323
0
    pub fn line_number(&mut self, write: bool) -> &mut Self {
324
0
        self.source_line_number = write;
325
0
        self
326
0
    }
327
328
    /// Whether or not to write the module path in the default format.
329
0
    pub fn module_path(&mut self, write: bool) -> &mut Self {
330
0
        self.module_path = write;
331
0
        self
332
0
    }
333
334
    /// Whether or not to write the target in the default format.
335
0
    pub fn target(&mut self, write: bool) -> &mut Self {
336
0
        self.target = write;
337
0
        self
338
0
    }
339
340
    /// Configures the amount of spaces to use to indent multiline log records.
341
    /// A value of `None` disables any kind of indentation.
342
0
    pub fn indent(&mut self, indent: Option<usize>) -> &mut Self {
343
0
        self.indent = indent;
344
0
        self
345
0
    }
346
347
    /// Configures if timestamp should be included and in what precision.
348
0
    pub fn timestamp(&mut self, timestamp: Option<TimestampPrecision>) -> &mut Self {
349
0
        self.timestamp = timestamp;
350
0
        self
351
0
    }
352
353
    /// Configures the end of line suffix.
354
0
    pub fn suffix(&mut self, suffix: &'static str) -> &mut Self {
355
0
        self.suffix = suffix;
356
0
        self
357
0
    }
358
359
    /// Set the format for structured key/value pairs in the log record
360
    ///
361
    /// With the default format, this function is called for each record and should format
362
    /// the structured key-value pairs as returned by [`log::Record::key_values`].
363
    ///
364
    /// The format function is expected to output the string directly to the `Formatter` so that
365
    /// implementations can use the [`std::fmt`] macros, similar to the main format function.
366
    ///
367
    /// The default format uses a space to separate each key-value pair, with an "=" between
368
    /// the key and value.
369
    #[cfg(feature = "kv")]
370
    pub fn key_values<F>(&mut self, format: F) -> &mut Self
371
    where
372
        F: Fn(&mut Formatter, &dyn log::kv::Source) -> io::Result<()> + Sync + Send + 'static,
373
    {
374
        self.kv_format = Some(Box::new(format));
375
        self
376
    }
377
}
378
379
impl Default for ConfigurableFormat {
380
770
    fn default() -> Self {
381
770
        Self {
382
770
            timestamp: Some(Default::default()),
383
770
            module_path: false,
384
770
            target: true,
385
770
            level: true,
386
770
            source_file: false,
387
770
            source_line_number: false,
388
770
            indent: Some(4),
389
770
            suffix: "\n",
390
770
            #[cfg(feature = "kv")]
391
770
            kv_format: None,
392
770
        }
393
770
    }
394
}
395
396
impl RecordFormat for ConfigurableFormat {
397
0
    fn format(&self, formatter: &mut Formatter, record: &Record<'_>) -> io::Result<()> {
398
0
        self.format(formatter, record)
399
0
    }
400
}
401
402
/// The default format.
403
///
404
/// This format needs to work with any combination of crate features.
405
struct ConfigurableFormatWriter<'a> {
406
    format: &'a ConfigurableFormat,
407
    buf: &'a mut Formatter,
408
    written_header_value: bool,
409
}
410
411
impl ConfigurableFormatWriter<'_> {
412
0
    fn write(mut self, record: &Record<'_>) -> io::Result<()> {
413
0
        self.write_timestamp()?;
414
0
        self.write_level(record)?;
415
0
        self.write_module_path(record)?;
416
0
        self.write_source_location(record)?;
417
0
        self.write_target(record)?;
418
0
        self.finish_header()?;
419
420
0
        self.write_args(record)?;
421
        #[cfg(feature = "kv")]
422
        self.write_kv(record)?;
423
0
        write!(self.buf, "{}", self.format.suffix)
424
0
    }
425
426
0
    fn subtle_style(&self, text: &'static str) -> SubtleStyle {
427
0
        #[cfg(feature = "color")]
428
0
        {
429
0
            StyledValue {
430
0
                style: if self.buf.write_style == WriteStyle::Never {
431
0
                    style::Style::new()
432
                } else {
433
0
                    style::AnsiColor::BrightBlack.on_default()
434
                },
435
0
                value: text,
436
0
            }
437
0
        }
438
0
        #[cfg(not(feature = "color"))]
439
0
        {
440
0
            text
441
0
        }
442
0
    }
443
444
0
    fn write_header_value<T>(&mut self, value: T) -> io::Result<()>
445
0
    where
446
0
        T: Display,
447
0
    {
448
0
        if !self.written_header_value {
449
0
            self.written_header_value = true;
450
0
451
0
            let open_brace = self.subtle_style("[");
452
0
            write!(self.buf, "{open_brace}{value}")
453
        } else {
454
0
            write!(self.buf, " {value}")
455
        }
456
0
    }
457
458
0
    fn write_level(&mut self, record: &Record<'_>) -> io::Result<()> {
459
0
        if !self.format.level {
460
0
            return Ok(());
461
0
        }
462
0
463
0
        let level = {
464
0
            let level = record.level();
465
0
            #[cfg(feature = "color")]
466
0
            {
467
0
                StyledValue {
468
0
                    style: self.buf.default_level_style(level),
469
0
                    value: level,
470
0
                }
471
0
            }
472
0
            #[cfg(not(feature = "color"))]
473
0
            {
474
0
                level
475
0
            }
476
0
        };
477
0
478
0
        self.write_header_value(format_args!("{level:<5}"))
479
0
    }
480
481
0
    fn write_timestamp(&mut self) -> io::Result<()> {
482
0
        #[cfg(feature = "humantime")]
483
0
        {
484
0
            use self::TimestampPrecision::{Micros, Millis, Nanos, Seconds};
485
0
            let ts = match self.format.timestamp {
486
0
                None => return Ok(()),
487
0
                Some(Seconds) => self.buf.timestamp_seconds(),
488
0
                Some(Millis) => self.buf.timestamp_millis(),
489
0
                Some(Micros) => self.buf.timestamp_micros(),
490
0
                Some(Nanos) => self.buf.timestamp_nanos(),
491
0
            };
492
0
493
0
            self.write_header_value(ts)
494
0
        }
495
0
        #[cfg(not(feature = "humantime"))]
496
0
        {
497
0
            // Trick the compiler to think we have used self.timestamp
498
0
            // Workaround for "field is never used: `timestamp`" compiler nag.
499
0
            let _ = self.format.timestamp;
500
0
            Ok(())
501
0
        }
502
0
    }
503
504
0
    fn write_module_path(&mut self, record: &Record<'_>) -> io::Result<()> {
505
0
        if !self.format.module_path {
506
0
            return Ok(());
507
0
        }
508
509
0
        if let Some(module_path) = record.module_path() {
510
0
            self.write_header_value(module_path)
511
        } else {
512
0
            Ok(())
513
        }
514
0
    }
515
516
0
    fn write_source_location(&mut self, record: &Record<'_>) -> io::Result<()> {
517
0
        if !self.format.source_file {
518
0
            return Ok(());
519
0
        }
520
521
0
        if let Some(file_path) = record.file() {
522
0
            let line = self
523
0
                .format
524
0
                .source_line_number
525
0
                .then(|| record.line())
526
0
                .flatten();
527
0
            match line {
528
0
                Some(line) => self.write_header_value(format_args!("{file_path}:{line}")),
529
0
                None => self.write_header_value(file_path),
530
            }
531
        } else {
532
0
            Ok(())
533
        }
534
0
    }
535
536
0
    fn write_target(&mut self, record: &Record<'_>) -> io::Result<()> {
537
0
        if !self.format.target {
538
0
            return Ok(());
539
0
        }
540
0
541
0
        match record.target() {
542
0
            "" => Ok(()),
543
0
            target => self.write_header_value(target),
544
        }
545
0
    }
546
547
0
    fn finish_header(&mut self) -> io::Result<()> {
548
0
        if self.written_header_value {
549
0
            let close_brace = self.subtle_style("]");
550
0
            write!(self.buf, "{close_brace} ")
551
        } else {
552
0
            Ok(())
553
        }
554
0
    }
555
556
0
    fn write_args(&mut self, record: &Record<'_>) -> io::Result<()> {
557
0
        match self.format.indent {
558
            // Fast path for no indentation
559
0
            None => write!(self.buf, "{}", record.args()),
560
561
0
            Some(indent_count) => {
562
                // Create a wrapper around the buffer only if we have to actually indent the message
563
564
                struct IndentWrapper<'a, 'b> {
565
                    fmt: &'a mut ConfigurableFormatWriter<'b>,
566
                    indent_count: usize,
567
                }
568
569
                impl Write for IndentWrapper<'_, '_> {
570
0
                    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
571
0
                        let mut first = true;
572
0
                        for chunk in buf.split(|&x| x == b'\n') {
573
0
                            if !first {
574
0
                                write!(
575
0
                                    self.fmt.buf,
576
0
                                    "{}{:width$}",
577
0
                                    self.fmt.format.suffix,
578
0
                                    "",
579
0
                                    width = self.indent_count
580
0
                                )?;
581
0
                            }
582
0
                            self.fmt.buf.write_all(chunk)?;
583
0
                            first = false;
584
                        }
585
586
0
                        Ok(buf.len())
587
0
                    }
588
589
0
                    fn flush(&mut self) -> io::Result<()> {
590
0
                        self.fmt.buf.flush()
591
0
                    }
592
                }
593
594
                // The explicit scope here is just to make older versions of Rust happy
595
                {
596
0
                    let mut wrapper = IndentWrapper {
597
0
                        fmt: self,
598
0
                        indent_count,
599
0
                    };
600
0
                    write!(wrapper, "{}", record.args())?;
601
                }
602
603
0
                Ok(())
604
            }
605
        }
606
0
    }
607
608
    #[cfg(feature = "kv")]
609
    fn write_kv(&mut self, record: &Record<'_>) -> io::Result<()> {
610
        let format = self
611
            .format
612
            .kv_format
613
            .as_deref()
614
            .unwrap_or(&default_kv_format);
615
        format(self.buf, record.key_values())
616
    }
617
}
618
619
#[cfg(test)]
620
mod tests {
621
    use super::*;
622
623
    use log::{Level, Record};
624
625
    fn write_record(record: Record<'_>, fmt: ConfigurableFormatWriter<'_>) -> String {
626
        let buf = fmt.buf.buf.clone();
627
628
        fmt.write(&record).expect("failed to write record");
629
630
        let buf = buf.borrow();
631
        String::from_utf8(buf.as_bytes().to_vec()).expect("failed to read record")
632
    }
633
634
    fn write_target(target: &str, fmt: ConfigurableFormatWriter<'_>) -> String {
635
        write_record(
636
            Record::builder()
637
                .args(format_args!("log\nmessage"))
638
                .level(Level::Info)
639
                .file(Some("test.rs"))
640
                .line(Some(144))
641
                .module_path(Some("test::path"))
642
                .target(target)
643
                .build(),
644
            fmt,
645
        )
646
    }
647
648
    fn write(fmt: ConfigurableFormatWriter<'_>) -> String {
649
        write_target("", fmt)
650
    }
651
652
    fn formatter() -> Formatter {
653
        let writer = writer::Builder::new()
654
            .write_style(WriteStyle::Never)
655
            .build();
656
657
        Formatter::new(&writer)
658
    }
659
660
    #[test]
661
    fn format_with_header() {
662
        let mut f = formatter();
663
664
        let written = write(ConfigurableFormatWriter {
665
            format: &ConfigurableFormat {
666
                timestamp: None,
667
                module_path: true,
668
                target: false,
669
                level: true,
670
                source_file: false,
671
                source_line_number: false,
672
                #[cfg(feature = "kv")]
673
                kv_format: Some(Box::new(hidden_kv_format)),
674
                indent: None,
675
                suffix: "\n",
676
            },
677
            written_header_value: false,
678
            buf: &mut f,
679
        });
680
681
        assert_eq!("[INFO  test::path] log\nmessage\n", written);
682
    }
683
684
    #[test]
685
    fn format_no_header() {
686
        let mut f = formatter();
687
688
        let written = write(ConfigurableFormatWriter {
689
            format: &ConfigurableFormat {
690
                timestamp: None,
691
                module_path: false,
692
                target: false,
693
                level: false,
694
                source_file: false,
695
                source_line_number: false,
696
                #[cfg(feature = "kv")]
697
                kv_format: Some(Box::new(hidden_kv_format)),
698
                indent: None,
699
                suffix: "\n",
700
            },
701
            written_header_value: false,
702
            buf: &mut f,
703
        });
704
705
        assert_eq!("log\nmessage\n", written);
706
    }
707
708
    #[test]
709
    fn format_indent_spaces() {
710
        let mut f = formatter();
711
712
        let written = write(ConfigurableFormatWriter {
713
            format: &ConfigurableFormat {
714
                timestamp: None,
715
                module_path: true,
716
                target: false,
717
                level: true,
718
                source_file: false,
719
                source_line_number: false,
720
                #[cfg(feature = "kv")]
721
                kv_format: Some(Box::new(hidden_kv_format)),
722
                indent: Some(4),
723
                suffix: "\n",
724
            },
725
            written_header_value: false,
726
            buf: &mut f,
727
        });
728
729
        assert_eq!("[INFO  test::path] log\n    message\n", written);
730
    }
731
732
    #[test]
733
    fn format_indent_zero_spaces() {
734
        let mut f = formatter();
735
736
        let written = write(ConfigurableFormatWriter {
737
            format: &ConfigurableFormat {
738
                timestamp: None,
739
                module_path: true,
740
                target: false,
741
                level: true,
742
                source_file: false,
743
                source_line_number: false,
744
                #[cfg(feature = "kv")]
745
                kv_format: Some(Box::new(hidden_kv_format)),
746
                indent: Some(0),
747
                suffix: "\n",
748
            },
749
            written_header_value: false,
750
            buf: &mut f,
751
        });
752
753
        assert_eq!("[INFO  test::path] log\nmessage\n", written);
754
    }
755
756
    #[test]
757
    fn format_indent_spaces_no_header() {
758
        let mut f = formatter();
759
760
        let written = write(ConfigurableFormatWriter {
761
            format: &ConfigurableFormat {
762
                timestamp: None,
763
                module_path: false,
764
                target: false,
765
                level: false,
766
                source_file: false,
767
                source_line_number: false,
768
                #[cfg(feature = "kv")]
769
                kv_format: Some(Box::new(hidden_kv_format)),
770
                indent: Some(4),
771
                suffix: "\n",
772
            },
773
            written_header_value: false,
774
            buf: &mut f,
775
        });
776
777
        assert_eq!("log\n    message\n", written);
778
    }
779
780
    #[test]
781
    fn format_suffix() {
782
        let mut f = formatter();
783
784
        let written = write(ConfigurableFormatWriter {
785
            format: &ConfigurableFormat {
786
                timestamp: None,
787
                module_path: false,
788
                target: false,
789
                level: false,
790
                source_file: false,
791
                source_line_number: false,
792
                #[cfg(feature = "kv")]
793
                kv_format: Some(Box::new(hidden_kv_format)),
794
                indent: None,
795
                suffix: "\n\n",
796
            },
797
            written_header_value: false,
798
            buf: &mut f,
799
        });
800
801
        assert_eq!("log\nmessage\n\n", written);
802
    }
803
804
    #[test]
805
    fn format_suffix_with_indent() {
806
        let mut f = formatter();
807
808
        let written = write(ConfigurableFormatWriter {
809
            format: &ConfigurableFormat {
810
                timestamp: None,
811
                module_path: false,
812
                target: false,
813
                level: false,
814
                source_file: false,
815
                source_line_number: false,
816
                #[cfg(feature = "kv")]
817
                kv_format: Some(Box::new(hidden_kv_format)),
818
                indent: Some(4),
819
                suffix: "\n\n",
820
            },
821
            written_header_value: false,
822
            buf: &mut f,
823
        });
824
825
        assert_eq!("log\n\n    message\n\n", written);
826
    }
827
828
    #[test]
829
    fn format_target() {
830
        let mut f = formatter();
831
832
        let written = write_target(
833
            "target",
834
            ConfigurableFormatWriter {
835
                format: &ConfigurableFormat {
836
                    timestamp: None,
837
                    module_path: true,
838
                    target: true,
839
                    level: true,
840
                    source_file: false,
841
                    source_line_number: false,
842
                    #[cfg(feature = "kv")]
843
                    kv_format: Some(Box::new(hidden_kv_format)),
844
                    indent: None,
845
                    suffix: "\n",
846
                },
847
                written_header_value: false,
848
                buf: &mut f,
849
            },
850
        );
851
852
        assert_eq!("[INFO  test::path target] log\nmessage\n", written);
853
    }
854
855
    #[test]
856
    fn format_empty_target() {
857
        let mut f = formatter();
858
859
        let written = write(ConfigurableFormatWriter {
860
            format: &ConfigurableFormat {
861
                timestamp: None,
862
                module_path: true,
863
                target: true,
864
                level: true,
865
                source_file: false,
866
                source_line_number: false,
867
                #[cfg(feature = "kv")]
868
                kv_format: Some(Box::new(hidden_kv_format)),
869
                indent: None,
870
                suffix: "\n",
871
            },
872
            written_header_value: false,
873
            buf: &mut f,
874
        });
875
876
        assert_eq!("[INFO  test::path] log\nmessage\n", written);
877
    }
878
879
    #[test]
880
    fn format_no_target() {
881
        let mut f = formatter();
882
883
        let written = write_target(
884
            "target",
885
            ConfigurableFormatWriter {
886
                format: &ConfigurableFormat {
887
                    timestamp: None,
888
                    module_path: true,
889
                    target: false,
890
                    level: true,
891
                    source_file: false,
892
                    source_line_number: false,
893
                    #[cfg(feature = "kv")]
894
                    kv_format: Some(Box::new(hidden_kv_format)),
895
                    indent: None,
896
                    suffix: "\n",
897
                },
898
                written_header_value: false,
899
                buf: &mut f,
900
            },
901
        );
902
903
        assert_eq!("[INFO  test::path] log\nmessage\n", written);
904
    }
905
906
    #[test]
907
    fn format_with_source_file_and_line_number() {
908
        let mut f = formatter();
909
910
        let written = write(ConfigurableFormatWriter {
911
            format: &ConfigurableFormat {
912
                timestamp: None,
913
                module_path: false,
914
                target: false,
915
                level: true,
916
                source_file: true,
917
                source_line_number: true,
918
                #[cfg(feature = "kv")]
919
                kv_format: Some(Box::new(hidden_kv_format)),
920
                indent: None,
921
                suffix: "\n",
922
            },
923
            written_header_value: false,
924
            buf: &mut f,
925
        });
926
927
        assert_eq!("[INFO  test.rs:144] log\nmessage\n", written);
928
    }
929
930
    #[cfg(feature = "kv")]
931
    #[test]
932
    fn format_kv_default() {
933
        let kvs = &[("a", 1u32), ("b", 2u32)][..];
934
        let mut f = formatter();
935
        let record = Record::builder()
936
            .args(format_args!("log message"))
937
            .level(Level::Info)
938
            .module_path(Some("test::path"))
939
            .key_values(&kvs)
940
            .build();
941
942
        let written = write_record(
943
            record,
944
            ConfigurableFormatWriter {
945
                format: &ConfigurableFormat {
946
                    timestamp: None,
947
                    module_path: false,
948
                    target: false,
949
                    level: true,
950
                    source_file: false,
951
                    source_line_number: false,
952
                    kv_format: Some(Box::new(default_kv_format)),
953
                    indent: None,
954
                    suffix: "\n",
955
                },
956
                written_header_value: false,
957
                buf: &mut f,
958
            },
959
        );
960
961
        assert_eq!("[INFO ] log message a=1 b=2\n", written);
962
    }
963
964
    #[cfg(feature = "kv")]
965
    #[test]
966
    fn format_kv_default_full() {
967
        let kvs = &[("a", 1u32), ("b", 2u32)][..];
968
        let mut f = formatter();
969
        let record = Record::builder()
970
            .args(format_args!("log\nmessage"))
971
            .level(Level::Info)
972
            .module_path(Some("test::path"))
973
            .target("target")
974
            .file(Some("test.rs"))
975
            .line(Some(42))
976
            .key_values(&kvs)
977
            .build();
978
979
        let written = write_record(
980
            record,
981
            ConfigurableFormatWriter {
982
                format: &ConfigurableFormat {
983
                    timestamp: None,
984
                    module_path: true,
985
                    target: true,
986
                    level: true,
987
                    source_file: true,
988
                    source_line_number: true,
989
                    kv_format: Some(Box::new(default_kv_format)),
990
                    indent: None,
991
                    suffix: "\n",
992
                },
993
                written_header_value: false,
994
                buf: &mut f,
995
            },
996
        );
997
998
        assert_eq!(
999
            "[INFO  test::path test.rs:42 target] log\nmessage a=1 b=2\n",
1000
            written
1001
        );
1002
    }
1003
}