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: SpinRwLock<Option<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]      | [default error handler] |
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    /// [default error handler]: error/index.html#default-error-handler
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: None,
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(Some(|err| {
383    ///     panic!("An error occurred in the default logger: {}", err)
384    /// }));
385    /// ```
386    pub fn set_error_handler(&self, handler: Option<ErrorHandler>) {
387        *self.error_handler.write() = handler;
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: SpinRwLock::new(*self.error_handler.read()),
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        if let Some(handler) = self.error_handler.read().as_ref() {
510            handler(err)
511        } else {
512            crate::default_error_handler(
513                format!(
514                    "Logger ({})",
515                    self.name.as_ref().map_or("*no name*", String::as_str)
516                ),
517                err,
518            );
519        }
520    }
521
522    #[must_use]
523    fn should_flush(&self, record: &Record) -> bool {
524        self.flush_level_filter().test(record.level())
525    }
526}
527
528impl Clone for Logger {
529    /// Clones the `Logger`.
530    ///
531    /// # Panics
532    ///
533    /// Panics if [`Logger::set_flush_period`] is called with `Some` value and
534    /// then clones the `Logger` instead of the `Arc<Logger>`.
535    fn clone(&self) -> Self {
536        if self.periodic_flusher.lock_expect().is_some() {
537            panic!(
538                "you can't clone a `Logger` with a `flush_period` value, \
539                 clone a `Arc<Logger>` instead."
540            );
541        }
542        self.clone_lossy()
543    }
544}
545
546#[allow(missing_docs)]
547#[derive(Clone)]
548pub struct LoggerBuilder {
549    name: Option<String>,
550    level_filter: LevelFilter,
551    sinks: Sinks,
552    flush_level_filter: LevelFilter,
553    error_handler: Option<ErrorHandler>,
554}
555
556impl LoggerBuilder {
557    /// Constructs a `LoggerBuilder`.
558    #[allow(clippy::new_without_default)]
559    #[deprecated(
560        since = "0.3.0",
561        note = "it may be removed in the future, use `Logger::builder()` instead"
562    )]
563    #[must_use]
564    pub fn new() -> Self {
565        Logger::builder()
566    }
567
568    /// Sets the name of the logger.
569    ///
570    /// This parameter is **optional**.
571    ///
572    /// # Requirements
573    ///
574    /// A logger name should not contain any of these characters:
575    /// `,` `=` `*` `?` `$` `{` `}` `"` `'` `;`,
576    /// and cannot start or end with a whitespace.
577    ///
578    /// Otherwise, [`LoggerBuilder::build`] will return an error.
579    pub fn name<S>(&mut self, name: S) -> &mut Self
580    where
581        S: Into<String>,
582    {
583        self.name = Some(name.into());
584        self
585    }
586
587    /// Sets the log level filter.
588    ///
589    /// This parameter is **optional**.
590    pub fn level_filter(&mut self, level_filter: LevelFilter) -> &mut Self {
591        self.level_filter = level_filter;
592        self
593    }
594
595    /// Add a [`Sink`].
596    pub fn sink(&mut self, sink: Arc<dyn Sink>) -> &mut Self {
597        self.sinks.push(sink);
598        self
599    }
600
601    /// Add multiple [`Sink`]s.
602    pub fn sinks<I>(&mut self, sinks: I) -> &mut Self
603    where
604        I: IntoIterator<Item = Arc<dyn Sink>>,
605    {
606        self.sinks.append(&mut sinks.into_iter().collect());
607        self
608    }
609
610    /// Sets the flush level filter.
611    ///
612    /// This paramter is **optional**.
613    ///
614    /// See the documentation of [`Logger::set_flush_level_filter`] for the
615    /// description of this parameter.
616    pub fn flush_level_filter(&mut self, level_filter: LevelFilter) -> &mut Self {
617        self.flush_level_filter = level_filter;
618        self
619    }
620
621    /// Sets the error handler.
622    ///
623    /// This parameter is **optional**.
624    ///
625    /// See the documentation of [`Logger::set_error_handler`] for the
626    /// description of this parameter.
627    pub fn error_handler(&mut self, handler: ErrorHandler) -> &mut Self {
628        self.error_handler = Some(handler);
629        self
630    }
631
632    /// Builds a [`Logger`].
633    pub fn build(&mut self) -> Result<Logger> {
634        self.build_inner(self.preset_level(false))
635    }
636
637    pub(crate) fn build_default(&mut self) -> Result<Logger> {
638        self.build_inner(self.preset_level(true))
639    }
640
641    #[must_use]
642    fn preset_level(&self, is_default: bool) -> Option<LevelFilter> {
643        if is_default {
644            env_level::logger_level(env_level::LoggerKind::Default)
645        } else {
646            env_level::logger_level(env_level::LoggerKind::Other(self.name.as_deref()))
647        }
648    }
649
650    fn build_inner(&mut self, preset_level: Option<LevelFilter>) -> Result<Logger> {
651        if let Some(name) = &self.name {
652            check_logger_name(name).map_err(InvalidArgumentError::from)?;
653        }
654
655        let logger = Logger {
656            name: self.name.clone(),
657            level_filter: Atomic::new(self.level_filter),
658            sinks: self.sinks.clone(),
659            flush_level_filter: Atomic::new(self.flush_level_filter),
660            error_handler: SpinRwLock::new(self.error_handler),
661            periodic_flusher: Mutex::new(None),
662        };
663
664        if let Some(preset_level) = preset_level {
665            logger.set_level_filter(preset_level);
666        }
667
668        Ok(logger)
669    }
670
671    #[cfg(test)]
672    #[must_use]
673    fn build_inner_for_test(&mut self, env_level: &str, is_default: bool) -> Logger {
674        let preset_level = if is_default {
675            env_level::logger_level_inner(
676                &env_level::from_str_inner(env_level).unwrap(),
677                env_level::LoggerKind::Default,
678            )
679        } else {
680            env_level::logger_level_inner(
681                &env_level::from_str_inner(env_level).unwrap(),
682                env_level::LoggerKind::Other(self.name.as_deref()),
683            )
684        };
685
686        self.build_inner(preset_level).unwrap()
687    }
688}
689
690#[cfg(test)]
691mod tests {
692    use std::{thread, time::Duration};
693
694    use super::*;
695    use crate::{prelude::*, test_utils::*};
696
697    #[test]
698    fn logger_traits() {
699        assert_trait!(Logger: Send + Sync + Debug);
700    }
701
702    #[test]
703    fn flush_level() {
704        let test_sink = Arc::new(TestSink::new());
705        let test_logger = Logger::builder().sink(test_sink.clone()).build().unwrap();
706
707        trace!(logger: test_logger, "");
708        error!(logger: test_logger, "");
709        assert_eq!(test_sink.flush_count(), 0);
710        test_sink.reset();
711
712        test_logger.set_flush_level_filter(LevelFilter::MoreSevereEqual(Level::Warn));
713        debug!(logger: test_logger, "");
714        warn!(logger: test_logger, "");
715        assert_eq!(test_sink.flush_count(), 1);
716        test_sink.reset();
717
718        test_logger.set_flush_level_filter(LevelFilter::Off);
719        info!(logger: test_logger, "");
720        trace!(logger: test_logger, "");
721        assert_eq!(test_sink.flush_count(), 0);
722        test_sink.reset();
723
724        test_logger.set_flush_level_filter(LevelFilter::MoreSevereEqual(Level::Trace));
725        info!(logger: test_logger, "");
726        warn!(logger: test_logger, "");
727        assert_eq!(test_sink.flush_count(), 2);
728        test_sink.reset();
729    }
730
731    #[test]
732    fn periodic_flush() {
733        let test_sink = Arc::new(TestSink::new());
734        let test_logger = Arc::new(Logger::builder().sink(test_sink.clone()).build().unwrap());
735
736        test_logger.set_flush_period(Some(Duration::from_secs(1)));
737
738        assert_eq!(test_sink.flush_count(), 0);
739
740        thread::sleep(Duration::from_millis(1250));
741        assert_eq!(test_sink.flush_count(), 1);
742
743        thread::sleep(Duration::from_millis(1250));
744        assert_eq!(test_sink.flush_count(), 2);
745
746        test_logger.set_flush_period(None);
747
748        thread::sleep(Duration::from_millis(1250));
749        assert_eq!(test_sink.flush_count(), 2);
750
751        test_logger.set_flush_period(Some(Duration::from_secs(1)));
752
753        thread::sleep(Duration::from_millis(1250));
754        assert_eq!(test_sink.flush_count(), 3);
755    }
756
757    #[test]
758    fn builder_name() {
759        Logger::builder().name("hello-world");
760
761        macro_rules! assert_name_err {
762            ( $($name:literal),+ $(,)? ) => {
763                $(match Logger::builder().name($name).build() {
764                    Err(Error::InvalidArgument(InvalidArgumentError::LoggerName(err))) => {
765                        assert_eq!(err.name(), $name)
766                    }
767                    _ => panic!("test case '{}' failed", $name),
768                })+
769            };
770        }
771
772        assert_name_err! {
773            " hello", "hello ",
774            "hello,world", "hello=world", "hello*world", "hello?world", "hello$world",
775            "hello{world", "hello}world", r#"hello"world"#, "hello'world", "hello;world",
776        };
777    }
778
779    #[test]
780    fn env_level() {
781        macro_rules! assert_levels {
782            ($env_level:literal, DEFAULT => $default:expr, UNNAMED => $unnamed:expr, NAMED($name:literal) => $named:expr $(,)?) => {
783                assert_eq!(
784                    Logger::builder()
785                        .build_inner_for_test($env_level, true)
786                        .level_filter(),
787                    $default
788                );
789                assert_eq!(
790                    Logger::builder()
791                        .build_inner_for_test($env_level, false)
792                        .level_filter(),
793                    $unnamed
794                );
795                assert_eq!(
796                    Logger::builder()
797                        .name($name)
798                        .build_inner_for_test($env_level, false)
799                        .level_filter(),
800                    $named
801                );
802            };
803            (_, DEFAULT => $default:expr, UNNAMED => $unnamed:expr, NAMED($name:literal) => $named:expr $(,)?) => {
804                assert_eq!(
805                    Logger::builder().build_default().unwrap().level_filter(),
806                    $default
807                );
808                assert_eq!(Logger::builder().build().unwrap().level_filter(), $unnamed);
809                assert_eq!(
810                    Logger::builder()
811                        .name($name)
812                        .build()
813                        .unwrap()
814                        .level_filter(),
815                    $named
816                );
817            };
818        }
819
820        let unchanged = LevelFilter::MoreSevereEqual(Level::Info);
821
822        assert_levels!(
823            _,
824            DEFAULT => unchanged,
825            UNNAMED => unchanged,
826            NAMED("name") => unchanged,
827        );
828
829        assert_levels!(
830            "deBug",
831            DEFAULT => LevelFilter::MoreSevereEqual(Level::Debug),
832            UNNAMED => unchanged,
833            NAMED("name") => unchanged,
834        );
835
836        assert_levels!(
837            "deBug,*=tRace",
838            DEFAULT => LevelFilter::MoreSevereEqual(Level::Debug),
839            UNNAMED => LevelFilter::MoreSevereEqual(Level::Trace),
840            NAMED("name") => LevelFilter::MoreSevereEqual(Level::Trace),
841        );
842
843        assert_levels!(
844            "=trAce",
845            DEFAULT => unchanged,
846            UNNAMED => LevelFilter::MoreSevereEqual(Level::Trace),
847            NAMED("name") => unchanged,
848        );
849
850        assert_levels!(
851            "*=waRn",
852            DEFAULT => unchanged,
853            UNNAMED => LevelFilter::MoreSevereEqual(Level::Warn),
854            NAMED("name") => LevelFilter::MoreSevereEqual(Level::Warn),
855        );
856
857        assert_levels!(
858            "=eRror,*=waRn",
859            DEFAULT => unchanged,
860            UNNAMED => LevelFilter::MoreSevereEqual(Level::Error),
861            NAMED("name") => LevelFilter::MoreSevereEqual(Level::Warn),
862        );
863
864        assert_levels!(
865            "=eRror,*=waRn,name=trAce",
866            DEFAULT => unchanged,
867            UNNAMED => LevelFilter::MoreSevereEqual(Level::Error),
868            NAMED("name") => LevelFilter::MoreSevereEqual(Level::Trace),
869        );
870
871        assert_levels!(
872            "all,*=all",
873            DEFAULT => LevelFilter::All,
874            UNNAMED => LevelFilter::All,
875            NAMED("name") => LevelFilter::All,
876        );
877
878        assert_levels!(
879            "off,*=all",
880            DEFAULT => LevelFilter::Off,
881            UNNAMED => LevelFilter::All,
882            NAMED("name") => LevelFilter::All,
883        );
884    }
885
886    #[test]
887    fn fork_logger() {
888        let test_sink = (Arc::new(TestSink::new()), Arc::new(TestSink::new()));
889        let logger = Arc::new(build_test_logger(|b| b.sink(test_sink.0.clone())));
890
891        assert!(logger.name().is_none());
892        assert_eq!(test_sink.0.log_count(), 0);
893        assert_eq!(test_sink.0.flush_count(), 0);
894        assert_eq!(test_sink.1.log_count(), 0);
895        assert_eq!(test_sink.1.flush_count(), 0);
896
897        info!(logger: logger, "qwq");
898        assert!(logger.name().is_none());
899        assert_eq!(test_sink.0.log_count(), 1);
900        assert_eq!(test_sink.0.flush_count(), 0);
901        assert_eq!(test_sink.1.log_count(), 0);
902        assert_eq!(test_sink.1.flush_count(), 0);
903
904        let old = logger;
905        let new = old.fork_with_name(Some("cat")).unwrap();
906        info!(logger: new, "meow");
907        assert!(old.name().is_none());
908        assert_eq!(new.name(), Some("cat"));
909        assert_eq!(test_sink.0.log_count(), 2);
910        assert_eq!(test_sink.0.flush_count(), 0);
911        assert_eq!(test_sink.1.log_count(), 0);
912        assert_eq!(test_sink.1.flush_count(), 0);
913
914        let old = new;
915        let new = old
916            .fork_with(|new| {
917                new.set_name(Some("dog")).unwrap();
918                new.sinks_mut().push(test_sink.1.clone());
919                Ok(())
920            })
921            .unwrap();
922        info!(logger: new, "woof");
923        assert_eq!(old.name(), Some("cat"));
924        assert_eq!(new.name(), Some("dog"));
925        assert_eq!(test_sink.0.log_count(), 3);
926        assert_eq!(test_sink.0.flush_count(), 0);
927        assert_eq!(test_sink.1.log_count(), 1);
928        assert_eq!(test_sink.1.flush_count(), 0);
929
930        assert!(matches!(
931            new.fork_with_name(Some("invalid,name")),
932            Err(Error::InvalidArgument(InvalidArgumentError::LoggerName(_)))
933        ));
934
935        assert!(new
936            .fork_with_name(None as Option<&str>)
937            .unwrap()
938            .name()
939            .is_none());
940
941        let test_sink = (Arc::new(TestSink::new()), Arc::new(TestSink::new()));
942        let old = Arc::new(build_test_logger(|b| b.sink(test_sink.0.clone())));
943        old.set_flush_period(Some(Duration::from_secs(1)));
944        std::thread::sleep(Duration::from_millis(1250));
945
946        let _new = old
947            .fork_with(|new| {
948                new.sinks_mut().clear();
949                new.sinks_mut().push(test_sink.1.clone());
950                Ok(())
951            })
952            .unwrap();
953        std::thread::sleep(Duration::from_millis(1250));
954
955        assert_eq!(test_sink.0.log_count(), 0);
956        assert_eq!(test_sink.0.flush_count(), 2);
957        assert_eq!(test_sink.1.log_count(), 0);
958        assert_eq!(test_sink.1.flush_count(), 1);
959    }
960}