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