Coverage Report

Created: 2025-07-11 06:39

/rust/registry/src/index.crates.io-6f17d22bba15001f/simple_logger-4.3.3/src/lib.rs
Line
Count
Source (jump to first uncovered line)
1
//! A logger that prints all messages with a simple, readable output format.
2
//!
3
//! Optional features include timestamps, colored output and logging to stderr.
4
//!
5
//! ```rust
6
//! simple_logger::SimpleLogger::new().env().init().unwrap();
7
//!
8
//! log::warn!("This is an example message.");
9
//! ```
10
//!
11
//! Some shortcuts are available for common use cases.
12
//!
13
//! Just initialize logging without any configuration:
14
//!
15
//! ```rust
16
//! simple_logger::init().unwrap();
17
//! ```
18
//!
19
//! Set the log level from the `RUST_LOG` environment variable:
20
//!
21
//! ```rust
22
//! simple_logger::init_with_env().unwrap();
23
//! ```
24
//!
25
//! Hardcode a default log level:
26
//!
27
//! ```rust
28
//! simple_logger::init_with_level(log::Level::Warn).unwrap();
29
//! ```
30
31
#![cfg_attr(feature = "nightly", feature(thread_id_value))]
32
33
#[cfg(feature = "colored")]
34
use colored::*;
35
use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError};
36
use std::{collections::HashMap, str::FromStr};
37
#[cfg(feature = "timestamps")]
38
use time::{format_description::FormatItem, OffsetDateTime, UtcOffset};
39
40
#[cfg(feature = "timestamps")]
41
const TIMESTAMP_FORMAT_OFFSET: &[FormatItem] = time::macros::format_description!(
42
    "[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3][offset_hour sign:mandatory]:[offset_minute]"
43
);
44
45
#[cfg(feature = "timestamps")]
46
const TIMESTAMP_FORMAT_UTC: &[FormatItem] =
47
    time::macros::format_description!("[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:3]Z");
48
49
#[cfg(feature = "timestamps")]
50
#[derive(PartialEq)]
51
enum Timestamps {
52
    None,
53
    Local,
54
    Utc,
55
    UtcOffset(UtcOffset),
56
}
57
58
/// Implements [`Log`] and a set of simple builder methods for configuration.
59
///
60
/// Use the various "builder" methods on this struct to configure the logger,
61
/// then call [`init`] to configure the [`log`] crate.
62
pub struct SimpleLogger {
63
    /// The default logging level
64
    default_level: LevelFilter,
65
66
    /// The specific logging level for each module
67
    ///
68
    /// This is used to override the default value for some specific modules.
69
    ///
70
    /// This must be sorted from most-specific to least-specific, so that [`enabled`](#method.enabled) can scan the
71
    /// vector for the first match to give us the desired log level for a module.
72
    module_levels: Vec<(String, LevelFilter)>,
73
74
    /// Whether to include thread names (and IDs) or not
75
    ///
76
    /// This field is only available if the `threads` feature is enabled.
77
    #[cfg(feature = "threads")]
78
    threads: bool,
79
80
    /// Control how timestamps are displayed.
81
    ///
82
    /// This field is only available if the `timestamps` feature is enabled.
83
    #[cfg(feature = "timestamps")]
84
    timestamps: Timestamps,
85
    #[cfg(feature = "timestamps")]
86
    timestamps_format: Option<&'static [FormatItem<'static>]>,
87
88
    /// Whether to use color output or not.
89
    ///
90
    /// This field is only available if the `color` feature is enabled.
91
    #[cfg(feature = "colored")]
92
    colors: bool,
