spdlog/
logger.rs

1use std::{fmt::Debug, result::Result as StdResult, time::Duration};
2
3use crate::{
4    env_level,
5    error::{Error, ErrorHandler, InvalidArgumentError, SetLoggerNameError},
6    periodic_worker::PeriodicWorker,
7    sink::{Sink, Sinks},
8    sync::*,
9    Level, LevelFilter, Record, Result,
10};
11
12fn check_logger_name(name: impl AsRef<str>) -> StdResult<(), SetLoggerNameError> {
13    let name = name.as_ref();
14
15    if name.chars().any(|ch| {
16        ch == ','
17            || ch == '='
18            || ch == '*'
19            || ch == '?'
20            || ch == '$'
21            || ch == '{'
22            || ch == '}'
23            || ch == '"'
24            || ch == '\''
25            || ch == ';'
26    }) || name.starts_with(' ')
27        || name.ends_with(' ')
28    {
29        Err(SetLoggerNameError::new(name))
30    } else {
31        Ok(())
32    }
33}
34
35/// Manages, controls and manipulates multiple sinks.
36///
37/// Logger is an entry point for a log to be processed, the [`Logger::log`]
38/// method will be called by logging macros.
39///
40/// ---
41///
42/// A Logger:
43///
44/// - contains one or more [sink]s, each log will be passed into sinks in
45///   sequence;
46///
47/// - has a level filter of its own, which is not shared with the level filter
48///   of sinks;
49///
50/// - has automatic flushing policies that can be optionally enabled:
51///   - Flush level filter: Flush once when the filter returns `true` for a log.
52///   - Flush period: Flush periodically.
53///
54///   These two automatic flushing policies can work at the same time.
55///
56/// ---
57///
58/// *spdlog-rs* has a global [`default_logger`], you can modify it, or configure
59/// a new logger to suit your needs and then replace it. Basically for a
60/// lightweight program, such a global singleton logger is enough. For a complex
61/// program, for example if you have a number of different components, you can
62/// also configure multiple loggers and store them independently inside the
63/// component struct, which allows different components to have different
64/// approaches for logging.
65///
66/// # Examples
67///
68/// - Logging to the global default logger.
69///
70/// ```
71/// use spdlog::prelude::*;
72///
73/// info!("logging to the default logger");
74///
75/// spdlog::default_logger().set_level_filter(LevelFilter::All);
76/// trace!("logging to the default logger at trace level");
77/// ```
78///
79/// - Logging to a separated logger.
80///
81/// ```
82/// # use spdlog::prelude::*;
83/// let separated_logger = /* ... */
84/// # spdlog::default_logger();
85///
86/// info!(logger: separated_logger, "logging to a separated logger");
87/// error!(logger: separated_logger, "logging to a separated logger at error level");
88/// ```
89///
90/// - Fork, configure and replace the default logger.
91///
92/// ```
93/// # use spdlog::prelude::*;
94/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
95/// let new_logger = spdlog::default_logger().fork_with(|new_logger| {
96///     // Configure the new logger...
97///     Ok(())
98/// })?;
99/// spdlog::set_default_logger(new_logger);
100/// info!("logging to the default logger, but it's reconfigured");
101/// # Ok(()) }
102/// ```
103///
104/// - For more examples, see [./examples] directory.
105///
106/// [sink]: crate::sink
107/// [`default_logger`]: crate::default_logger
108/// [./examples]: https://github.com/SpriteOvO/spdlog-rs/tree/main/spdlog/examples
109pub struct Logger {
110    name: Option<String>,
111    level_filter: Atomic<LevelFilter>,
112    sinks: Sinks,
113    flush_level_filter: Atomic<LevelFilter>,
114    error_handler: RwLock<ErrorHandler>,
115    periodic_flusher: Mutex<Option<(Duration, PeriodicWorker)>>,
116}
117
118impl Debug for Logger {
119    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120        f.debug_struct("Logger")
121            .field("name", &self.name)
122            .field("level_filter", &self.level_filter())
123            .field(
124                "sinks",
125                &self
126                    .sinks
127                    .iter()
128                    .map(|sink| sink.level_filter())
129                    .collect::<Vec<_>>(),
130            )
131            .field("flush_level_filter", &self.flush_level_filter())
132            .field("error_handler", &self.error_handler.read())
133            .field(
134                "periodic_flusher",
135                &self
136                    .periodic_flusher
137                    .lock()
138                    .as_deref()
139                    .map(|opt| opt.as_ref().map(|(dur, _)| *dur))
140                    .as_ref()
141                    .map(|dur| dur as &dyn Debug)
142                    .unwrap_or(&"*lock is poisoned*"),
143            )
144            .finish()
145    }
146}
147
148impl Logger {
149    /// Gets a [`LoggerBuilder`] with default parameters:
150    ///
151    /// | Parameter            | Default Value               |
152    /// |----------------------|-----------------------------|
153    /// | [name]               | `None`                      |
154    /// | [sinks]              | `[]`                        |
155    /// | [level_filter]       | `MoreSevereEqual(Info)`     |
156    /// | [flush_level_filter] | `Off`                       |
157    /// | [flush_period]       | `None`                      |
158    /// | [error_handler]      | [`ErrorHandler::default()`] |
159    ///
160    /// [name]: LoggerBuilder::name
161    /// [sinks]: LoggerBuilder::sink
162    /// [level_filter]: LoggerBuilder::level_filter
163    /// [flush_level_filter]: LoggerBuilder::flush_level_filter
164    /// [flush_period]: Logger::set_flush_period
165    /// [error_handler]: LoggerBuilder::error_handler
166    /// [`ErrorHandler::default()`]: crate::error::ErrorHandler::default()
167    #[must_use]
168    pub fn builder() -> LoggerBuilder {
169        LoggerBuilder {
170            name: None,
171            level_filter: LevelFilter::MoreSevereEqual(Level::Info),
172            sinks: vec![],
173            flush_level_filter: LevelFilter::Off,
174            error_handler: ErrorHandler::default(),
175        }
176    }
177
178    /// Gets the logger name.
179    ///
180    /// Returns `None` if the logger does not have a name.
181    #[must_use]
182    pub fn name(&self) -> Option<&str> {
183        self.name.as_ref().map(|s| s.as_ref())
184    }
185
186    /// Sets the logger name.
187    pub fn set_name<S>(&mut self, name: Option<S>) -> StdResult<(), SetLoggerNameError>
188    where
189        S: Into<String>,
190    {
191        if let Some(name) = name {
192            let name = name.into();
193            check_logger_name(&name)?;
194            self.name = Some(name);
195        } else {
196            self.name = None;
197        }
198        Ok(())
199    }
200
201    /// Determines if a log with the specified level would be logged.
202    ///
203    /// This allows callers to avoid expensive computation of log arguments if
204    /// the would be discarded anyway.
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// # use std::sync::Arc;
210    /// use spdlog::prelude::*;
211    /// # let logger = spdlog::default_logger();
212    ///
213    /// logger.set_level_filter(LevelFilter::MoreSevere(Level::Info));
214    /// assert_eq!(logger.should_log(Level::Debug), false);
215    /// assert_eq!(logger.should_log(Level::Info), false);
216    /// assert_eq!(logger.should_log(Level::Warn), true);
217    /// assert_eq!(logger.should_log(Level::Error), true);
218    ///
219    /// logger.set_level_filter(LevelFilter::All);
220    /// assert_eq!(logger.should_log(Level::Debug), true);
221    /// assert_eq!(logger.should_log(Level::Info), true);
222    /// assert_eq!(logger.should_log(Level::Warn), true);
223    /// assert_eq!(logger.should_log(Level::Error), true);
224    /// ```
225    #[must_use]
226    pub fn should_log(&self, level: Level) -> bool {
227        self.level_filter().test(level)
228    }
229
230    /// Passes a log into sinks in sequence.
231    ///
232    /// It calls [`Sink::log`] method internally for each sink in sequence.
233    ///
234    /// # Note
235    ///
236    /// Users usually do not use this function directly, use logging macros
237    /// instead.
238    pub fn log(&self, record: &Record) {
239        if !self.should_log(record.level()) {
240            return;
241        }
242        self.sink_record(record);
243    }
244
245    /// Flushes sinks explicitly.
246    ///
247    /// It calls [`Sink::flush`] method internally for each sink in sequence.
248    ///
249    /// Users can call this function to flush explicitly and/or use automatic
250    /// flushing policies. See also [`Logger::flush_level_filter`] and
251    /// [`Logger::set_flush_period`].
252    ///
253    /// Be aware that the method can be expensive, calling it frequently may
254    /// affect performance.
255    pub fn flush(&self) {
256        self.flush_sinks();
257    }
258
259    /// Gets the flush level filter.
260    #[must_use]
261    pub fn flush_level_filter(&self) -> LevelFilter {
262        self.flush_level_filter.load(Ordering::Relaxed)
263    }
264
265    /// Sets a flush level filter.
266    ///
267    /// When logging a new record, flush the buffer if this filter condition
268    /// returns `true`.
269    ///
270    /// This automatic flushing policy can work with
271    /// [`Logger::set_flush_period`] at the same time.
272    ///
273    /// # Examples
274    ///
275    /// ```
276    /// # use std::sync::Arc;
277    /// use spdlog::prelude::*;
278    ///
279    /// # let logger: Arc<Logger> = spdlog::default_logger();
280    /// logger.set_flush_level_filter(LevelFilter::Off);
281    /// trace!(logger: logger, "hello");
282    /// trace!(logger: logger, "world");
283    /// // Until here the buffer may not have been flushed (depending on sinks implementation)
284    ///
285    /// logger.set_flush_level_filter(LevelFilter::All);
286    /// trace!(logger: logger, "hello"); // Logs and flushes the buffer once
287    /// trace!(logger: logger, "world"); // Logs and flushes the buffer once
288    /// ```
289    pub fn set_flush_level_filter(&self, level_filter: LevelFilter) {
290        self.flush_level_filter
291            .store(level_filter, Ordering::Relaxed);
292    }
293
294    /// Gets the log level filter.
295    #[must_use]
296    pub fn level_filter(&self) -> LevelFilter {
297        self.level_filter.load(Ordering::Relaxed)
298    }
299
300    /// Sets the log level filter.
301    ///
302    /// # Examples
303    ///
304    /// See [`Logger::should_log`].
305    pub fn set_level_filter(&self, level_filter: LevelFilter) {
306        self.level_filter.store(level_filter, Ordering::Relaxed);
307    }
308
309    /// Sets automatic periodic flushing.
310    ///
311    /// This function receives a `&Arc<Self>`. Calling it will spawn a new
312    /// thread internally.
313    ///
314    /// This automatic flushing policy can work with
315    /// [`Logger::set_flush_level_filter`] at the same time.
316    ///
317    /// # Panics
318    ///
319    ///  - Panics if `interval` is zero.
320    ///
321    ///  - Panics if this function is called with `Some` value and then clones
322    ///    the `Logger` instead of the `Arc<Logger>`.
323    ///
324    /// # Examples
325    ///
326    /// ```
327    /// use std::time::Duration;
328    /// # use std::sync::Arc;
329    /// # use spdlog::prelude::*;
330    ///
331    /// # let logger: Arc<Logger> = spdlog::default_logger();
332    /// // From now on, the `logger` will automatically call `flush` method the every 10 seconds.
333    /// logger.set_flush_period(Some(Duration::from_secs(10)));
334    ///
335    /// // Disable automatic periodic flushing.
336    /// logger.set_flush_period(None);
337    /// ```
338    pub fn set_flush_period(self: &Arc<Self>, interval: Option<Duration>) {
339        let mut periodic_flusher = self.periodic_flusher.lock_expect();
340
341        *periodic_flusher = None;
342
343        if let Some(interval) = interval {
344            let weak = Arc::downgrade(self);
345            let callback = move || {
346                let strong = weak.upgrade();
347                if let Some(strong) = strong {
348                    strong.flush_sinks();
349                    true
350                } else {
351                    false // All `Arc`s are dropped, return `false` to quit the
352                          // worker thread.
353                }
354            };
355            *periodic_flusher = Some((interval, PeriodicWorker::new(callback, interval)));
356        }
357    }
358
359    /// Gets a reference to sinks in the logger.
360    #[must_use]
361    pub fn sinks(&self) -> &[Arc<dyn Sink>] {
362        &self.sinks
363    }
364
365    /// Gets a mutable reference to sinks in the logger.
366    #[must_use]
367    pub fn sinks_mut(&mut self) -> &mut Sinks {
368        &mut self.sinks
369    }
370
371    /// Sets a error handler.
372    ///
373    /// If an error occurs while logging or flushing, this handler will be
374    /// called. If no handler is set, the error will be print to `stderr` and
375    /// then ignored.
376    ///
377    /// # Examples
378    ///
379    /// ```
380    /// use spdlog::prelude::*;
381    ///
382    /// spdlog::default_logger().set_error_handler(|err| {
383    ///     panic!("An error occurred in the default logger: {}", err)
384    /// });
385    /// ```
386    pub fn set_error_handler<F: Into<ErrorHandler>>(&self, handler: F) {
387        *self.error_handler.write_expect() = handler.into();
388    }
389
390    /// Forks and configures a separate new logger.
391    ///
392    /// This function creates a new logger object that inherits logger
393    /// properties from `Arc<Self>`. Then this function calls the given
394    /// `modifier` function which configures the properties on the new
395    /// logger object. The created new logger object will be a separate
396    /// object from `Arc<Self>`. (No ownership sharing)
397    ///
398    /// # Examples
399    ///
400    /// ```
401    #[doc = include_str!(concat!(env!("OUT_DIR"), "/test_utils/common_for_doc_test.rs"))]
402    /// # use std::sync::Arc;
403    /// # use spdlog::prelude::*;
404    /// #
405    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
406    /// # let test_sink = Arc::new(test_utils::StringSink::new());
407    /// let old: Arc<Logger> = /* ... */
408    /// # Arc::new(Logger::builder().build().unwrap());
409    /// // Fork from an existing logger and add a new sink.
410    /// # let new_sink = test_sink.clone();
411    /// let new = old.fork_with(|new| {
412    ///     new.sinks_mut().push(new_sink);
413    ///     Ok(())
414    /// })?;
415    ///
416    /// # info!(logger: new, "first line");
417    /// info!(logger: new, "this record will be written to `new_sink`");
418    /// # assert_eq!(test_sink.clone_string().lines().count(), 2);
419    /// info!(logger: old, "this record will not be written to `new_sink`");
420    /// # assert_eq!(test_sink.clone_string().lines().count(), 2);
421    /// # Ok(()) }
422    /// ```
423    pub fn fork_with<F>(self: &Arc<Self>, modifier: F) -> Result<Arc<Self>>
424    where
425        F: FnOnce(&mut Logger) -> Result<()>,
426    {
427        let flush_period = self.periodic_flusher.lock_expect().as_ref().map(|v| v.0);
428
429        let mut new_logger = self.clone_lossy();
430        modifier(&mut new_logger)?;
431
432        let new_logger = Arc::new(new_logger);
433        if let Some(interval) = flush_period {
434            new_logger.set_flush_period(Some(interval));
435        }
436
437        Ok(new_logger)
438    }
439
440    /// Forks a separates new logger with a new name.
441    ///
442    /// This function creates a new logger object that inherits logger
443    /// properties from `Arc<Self>` and rename the new logger object to the
444    /// given name. The created new logger object will be a separate object
445    /// from `Arc<Self>`. (No ownership sharing)
446    ///
447    /// This is a shorthand wrapper for [`Logger::fork_with`].
448    ///
449    /// # Examples
450    ///
451    /// ```
452    /// # use std::sync::Arc;
453    /// # use spdlog::prelude::*;
454    /// #
455    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
456    /// let old = Arc::new(Logger::builder().name("dog").build()?);
457    /// let new = old.fork_with_name(Some("cat"))?;
458    ///
459    /// assert_eq!(old.name(), Some("dog"));
460    /// assert_eq!(new.name(), Some("cat"));
461    /// # Ok(()) }
462    /// ```
463    pub fn fork_with_name<S>(self: &Arc<Self>, new_name: Option<S>) -> Result<Arc<Self>>
464    where
465        S: Into<String>,
466    {
467        self.fork_with(|new| {
468            new.set_name(new_name).map_err(InvalidArgumentError::from)?;
469            Ok(())
470        })
471    }
472
473    // This will lose the periodic flush property, if any.
474    #[must_use]
475    fn clone_lossy(&self) -> Self {
476        Logger {
477            name: self.name.clone(),
478            level_filter: Atomic::new(self.level_filter()),
479            sinks: self.sinks.clone(),
480            flush_level_filter: Atomic::new(self.flush_level_filter()),
481            periodic_flusher: Mutex::new(None),
482            error_handler: RwLock::new(self.error_handler.read_expect().clone()),
483        }
484    }
485
486    fn sink_record(&self, record: &Record) {
487        self.sinks.iter().for_each(|sink| {
488            if sink.should_log(record.level()) {
489                if let Err(err) = sink.log(record) {
490                    self.handle_error(err);
491                }
492            }
493        });
494
495        if self.should_flush(record) {
496            self.flush();
497        }
498    }
499
500    fn flush_sinks(&self) {
501        self.sinks.iter().for_each(|sink| {
502            if let Err(err) = sink.flush() {
503                self.handle_error(err);
504            }
505        });
506    }
507
508    fn handle_error(&self, err: Error) {
509        self.error_handler.read_expect().call_internal(
510            format!(
511                "Logger ({})",
512                self.name.as_ref().map_or("*no name*", String::as_str)
513            ),
514            err,
515        );
516    }
517
518    #[must_use]
519    fn should_flush(&self, record: &Record) -> bool {
520        self.flush_level_filter().test(record.level())
521    }
522}
523
524impl Clone for Logger {
525    /// Clones the `Logger`.
526    ///
527    /// # Panics
528    ///
529    /// Panics if [`Logger::set_flush_period`] is called with `Some` value and
530    /// then clones the `Logger` instead of the `Arc<Logger>`.
531    fn clone(&self) -> Self {
532        if self.periodic_flusher.lock_expect().is_some() {
533            panic!(
534                "you can't clone a `Logger` with a `flush_period` value, \
535                 clone a `Arc<Logger>` instead."
536            );
537        }
538        self.clone_lossy()
539    }
540}
541
542#[allow(missing_docs)]
543#[derive(Clone)]
544pub struct LoggerBuilder {
545    name: Option<String>,
546    level_filter: LevelFilter,
547    sinks: Sinks,
548    flush_level_filter: LevelFilter,
549    error_handler: ErrorHandler,
550}
551
552impl LoggerBuilder {
553    /// Constructs a `LoggerBuilder`.
554    #[allow(clippy::new_without_default)]
555    #[deprecated(
556        since = "0.3.0",
557        note = "it may be removed in the future, use `Logger::builder()` instead"
558    )]
559    #[must_use]
560    pub fn new() -> Self {
561        Logger::builder()
562    }
563
564    /// Sets the name of the logger.
565    ///
566    /// This parameter is **optional**.
567    ///
568    /// # Requirements
569    ///
570    /// A logger name should not contain any of these characters:
571    /// `,` `=` `*` `?` `$` `{` `}` `"` `'` `;`,
572    /// and cannot start or end with a whitespace.
573    ///
574    /// Otherwise, [`LoggerBuilder::build`] will return an error.
575    pub fn name<S>(&mut self, name: S) -> &mut Self
576    where
577        S: Into<String>,
578    {
579        self.name = Some(name.into());
580        self
581    }
582
583    /// Sets the log level filter.
584    ///
585    /// This parameter is **optional**.
586    pub fn level_filter(&mut self, level_filter: LevelFilter) -> &mut Self {
587        self.level_filter = level_filter;
588        self
589    }
590
591    /// Add a [`Sink`].
592    pub fn sink(&mut self, sink: Arc<dyn Sink>) -> &mut Self {
593        self.sinks.push(sink);
594        self
595    }
596
597    /// Add multiple [`Sink`]s.
598    pub fn sinks<I>(&mut self, sinks: I) -> &mut Self
599    where
600        I: IntoIterator<Item = Arc<dyn Sink>>,
601    {
602        self.sinks.append(&mut sinks.into_iter().collect());
603        self
604    }
605
606    /// Sets the flush level filter.
607    ///
608    /// This parameter is **optional**.
609    ///
610    /// See the documentation of [`Logger::set_flush_level_filter`] for the
611    /// description of this parameter.
612    pub fn flush_level_filter(&mut self, level_filter: LevelFilter) -> &mut Self {
613        self.flush_level_filter = level_filter;
614        self
615    }
616
617    /// Sets the error handler.
618    ///
619    /// This parameter is **optional**.
620    ///
621    /// See the documentation of [`Logger::set_error_handler`] for the
622    /// description of this parameter.
623    pub fn error_handler<F: Into<ErrorHandler>>(&mut self, handler: F) -> &mut Self {
624        self.error_handler = handler.into();
625        self
626    }
627
628    /// Builds a [`Logger`].
629    pub fn build(&mut self) -> Result<Logger> {
630        self.build_inner(self.preset_level(false))
631    }
632
633    pub(crate) fn build_default(&mut self) -> Result<Logger> {
634        self.build_inner(self.preset_level(true))
635    }
636
637    #[must_use]
638    fn preset_level(&self, is_default: bool) -> Option<LevelFilter> {
639        if is_default {
640            env_level::logger_level(env_level::LoggerKind::Default)
641        } else {
642            env_level::logger_level(env_level::LoggerKind::Other(self.name.as_deref()))
643        }
644    }
645
646    fn build_inner(&mut self, preset_level: Option<LevelFilter>) -> Result<Logger> {
647        if let Some(name) = &self.name {
648            check_logger_name(name).map_err(InvalidArgumentError::from)?;
649        }
650
651        let logger = Logger {
652            name: self.name.clone(),
653            level_filter: Atomic::new(self.level_filter),
654            sinks: self.sinks.clone(),
655            flush_level_filter: Atomic::new(self.flush_level_filter),
656            error_handler: RwLock::new(self.error_handler.clone()),
657            periodic_flusher: Mutex::new(None),
658        };
659
660        if let Some(preset_level) = preset_level {
661            logger.set_level_filter(preset_level);
662        }
663
664        Ok(logger)
665    }
666
667    #[cfg(test)]
668    #[must_use]
669    fn build_inner_for_test(&mut self, env_level: &str, is_default: bool) -> Logger {
670        let preset_level = if is_default {
671            env_level::logger_level_inner(
672                &env_level::from_str_inner(env_level).unwrap(),
673                env_level::LoggerKind::Default,
674            )
675        } else {
676            env_level::logger_level_inner(
677                &env_level::from_str_inner(env_level).unwrap(),
678                env_level::LoggerKind::Other(self.name.as_deref()),
679            )
680        };
681
682        self.build_inner(preset_level).unwrap()
683    }
684}
685
686#[cfg(test)]
687mod tests {
688    use std::{thread, time::Duration};
689
690    use super::*;
691    use crate::{prelude::*, test_utils::*};
692
693    #[test]
694    fn logger_traits() {
695        assert_trait!(Logger: Send + Sync + Debug);
696    }
697
698    #[test]
699    fn flush_level() {
700        let test_sink = Arc::new(TestSink::new());
701        let test_logger = Logger::builder().sink(test_sink.clone()).build().unwrap();
702
703        trace!(logger: test_logger, "");
704        error!(logger: test_logger, "");
705        assert_eq!(test_sink.flush_count(), 0);
706        test_sink.reset();
707
708        test_logger.set_flush_level_filter(LevelFilter::MoreSevereEqual(Level::Warn));
709        debug!(logger: test_logger, "");
710        warn!(logger: test_logger, "");
711        assert_eq!(test_sink.flush_count(), 1);
712        test_sink.reset();
713
714        test_logger.set_flush_level_filter(LevelFilter::Off);
715        info!(logger: test_logger, "");
716        trace!(logger: test_logger, "");
717        assert_eq!(test_sink.flush_count(), 0);
718        test_sink.reset();
719
720        test_logger.set_flush_level_filter(LevelFilter::MoreSevereEqual(Level::Trace));
721        info!(logger: test_logger, "");
722        warn!(logger: test_logger, "");
723        assert_eq!(test_sink.flush_count(), 2);
724        test_sink.reset();
725    }
726
727    #[test]
728    fn periodic_flush() {
729        let test_sink = Arc::new(TestSink::new());
730        let test_logger = Arc::new(Logger::builder().sink(test_sink.clone()).build().unwrap());
731
732        test_logger.set_flush_period(Some(Duration::from_secs(1)));
733
734        assert_eq!(test_sink.flush_count(), 0);
735
736        thread::sleep(Duration::from_millis(1250));
737        assert_eq!(test_sink.flush_count(), 1);
738
739        thread::sleep(Duration::from_millis(1250));
740        assert_eq!(test_sink.flush_count(), 2);
741
742        test_logger.set_flush_period(None);
743
744        thread::sleep(Duration::from_millis(1250));
745        assert_eq!(test_sink.flush_count(), 2);
746
747        test_logger.set_flush_period(Some(Duration::from_secs(1)));
748
749        thread::sleep(Duration::from_millis(1250));
750        assert_eq!(test_sink.flush_count(), 3);
751    }
752
753    #[test]
754    fn builder_name() {
755        Logger::builder().name("hello-world");
756
757        macro_rules! assert_name_err {
758            ( $($name:literal),+ $(,)? ) => {
759                $(match Logger::builder().name($name).build() {
760                    Err(Error::InvalidArgument(InvalidArgumentError::LoggerName(err))) => {
761                        assert_eq!(err.name(), $name)
762                    }
763                    _ => panic!("test case '{}' failed", $name),
764                })+
765            };
766        }
767
768        assert_name_err! {
769            " hello", "hello ",
770            "hello,world", "hello=world", "hello*world", "hello?world", "hello$world",
771            "hello{world", "hello}world", r#"hello"world"#, "hello'world", "hello;world",
772        };
773    }
774
775    #[test]
776    fn env_level() {
777        macro_rules! assert_levels {
778            ($env_level:literal, DEFAULT => $default:expr, UNNAMED => $unnamed:expr, NAMED($name:literal) => $named:expr $(,)?) => {
779                assert_eq!(
780                    Logger::builder()
781                        .build_inner_for_test($env_level, true)
782                        .level_filter(),
783                    $default
784                );
785                assert_eq!(
786                    Logger::builder()
787                        .build_inner_for_test($env_level, false)
788                        .level_filter(),
789                    $unnamed
790                );
791                assert_eq!(
792                    Logger::builder()
793                        .name($name)
794                        .build_inner_for_test($env_level, false)
795                        .level_filter(),
796                    $named
797                );
798            };
799            (_, DEFAULT => $default:expr, UNNAMED => $unnamed:expr, NAMED($name:literal) => $named:expr $(,)?) => {
800                assert_eq!(
801                    Logger::builder().build_default().unwrap().level_filter(),
802                    $default
803                );
804                assert_eq!(Logger::builder().build().unwrap().level_filter(), $unnamed);
805                assert_eq!(
806                    Logger::builder()
807                        .name($name)
808                        .build()
809                        .unwrap()
810                        .level_filter(),
811                    $named
812                );
813            };
814        }
815
816        let unchanged = LevelFilter::MoreSevereEqual(Level::Info);
817
818        assert_levels!(
819            _,
820            DEFAULT => unchanged,
821            UNNAMED => unchanged,
822            NAMED("name") => unchanged,
823        );
824
825        assert_levels!(
826            "deBug",
827            DEFAULT => LevelFilter::MoreSevereEqual(Level::Debug),
828            UNNAMED => unchanged,
829            NAMED("name") => unchanged,
830        );
831
832        assert_levels!(
833            "deBug,*=tRace",
834            DEFAULT => LevelFilter::MoreSevereEqual(Level::Debug),
835            UNNAMED => LevelFilter::MoreSevereEqual(Level::Trace),
836            NAMED("name") => LevelFilter::MoreSevereEqual(Level::Trace),
837        );
838
839        assert_levels!(
840            "=trAce",
841            DEFAULT => unchanged,
842            UNNAMED => LevelFilter::MoreSevereEqual(Level::Trace),
843            NAMED("name") => unchanged,
844        );
845
846        assert_levels!(
847            "*=waRn",
848            DEFAULT => unchanged,
849            UNNAMED => LevelFilter::MoreSevereEqual(Level::Warn),
850            NAMED("name") => LevelFilter::MoreSevereEqual(Level::Warn),
851        );
852
853        assert_levels!(
854            "=eRror,*=waRn",
855            DEFAULT => unchanged,
856            UNNAMED => LevelFilter::MoreSevereEqual(Level::Error),
857            NAMED("name") => LevelFilter::MoreSevereEqual(Level::Warn),
858        );
859
860        assert_levels!(
861            "=eRror,*=waRn,name=trAce",
862            DEFAULT => unchanged,
863            UNNAMED => LevelFilter::MoreSevereEqual(Level::Error),
864            NAMED("name") => LevelFilter::MoreSevereEqual(Level::Trace),
865        );
866
867        assert_levels!(
868            "all,*=all",
869            DEFAULT => LevelFilter::All,
870            UNNAMED => LevelFilter::All,
871            NAMED("name") => LevelFilter::All,
872        );
873
874        assert_levels!(
875            "off,*=all",
876            DEFAULT => LevelFilter::Off,
877            UNNAMED => LevelFilter::All,
878            NAMED("name") => LevelFilter::All,
879        );
880    }
881
882    #[test]
883    fn fork_logger() {
884        let test_sink = (Arc::new(TestSink::new()), Arc::new(TestSink::new()));
885        let logger = Arc::new(build_test_logger(|b| b.sink(test_sink.0.clone())));
886
887        assert!(logger.name().is_none());
888        assert_eq!(test_sink.0.log_count(), 0);
889        assert_eq!(test_sink.0.flush_count(), 0);
890        assert_eq!(test_sink.1.log_count(), 0);
891        assert_eq!(test_sink.1.flush_count(), 0);
892
893        info!(logger: logger, "qwq");
894        assert!(logger.name().is_none());
895        assert_eq!(test_sink.0.log_count(), 1);
896        assert_eq!(test_sink.0.flush_count(), 0);
897        assert_eq!(test_sink.1.log_count(), 0);
898        assert_eq!(test_sink.1.flush_count(), 0);
899
900        let old = logger;
901        let new = old.fork_with_name(Some("cat")).unwrap();
902        info!(logger: new, "meow");
903        assert!(old.name().is_none());
904        assert_eq!(new.name(), Some("cat"));
905        assert_eq!(test_sink.0.log_count(), 2);
906        assert_eq!(test_sink.0.flush_count(), 0);
907        assert_eq!(test_sink.1.log_count(), 0);
908        assert_eq!(test_sink.1.flush_count(), 0);
909
910        let old = new;
911        let new = old
912            .fork_with(|new| {
913                new.set_name(Some("dog")).unwrap();
914                new.sinks_mut().push(test_sink.1.clone());
915                Ok(())
916            })
917            .unwrap();
918        info!(logger: new, "woof");
919        assert_eq!(old.name(), Some("cat"));
920        assert_eq!(new.name(), Some("dog"));
921        assert_eq!(test_sink.0.log_count(), 3);
922        assert_eq!(test_sink.0.flush_count(), 0);
923        assert_eq!(test_sink.1.log_count(), 1);
924        assert_eq!(test_sink.1.flush_count(), 0);
925
926        assert!(matches!(
927            new.fork_with_name(Some("invalid,name")),
928            Err(Error::InvalidArgument(InvalidArgumentError::LoggerName(_)))
929        ));
930
931        assert!(new
932            .fork_with_name(None as Option<&str>)
933            .unwrap()
934            .name()
935            .is_none());
936
937        let test_sink = (Arc::new(TestSink::new()), Arc::new(TestSink::new()));
938        let old = Arc::new(build_test_logger(|b| b.sink(test_sink.0.clone())));
939        old.set_flush_period(Some(Duration::from_secs(1)));
940        std::thread::sleep(Duration::from_millis(1250));
941
942        let _new = old
943            .fork_with(|new| {
944                new.sinks_mut().clear();
945                new.sinks_mut().push(test_sink.1.clone());
946                Ok(())
947            })
948            .unwrap();
949        std::thread::sleep(Duration::from_millis(1250));
950
951        assert_eq!(test_sink.0.log_count(), 0);
952        assert_eq!(test_sink.0.flush_count(), 2);
953        assert_eq!(test_sink.1.log_count(), 0);
954        assert_eq!(test_sink.1.flush_count(), 1);
955    }
956}