93
}
94
95
impl SimpleLogger {
96
    /// Initializes the global logger with a SimpleLogger instance with
97
    /// default log level set to `Level::Trace`.
98
    ///
99
    /// ```no_run
100
    /// use simple_logger::SimpleLogger;
101
    /// SimpleLogger::new().env().init().unwrap();
102
    /// log::warn!("This is an example message.");
103
    /// ```
104
    ///
105
    /// [`init`]: #method.init
106
    #[must_use = "You must call init() to begin logging"]
107
0
    pub fn new() -> SimpleLogger {
108
0
        SimpleLogger {
109
0
            default_level: LevelFilter::Trace,
110
0
            module_levels: Vec::new(),
111
0
112
0
            #[cfg(feature = "threads")]
113
0
            threads: false,
114
0
115
0
            #[cfg(feature = "timestamps")]
116
0
            timestamps: Timestamps::Utc,
117
0
118
0
            #[cfg(feature = "timestamps")]
119
0
            timestamps_format: None,
120
0
121
0
            #[cfg(feature = "colored")]
122
0
            colors: true,
123
0
        }
124
0
    }
125
126
    /// Initializes the global logger with log level read from `RUST_LOG` environment
127
    /// variable value. Deprecated in favor of `env()`.
128
    ///
129
    /// You may use the various builder-style methods on this type to configure
130
    /// the logger, and you must call [`init`] in order to start logging messages.
131
    ///
132
    /// ```no_run
133
    /// use simple_logger::SimpleLogger;
134
    /// SimpleLogger::from_env().init().unwrap();
135
    /// log::warn!("This is an example message.");
136
    /// ```
137
    ///
138
    /// [`init`]: #method.init
139
    #[must_use = "You must call init() to begin logging"]
140
    #[deprecated(
141
        since = "1.12.0",
142
        note = "Use [`env`](#method.env) instead. Will be removed in version 2.0.0."
143
    )]
144
0
    pub fn from_env() -> SimpleLogger {
145
0
        SimpleLogger::new().with_level(log::LevelFilter::Error).env()
146
0
    }
147
148
    /// Enables the user to choose log level by setting `RUST_LOG=<level>`
149
    /// environment variable. This will use the default level set by
150
    /// [`with_level`] if `RUST_LOG` is not set or can't be parsed as a
151
    /// standard log level.
152
    ///
153
    /// This must be called after [`with_level`]. If called before
154
    /// [`with_level`], it will have no effect.
155
    ///
156
    /// [`with_level`]: #method.with_level
157
    #[must_use = "You must call init() to begin logging"]
158
0
    pub fn env(mut self) -> SimpleLogger {
159
0
        self.default_level = std::env::var("RUST_LOG")
160
0
            .ok()
161
0
            .as_deref()
162
0
            .map(log::LevelFilter::from_str)
163
0
            .and_then(Result::ok)
164
0
            .unwrap_or(self.default_level);
165
0
166
0
        self
167
0
    }
168
169
    /// Set the 'default' log level.
170
    ///
171
    /// You can override the default level for specific modules and their sub-modules using [`with_module_level`]
172
    ///
173
    /// This must be called before [`env`]. If called after [`env`], it will override the value loaded from the environment.
174
    ///
175
    /// [`env`]: #method.env
176
    /// [`with_module_level`]: #method.with_module_level
177
    #[must_use = "You must call init() to begin logging"]
178
0
    pub fn with_level(mut self, level: LevelFilter) -> SimpleLogger {
179
0
        self.default_level = level;
180
0
        self
181
0
    }
182
183
    /// Override the log level for some specific modules.
184
    ///
185
    /// This sets the log level of a specific module and all its sub-modules.
186
    /// When both the level for a parent module as well as a child module are set,
187
    /// the more specific value is taken. If the log level for the same module is
188
    /// specified twice, the resulting log level is implementation defined.
189
    ///
190
    /// # Examples
191
    ///
192
    /// Silence an overly verbose crate:
193
    ///
194
    /// ```no_run
195
    /// use simple_logger::SimpleLogger;
196
    /// use log::LevelFilter;
197
    ///
198
    /// SimpleLogger::new().with_module_level("chatty_dependency", LevelFilter::Warn).init().unwrap();
199
    /// ```
200
    ///
201
    /// Disable logging for all dependencies:
202
    ///
203
    /// ```no_run
204
    /// use simple_logger::SimpleLogger;
205
    /// use log::LevelFilter;
206
    ///
207
    /// SimpleLogger::new()
208
    ///     .with_level(LevelFilter::Off)
209
    ///     .with_module_level("my_crate", LevelFilter::Info)
210
    ///     .init()
211
    ///     .unwrap();
212
    /// ```
213
    //
214
    // This method *must* sort `module_levels` for the [`enabled`](#method.enabled) method to work correctly.
215
    #[must_use = "You must call init() to begin logging"]
216
0
    pub fn with_module_level(mut self, target: &str, level: LevelFilter) -> SimpleLogger {
217
0
        self.module_levels.push((target.to_string(), level));
218
0
        self.module_levels
219
0
            .sort_by_key(|(name, _level)| name.len().wrapping_neg());
220
0
        self
221
0
    }
222
223
    /// Override the log level for specific targets.
224
    // This method *must* sort `module_levels` for the [`enabled`](#method.enabled) method to work correctly.
225
    #[must_use = "You must call init() to begin logging"]
226
    #[deprecated(
227
        since = "1.11.0",
228
        note = "Use [`with_module_level`](#method.with_module_level) instead. Will be removed in version 2.0.0."
229
    )]
230
0
    pub fn with_target_levels(mut self, target_levels: HashMap<String, LevelFilter>) -> SimpleLogger {
231
0
        self.module_levels = target_levels.into_iter().collect();
232
0
        self.module_levels
233
0
            .sort_by_key(|(name, _level)| name.len().wrapping_neg());
234
0
        self
235
0
    }
236
237
    /// Control whether thread names (and IDs) are printed or not.
238
    ///
239
    /// This method is only available if the `threads` feature is enabled.
240
    /// Thread names are disabled by default.
241
    #[must_use = "You must call init() to begin logging"]
242
    #[cfg(feature = "threads")]
243
    pub fn with_threads(mut self, threads: bool) -> SimpleLogger {
244
        self.threads = threads;
245
        self
246
    }
247
248
    /// Control whether timestamps are printed or not.
249
    ///
250
    /// Timestamps will be displayed in the local timezone.
251
    ///
252
    /// This method is only available if the `timestamps` feature is enabled.
253
    #[must_use = "You must call init() to begin logging"]
254
    #[cfg(feature = "timestamps")]
255
    #[deprecated(
256
        since = "1.16.0",
257
        note = "Use [`with_local_timestamps`] or [`with_utc_timestamps`] instead. Will be removed in version 2.0.0."
258
    )]
259
0
    pub fn with_timestamps(mut self, timestamps: bool) -> SimpleLogger {
260
0
        if timestamps {
261
0
            self.timestamps = Timestamps::Local
262
        } else {
263
0
            self.timestamps = Timestamps::None
264
        }
265
0
        self
266
0
    }
267
268
    /// Control the format used for timestamps.
269
    ///
270
    /// Without this, a default format is used depending on the timestamps type.
271
    ///
272
    /// The syntax for the format_description macro can be found in the
273
    /// [`time` crate book](https://time-rs.github.io/book/api/format-description.html).
274
    ///
275
    /// ```
276
    /// simple_logger::SimpleLogger::new()
277
    ///  .with_level(log::LevelFilter::Debug)
278
    ///  .env()
279
    ///  .with_timestamp_format(time::macros::format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"))
280
    ///  .init()
281
    ///  .unwrap();
282
    /// ```
283
    #[must_use = "You must call init() to begin logging"]
284
    #[cfg(feature = "timestamps")]
285
0
    pub fn with_timestamp_format(mut self, format: &'static [FormatItem<'static>]) -> SimpleLogger {
286
0
        self.timestamps_format = Some(format);
287
0
        self
288
0
    }
289
290
    /// Don't display any timestamps.
291
    ///
292
    /// This method is only available if the `timestamps` feature is enabled.
293
    #[must_use = "You must call init() to begin logging"]
294
    #[cfg(feature = "timestamps")]
295
0
    pub fn without_timestamps(mut self) -> SimpleLogger {
296
0
        self.timestamps = Timestamps::None;
297
0
        self
298
0
    }
299
300
    /// Display timestamps using the local timezone.
301
    ///
302
    /// This method is only available if the `timestamps` feature is enabled.
303
    #[must_use = "You must call init() to begin logging"]
304
    #[cfg(feature = "timestamps")]
305
0
    pub fn with_local_timestamps(mut self) -> SimpleLogger {
306
0
        self.timestamps = Timestamps::Local;
307
0
        self
308
0
    }
309
310
    /// Display timestamps using UTC.
311
    ///
312
    /// This method is only available if the `timestamps` feature is enabled.
313
    #[must_use = "You must call init() to begin logging"]
314
    #[cfg(feature = "timestamps")]
315
0
    pub fn with_utc_timestamps(mut self) -> SimpleLogger {
316
0
        self.timestamps = Timestamps::Utc;
317
0
        self
318
0
    }
319
320
    /// Display timestamps using a static UTC offset.
321
    ///
322
    /// This method is only available if the `timestamps` feature is enabled.
323
    #[must_use = "You must call init() to begin logging"]
324
    #[cfg(feature = "timestamps")]
325
0
    pub fn with_utc_offset(mut self, offset: UtcOffset) -> SimpleLogger {
326
0
        self.timestamps = Timestamps::UtcOffset(offset);
327
0
        self
328
0
    }
329
330
    /// Control whether messages are colored or not.
331
    ///
332
    /// This method is only available if the `colored` feature is enabled.
333
    #[must_use = "You must call init() to begin logging"]
334
    #[cfg(feature = "colored")]
335
0
    pub fn with_colors(mut self, colors: bool) -> SimpleLogger {
336
0
        self.colors = colors;
337
0
        self
338
0
    }
339
340
    /// Configure the logger
341
0
    pub fn max_level(&self) -> LevelFilter {
342
0
        let max_level = self.module_levels.iter().map(|(_name, level)| level).copied().max();
343
0
        max_level
344
0
            .map(|lvl| lvl.max(self.default_level))
345
0
            .unwrap_or(self.default_level)
346
0
    }
347
348
    /// 'Init' the actual logger and instantiate it,
349
    /// this method MUST be called in order for the logger to be effective.
350
0
    pub fn init(self) -> Result<(), SetLoggerError> {
351
0
        #[cfg(all(windows, feature = "colored"))]
352
0
        set_up_color_terminal();
353
0
354
0
        log::set_max_level(self.max_level());
355
0
        log::set_boxed_logger(Box::new(self))
356
0
    }
357
}
358
359
impl Default for SimpleLogger {
360
    /// See [this](struct.SimpleLogger.html#method.new)
361
0
    fn default() -> Self {
362
0
        SimpleLogger::new()
363
0
    }
364
}
365
366
impl Log for SimpleLogger {
367
0
    fn enabled(&self, metadata: &Metadata) -> bool {
368
0
        &metadata.level().to_level_filter()
369
0
            <= self
370
0
                .module_levels
371
0
                .iter()
372
0
                /* At this point the Vec is already sorted so that we can simply take
373
0
                 * the first match
374
0
                 */
375
0
                .find(|(name, _level)| metadata.target().starts_with(name))
376
0
                .map(|(_name, level)| level)
377
0
                .unwrap_or(&self.default_level)
378
0
    }
379
380
0
    fn log(&self, record: &Record) {
381
0
        if self.enabled(record.metadata()) {
382
0
            let level_string = {
383
                #[cfg(feature = "colored")]
384
                {
385
0
                    if self.colors {
386
0
                        match record.level() {
387
0
                            Level::Error => format!("{:<5}", record.level().to_string()).red().to_string(),
388
0
                            Level::Warn => format!("{:<5}", record.level().to_string()).yellow().to_string(),
389
0
                            Level::Info => format!("{:<5}", record.level().to_string()).cyan().to_string(),
390
0
                            Level::Debug => format!("{:<5}", record.level().to_string()).purple().to_string(),
391
0
                            Level::Trace => format!("{:<5}", record.level().to_string()).normal().to_string(),
392
                        }
393
                    } else {
394
0
                        format!("{:<5}", record.level().to_string())
395
                    }
396
                }
397
                #[cfg(not(feature = "colored"))]
398
                {
399
                    format!("{:<5}", record.level().to_string())
400
                }
401
            };
402
403
0
            let target = if !record.target().is_empty() {
404
0
                record.target()
405
            } else {
406
0
                record.module_path().unwrap_or_default()
407
            };
408
409
0
            let thread = {
410
0
                #[cfg(feature = "threads")]
411
0
                if self.threads {
412
0
                    let thread = std::thread::current();
413
0
414
0
                    format!("@{}", {
415
0
                        #[cfg(feature = "nightly")]
416
0
                        {
417
0
                            thread.name().unwrap_or(&thread.id().as_u64().to_string())
418
0
                        }
419
0
420
0
                        #[cfg(not(feature = "nightly"))]
421
0
                        {
422
0
                            thread.name().unwrap_or("?")
423
0
                        }
424
0
                    })
425
0
                } else {
426
0
                    "".to_string()
427
0
                }
428
0
429
0
                #[cfg(not(feature = "threads"))]
430
0
                ""
431
            };
432
433
0
            let timestamp = {
434
                #[cfg(feature = "timestamps")]
435
0
                match self.timestamps {
436
0
                    Timestamps::None => "".to_string(),
437
0
                    Timestamps::Local => format!(
438
0
                        "{} ",
439
0
                        OffsetDateTime::now_local()
440
0
                            .expect(concat!(
441
0
                                "Could not determine the UTC offset on this system. ",
442
0
                                "Consider displaying UTC time instead. ",
443
0
                                "Possible causes are that the time crate does not implement \"local_offset_at\" ",
444
0
                                "on your system, or that you are running in a multi-threaded environment and ",
445
0
                                "the time crate is returning \"None\" from \"local_offset_at\" to avoid unsafe ",
446
0
                                "behaviour. See the time crate's documentation for more information. ",
447
0
                                "(https://time-rs.github.io/internal-api/time/index.html#feature-flags)"
448
0
                            ))
449
0
                            .format(&self.timestamps_format.unwrap_or(TIMESTAMP_FORMAT_OFFSET))
450
0
                            .unwrap()
451
0
                    ),
452
0
                    Timestamps::Utc => format!(
453
0
                        "{} ",
454
0
                        OffsetDateTime::now_utc()
455
0
                            .format(&self.timestamps_format.unwrap_or(TIMESTAMP_FORMAT_UTC))
456
0
                            .unwrap()
457
0
                    ),
458
0
                    Timestamps::UtcOffset(offset) => format!(
459
0
                        "{} ",
460
0
                        OffsetDateTime::now_utc()
461
0
                            .to_offset(offset)
462
0
                            .format(&self.timestamps_format.unwrap_or(TIMESTAMP_FORMAT_OFFSET))
463
0
                            .unwrap()
464
0
                    ),
465
                }
466
467
                #[cfg(not(feature = "timestamps"))]
468
                ""
469
            };
470
471
0
            let message = format!("{}{} [{}{}] {}", timestamp, level_string, target, thread, record.args());
472
0
473
0
            #[cfg(not(feature = "stderr"))]
474
0
            println!("{}", message);
475
476
            #[cfg(feature = "stderr")]
477
            eprintln!("{}", message);
478
0
        }
479
0
    }
480
481
0
    fn flush(&self) {}
482
}
483
484
/// Configure the console to display colours.
485
///
486
/// This is only needed on Windows when using the 'colored' feature.
487
#[cfg(all(windows, feature = "colored"))]
488
pub fn set_up_color_terminal() {
489
    use std::io::{stdout, IsTerminal};
490
491
    if stdout().is_terminal() {
492
        unsafe {
493
            use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
494
            use windows_sys::Win32::System::Console::{
495
                GetConsoleMode, GetStdHandle, SetConsoleMode, CONSOLE_MODE, ENABLE_VIRTUAL_TERMINAL_PROCESSING,
496
                STD_OUTPUT_HANDLE,
497
            };
498
499
            let stdout = GetStdHandle(STD_OUTPUT_HANDLE);
500
501
            if stdout == INVALID_HANDLE_VALUE {
502
                return;
503
            }
504
505
            let mut mode: CONSOLE_MODE = 0;
506
507
            if GetConsoleMode(stdout, &mut mode) == 0 {
508
                return;
509
            }
510
511
            SetConsoleMode(stdout, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
512
        }
513
    }
514
}
515
516
/// Configure the console to display colours.
517
///
518
/// This method does nothing if not running on Windows with the colored feature.
519
#[cfg(not(all(windows, feature = "colored")))]
520
0
pub fn set_up_color_terminal() {}
521
522
/// Initialise the logger with its default configuration.
523
///
524
/// Log messages will not be filtered.
525
/// The `RUST_LOG` environment variable is not used.
526
0
pub fn init() -> Result<(), SetLoggerError> {
527
0
    SimpleLogger::new().init()
528
0
}
529
530
/// Initialise the logger with its default configuration.
531
///
532
/// Log messages will not be filtered.
533
/// The `RUST_LOG` environment variable is not used.
534
///
535
/// This function is only available if the `timestamps` feature is enabled.
536
#[cfg(feature = "timestamps")]
537
0
pub fn init_utc() -> Result<(), SetLoggerError> {
538
0
    SimpleLogger::new().with_utc_timestamps().init()
539
0
}
540
541
/// Initialise the logger with the `RUST_LOG` environment variable.
542
///
543
/// Log messages will be filtered based on the `RUST_LOG` environment variable.
544
0
pub fn init_with_env() -> Result<(), SetLoggerError> {
545
0
    SimpleLogger::new().env().init()
546
0
}
547
548
/// Initialise the logger with a specific log level.
549
///
550
/// Log messages below the given [`Level`] will be filtered.
551
/// The `RUST_LOG` environment variable is not used.
552
0
pub fn init_with_level(level: Level) -> Result<(), SetLoggerError> {
553
0
    SimpleLogger::new().with_level(level.to_level_filter()).init()
554
0
}
555
556
/// Use [`init_with_env`] instead.
557
///
558
/// This does the same as [`init_with_env`] but unwraps the result.
559
#[deprecated(
560
    since = "1.12.0",
561
    note = "Use [`init_with_env`] instead, which does not unwrap the result. Will be removed in version 2.0.0."
562
)]
563
0
pub fn init_by_env() {
564
0
    init_with_env().unwrap()
565
0
}
566
567
#[cfg(test)]
568
mod test {
569
    use super::*;
570
571
    #[test]
572
    fn test_module_levels_allowlist() {
573
        let logger = SimpleLogger::new()
574
            .with_level(LevelFilter::Off)
575
            .with_module_level("my_crate", LevelFilter::Info);
576
577
        assert!(logger.enabled(&create_log("my_crate", Level::Info)));
578
        assert!(logger.enabled(&create_log("my_crate::module", Level::Info)));
579
        assert!(!logger.enabled(&create_log("my_crate::module", Level::Debug)));
580
        assert!(!logger.enabled(&create_log("not_my_crate", Level::Debug)));
581
        assert!(!logger.enabled(&create_log("not_my_crate::module", Level::Error)));
582
    }
583
584
    #[test]
585
    fn test_module_levels_denylist() {
586
        let logger = SimpleLogger::new()
587
            .with_level(LevelFilter::Debug)
588
            .with_module_level("my_crate", LevelFilter::Trace)
589
            .with_module_level("chatty_dependency", LevelFilter::Info);
590
591
        assert!(logger.enabled(&create_log("my_crate", Level::Info)));
592
        assert!(logger.enabled(&create_log("my_crate", Level::Trace)));
593
        assert!(logger.enabled(&create_log("my_crate::module", Level::Info)));
594
        assert!(logger.enabled(&create_log("my_crate::module", Level::Trace)));
595
        assert!(logger.enabled(&create_log("not_my_crate", Level::Debug)));
596
        assert!(!logger.enabled(&create_log("not_my_crate::module", Level::Trace)));
597
        assert!(logger.enabled(&create_log("chatty_dependency", Level::Info)));
598
        assert!(!logger.enabled(&create_log("chatty_dependency", Level::Debug)));
599
        assert!(!logger.enabled(&create_log("chatty_dependency::module", Level::Debug)));
600
        assert!(logger.enabled(&create_log("chatty_dependency::module", Level::Warn)));
601
    }
602
603
    /// Test that enabled() looks for the most specific target.
604
    #[test]
605
    fn test_module_levels() {
606
        let logger = SimpleLogger::new()
607
            .with_level(LevelFilter::Off)
608
            .with_module_level("a", LevelFilter::Off)
609
            .with_module_level("a::b::c", LevelFilter::Off)
610
            .with_module_level("a::b", LevelFilter::Info);
611
612
        assert_eq!(logger.enabled(&create_log("a", Level::Info)), false);
613
        assert_eq!(logger.enabled(&create_log("a::b", Level::Info)), true);
614
        assert_eq!(logger.enabled(&create_log("a::b::c", Level::Info)), false);
615
    }
616
617
    #[test]
618
    fn test_max_level() {
619
        let builder = SimpleLogger::new();
620
        assert_eq!(builder.max_level(), LevelFilter::Trace);
621
    }
622
623
    #[test]
624
    #[cfg(feature = "timestamps")]
625
    fn test_timestamps_defaults() {
626
        let builder = SimpleLogger::new();
627
        assert!(builder.timestamps == Timestamps::Utc);
628
    }
629
630
    #[test]
631
    #[cfg(feature = "timestamps")]
632
    #[allow(deprecated)]
633
    fn test_with_timestamps() {
634
        let builder = SimpleLogger::new().with_timestamps(false);
635
        assert!(builder.timestamps == Timestamps::None);
636
    }
637
638
    #[test]
639
    #[cfg(feature = "timestamps")]
640
    fn test_with_utc_timestamps() {
641
        let builder = SimpleLogger::new().with_utc_timestamps();
642
        assert!(builder.timestamps == Timestamps::Utc);
643
    }
644
645
    #[test]
646
    #[cfg(feature = "timestamps")]
647
    fn test_with_local_timestamps() {
648
        let builder = SimpleLogger::new().with_local_timestamps();
649
        assert!(builder.timestamps == Timestamps::Local);
650
    }
651
652
    #[test]
653
    #[cfg(feature = "timestamps")]
654
    #[allow(deprecated)]
655
    fn test_with_timestamps_format() {
656
        let builder =
657
            SimpleLogger::new().with_timestamp_format(time::macros::format_description!("[hour]:[minute]:[second]"));
658
        assert!(builder.timestamps_format.is_some());
659
    }
660
661
    #[test]
662
    #[cfg(feature = "colored")]
663
    fn test_with_colors() {
664
        let mut builder = SimpleLogger::new();
665
        assert!(builder.colors == true);
666
667
        builder = builder.with_colors(false);
668
        assert!(builder.colors == false);
669
    }
670
671
    /// > And, without sorting, this would lead to all serde_json logs being treated as if they were configured to
672
    /// > Error level instead of Trace (since to determine the logging level for target, the code finds first match in
673
    /// > module_levels by a string prefix).
674
    #[test]
675
    fn test_issue_90() {
676
        let logger = SimpleLogger::new()
677
            .with_level(LevelFilter::Off)
678
            .with_module_level("serde", LevelFilter::Error)
679
            .with_module_level("serde_json", LevelFilter::Trace);
680
681
        assert_eq!(logger.enabled(&create_log("serde", Level::Trace)), false);
682
        assert_eq!(logger.enabled(&create_log("serde_json", Level::Trace)), true);
683
    }
684
685
    fn create_log(name: &str, level: Level) -> Metadata {
686
        let mut builder = Metadata::builder();
687
        builder.level(level);
688
        builder.target(name);
689
        builder.build()
690
    }
691
}