Skip to main content

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()
381    ///     .set_error_handler(|err| panic!("An error occurred in the default logger: {}", err));
382    /// ```
383    pub fn set_error_handler<F: Into<ErrorHandler>>(&self, handler: F) {
384        *self.error_handler.write_expect() = handler.into();
385    }
386
387    /// Forks and configures a separate new logger.
388    ///
389    /// This function creates a new logger object that inherits logger
390    /// properties from `Arc<Self>`. Then this function calls the given
391    /// `modifier` function which configures the properties on the new
392    /// logger object. The created new logger object will be a separate
393    /// object from `Arc<Self>`. (No ownership sharing)
394    ///
395    /// # Examples
396    ///
397    /// ```
398    #[doc = include_str!(concat!(env!("OUT_DIR"), "/test_utils/common_for_doc_test.rs"))]
399    /// # use std::sync::Arc;
400    /// # use spdlog::prelude::*;
401    /// #
402    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
403    /// # let test_sink = Arc::new(test_utils::StringSink::new());
404    /// let old: Arc<Logger> = /* ... */
405    /// # Logger::builder().build_arc().unwrap();
406    /// // Fork from an existing logger and add a new sink.
407    /// # let new_sink = test_sink.clone();
408    /// let new = old.fork_with(|new| {
409    ///     new.sinks_mut().push(new_sink);
410    ///     Ok(())
411    /// })?;
412    ///
413    /// # info!(logger: new, "first line");
414    /// info!(logger: new, "this record will be written to `new_sink`");
415    /// # assert_eq!(test_sink.clone_string().lines().count(), 2);
416    /// info!(logger: old, "this record will not be written to `new_sink`");
417    /// # assert_eq!(test_sink.clone_string().lines().count(), 2);
418    /// # Ok(()) }
419    /// ```
420    pub fn fork_with<F>(self: &Arc<Self>, modifier: F) -> Result<Arc<Self>>
421    where
422        F: FnOnce(&mut Logger) -> Result<()>,
423    {
424        let flush_period = self.periodic_flusher.lock_expect().as_ref().map(|v| v.0);
425
426        let mut new_logger = self.clone_lossy();
427        modifier(&mut new_logger)?;
428
429        let new_logger = Arc::new(new_logger);
430        if let Some(interval) = flush_period {
431            new_logger.set_flush_period(Some(interval));
432        }
433
434        Ok(new_logger)
435    }
436
437    /// Forks a separates new logger with a new name.
438    ///
439    /// This function creates a new logger object that inherits logger
440    /// properties from `Arc<Self>` and rename the new logger object to the
441    /// given name. The created new logger object will be a separate object
442    /// from `Arc<Self>`. (No ownership sharing)
443    ///
444    /// This is a shorthand wrapper for [`Logger::fork_with`].
445    ///
446    /// # Examples
447    ///
448    /// ```
449    /// # use spdlog::prelude::*;
450    /// #
451    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
452    /// let old = Logger::builder().name("dog").build_arc()?;
453    /// let new = old.fork_with_name(Some("cat"))?;
454    ///
455    /// assert_eq!(old.name(), Some("dog"));
456    /// assert_eq!(new.name(), Some("cat"));
457    /// # Ok(()) }
458    /// ```
459    pub fn fork_with_name<S>(self: &Arc<Self>, new_name: Option<S>) -> Result<Arc<Self>>
460    where
461        S: Into<String>,
462    {
463        self.fork_with(|new| {
464            new.set_name(new_name).map_err(InvalidArgumentError::from)?;
465            Ok(())
466        })
467    }
468
469    // This will lose the periodic flush property, if any.
470    #[must_use]
471    fn clone_lossy(&self) -> Self {
472        Logger {
473            name: self.name.clone(),
474            level_filter: AtomicLevelFilter::new(self.level_filter()),
475            sinks: self.sinks.clone(),
476            flush_level_filter: AtomicLevelFilter::new(self.flush_level_filter()),
477            periodic_flusher: Mutex::new(None),
478            error_handler: RwLock::new(self.error_handler.read_expect().clone()),
479        }
480    }
481
482    fn sink_record(&self, record: &Record) {
483        self.sinks.iter().for_each(|sink| {
484            if sink.should_log(record.level()) {
485                if let Err(err) = sink.log(record) {
486                    self.handle_error(err);
487                }
488            }
489        });
490
491        if self.should_flush(record) {
492            self.flush();
493        }
494    }
495
496    fn flush_sinks_with(&self, with: impl Fn(&dyn Sink) -> Result<()>) {
497        self.sinks.iter().for_each(|sink| {
498            if let Err(err) = with(&**sink) {
499                self.handle_error(err);
500            }
501        });
502    }
503
504    pub(crate) fn flush_sinks_on_exit(&self) {
505        self.flush_sinks_with(|sink| sink.flush_on_exit());
506    }
507
508    pub(crate) fn flush_sinks(&self) {
509        self.flush_sinks_with(|sink| sink.flush());
510    }
511
512    fn handle_error(&self, err: Error) {
513        self.error_handler.read_expect().call_internal(
514            format!(
515                "Logger ({})",
516                self.name.as_ref().map_or("*no name*", String::as_str)
517            ),
518            err,
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: 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**, and defaults to `None`.
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**, and defaults to `MoreSevereEqual(Info)`.
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 parameter is **optional**, and defaults to [`LevelFilter::Off`].
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**, and defaults to
624    /// [`ErrorHandler::default()`].
625    ///
626    /// See the documentation of [`Logger::set_error_handler`] for the
627    /// description of this parameter.
628    pub fn error_handler<F: Into<ErrorHandler>>(&mut self, handler: F) -> &mut Self {
629        self.error_handler = handler.into();
630        self
631    }
632
633    /// Builds a [`Logger`].
634    pub fn build(&mut self) -> Result<Logger> {
635        self.build_inner(self.preset_level(false))
636    }
637
638    /// Builds a `Arc<Logger>`.
639    ///
640    /// This is a shorthand method for `.build().map(Arc::new)`.
641    ///
642    /// ## Notes
643    ///
644    /// Unlike [`Sink`], which almost always needs to work with [`Arc`].
645    /// [`Logger`] can be used directly in logging macros without `Arc`. So
646    /// when `Arc<Logger>` is not needed, users should just call
647    /// [`LoggerBuilder::build`] to get a `Logger` without `Arc`.
648    pub fn build_arc(&mut self) -> Result<Arc<Logger>> {
649        self.build().map(Arc::new)
650    }
651
652    pub(crate) fn build_default(&mut self) -> Result<Logger> {
653        self.build_inner(self.preset_level(true))
654    }
655
656    #[must_use]
657    fn preset_level(&self, is_default: bool) -> Option<LevelFilter> {
658        if is_default {
659            env_level::logger_level(env_level::LoggerKind::Default)
660        } else {
661            env_level::logger_level(env_level::LoggerKind::Other(self.name.as_deref()))
662        }
663    }
664
665    fn build_inner(&mut self, preset_level: Option<LevelFilter>) -> Result<Logger> {
666        if let Some(name) = &self.name {
667            check_logger_name(name).map_err(InvalidArgumentError::from)?;
668        }
669
670        let logger = Logger {
671            name: self.name.clone(),
672            level_filter: AtomicLevelFilter::new(self.level_filter),
673            sinks: self.sinks.clone(),
674            flush_level_filter: AtomicLevelFilter::new(self.flush_level_filter),
675            error_handler: RwLock::new(self.error_handler.clone()),
676            periodic_flusher: Mutex::new(None),
677        };
678
679        if let Some(preset_level) = preset_level {
680            logger.set_level_filter(preset_level);
681        }
682
683        Ok(logger)
684    }
685
686    #[cfg(test)]
687    #[must_use]
688    fn build_inner_for_test(&mut self, env_level: &str, is_default: bool) -> Logger {
689        let preset_level = if is_default {
690            env_level::logger_level_inner(
691                &env_level::from_str_inner(env_level).unwrap(),
692                env_level::LoggerKind::Default,
693            )
694        } else {
695            env_level::logger_level_inner(
696                &env_level::from_str_inner(env_level).unwrap(),
697                env_level::LoggerKind::Other(self.name.as_deref()),
698            )
699        };
700
701        self.build_inner(preset_level).unwrap()
702    }
703}
704
705#[cfg(test)]
706mod tests {
707    use std::{thread, time::Duration};
708
709    use super::*;
710    use crate::{prelude::*, test_utils::*};
711
712    #[test]
713    fn logger_traits() {
714        assert_trait!(Logger: Send + Sync + Debug);
715    }
716
717    #[test]
718    fn flush_level() {
719        let test_sink = Arc::new(TestSink::new());
720        let test_logger = Logger::builder().sink(test_sink.clone()).build().unwrap();
721
722        trace!(logger: test_logger, "");
723        error!(logger: test_logger, "");
724        assert_eq!(test_sink.flush_count(), 0);
725        test_sink.reset();
726
727        test_logger.set_flush_level_filter(LevelFilter::MoreSevereEqual(Level::Warn));
728        debug!(logger: test_logger, "");
729        warn!(logger: test_logger, "");
730        assert_eq!(test_sink.flush_count(), 1);
731        test_sink.reset();
732
733        test_logger.set_flush_level_filter(LevelFilter::Off);
734        info!(logger: test_logger, "");
735        trace!(logger: test_logger, "");
736        assert_eq!(test_sink.flush_count(), 0);
737        test_sink.reset();
738
739        test_logger.set_flush_level_filter(LevelFilter::MoreSevereEqual(Level::Trace));
740        info!(logger: test_logger, "");
741        warn!(logger: test_logger, "");
742        assert_eq!(test_sink.flush_count(), 2);
743        test_sink.reset();
744    }
745
746    #[test]
747    fn periodic_flush() {
748        let test_sink = Arc::new(TestSink::new());
749        let test_logger = Logger::builder()
750            .sink(test_sink.clone())
751            .build_arc()
752            .unwrap();
753
754        test_logger.set_flush_period(Some(Duration::from_secs(1)));
755
756        assert_eq!(test_sink.flush_count(), 0);
757
758        thread::sleep(Duration::from_millis(1250));
759        assert_eq!(test_sink.flush_count(), 1);
760
761        thread::sleep(Duration::from_millis(1250));
762        assert_eq!(test_sink.flush_count(), 2);
763
764        test_logger.set_flush_period(None);
765
766        thread::sleep(Duration::from_millis(1250));
767        assert_eq!(test_sink.flush_count(), 2);
768
769        test_logger.set_flush_period(Some(Duration::from_secs(1)));
770
771        thread::sleep(Duration::from_millis(1250));
772        assert_eq!(test_sink.flush_count(), 3);
773    }
774
775    #[test]
776    fn builder_name() {
777        Logger::builder().name("hello-world");
778
779        macro_rules! assert_name_err {
780            ( $($name:literal),+ $(,)? ) => {
781                $(match Logger::builder().name($name).build() {
782                    Err(Error::InvalidArgument(InvalidArgumentError::LoggerName(err))) => {
783                        assert_eq!(err.name(), $name)
784                    }
785                    _ => panic!("test case '{}' failed", $name),
786                })+
787            };
788        }
789
790        assert_name_err! {
791            " hello", "hello ",
792            "hello,world", "hello=world", "hello*world", "hello?world", "hello$world",
793            "hello{world", "hello}world", r#"hello"world"#, "hello'world", "hello;world",
794        };
795    }
796
797    #[test]
798    fn env_level() {
799        macro_rules! assert_levels {
800            ($env_level:literal, DEFAULT => $default:expr, UNNAMED => $unnamed:expr, NAMED($name:literal) => $named:expr $(,)?) => {
801                assert_eq!(
802                    Logger::builder()
803                        .build_inner_for_test($env_level, true)
804                        .level_filter(),
805                    $default
806                );
807                assert_eq!(
808                    Logger::builder()
809                        .build_inner_for_test($env_level, false)
810                        .level_filter(),
811                    $unnamed
812                );
813                assert_eq!(
814                    Logger::builder()
815                        .name($name)
816                        .build_inner_for_test($env_level, false)
817                        .level_filter(),
818                    $named
819                );
820            };
821            (_, DEFAULT => $default:expr, UNNAMED => $unnamed:expr, NAMED($name:literal) => $named:expr $(,)?) => {
822                assert_eq!(
823                    Logger::builder().build_default().unwrap().level_filter(),
824                    $default
825                );
826                assert_eq!(Logger::builder().build().unwrap().level_filter(), $unnamed);
827                assert_eq!(
828                    Logger::builder()
829                        .name($name)
830                        .build()
831                        .unwrap()
832                        .level_filter(),
833                    $named
834                );
835            };
836        }
837
838        let unchanged = LevelFilter::MoreSevereEqual(Level::Info);
839
840        assert_levels!(
841            _,
842            DEFAULT => unchanged,
843            UNNAMED => unchanged,
844            NAMED("name") => unchanged,
845        );
846
847        assert_levels!(
848            "deBug",
849            DEFAULT => LevelFilter::MoreSevereEqual(Level::Debug),
850            UNNAMED => unchanged,
851            NAMED("name") => unchanged,
852        );
853
854        assert_levels!(
855            "deBug,*=tRace",
856            DEFAULT => LevelFilter::MoreSevereEqual(Level::Debug),
857            UNNAMED => LevelFilter::MoreSevereEqual(Level::Trace),
858            NAMED("name") => LevelFilter::MoreSevereEqual(Level::Trace),
859        );
860
861        assert_levels!(
862            "=trAce",
863            DEFAULT => unchanged,
864            UNNAMED => LevelFilter::MoreSevereEqual(Level::Trace),
865            NAMED("name") => unchanged,
866        );
867
868        assert_levels!(
869            "*=waRn",
870            DEFAULT => unchanged,
871            UNNAMED => LevelFilter::MoreSevereEqual(Level::Warn),
872            NAMED("name") => LevelFilter::MoreSevereEqual(Level::Warn),
873        );
874
875        assert_levels!(
876            "=eRror,*=waRn",
877            DEFAULT => unchanged,
878            UNNAMED => LevelFilter::MoreSevereEqual(Level::Error),
879            NAMED("name") => LevelFilter::MoreSevereEqual(Level::Warn),
880        );
881
882        assert_levels!(
883            "=eRror,*=waRn,name=trAce",
884            DEFAULT => unchanged,
885            UNNAMED => LevelFilter::MoreSevereEqual(Level::Error),
886            NAMED("name") => LevelFilter::MoreSevereEqual(Level::Trace),
887        );
888
889        assert_levels!(
890            "all,*=all",
891            DEFAULT => LevelFilter::All,
892            UNNAMED => LevelFilter::All,
893            NAMED("name") => LevelFilter::All,
894        );
895
896        assert_levels!(
897            "off,*=all",
898            DEFAULT => LevelFilter::Off,
899            UNNAMED => LevelFilter::All,
900            NAMED("name") => LevelFilter::All,
901        );
902    }
903
904    #[test]
905    fn fork_logger() {
906        let test_sink = (Arc::new(TestSink::new()), Arc::new(TestSink::new()));
907        let logger = Arc::new(build_test_logger(|b| b.sink(test_sink.0.clone())));
908
909        assert!(logger.name().is_none());
910        assert_eq!(test_sink.0.log_count(), 0);
911        assert_eq!(test_sink.0.flush_count(), 0);
912        assert_eq!(test_sink.1.log_count(), 0);
913        assert_eq!(test_sink.1.flush_count(), 0);
914
915        info!(logger: logger, "qwq");
916        assert!(logger.name().is_none());
917        assert_eq!(test_sink.0.log_count(), 1);
918        assert_eq!(test_sink.0.flush_count(), 0);
919        assert_eq!(test_sink.1.log_count(), 0);
920        assert_eq!(test_sink.1.flush_count(), 0);
921
922        let old = logger;
923        let new = old.fork_with_name(Some("cat")).unwrap();
924        info!(logger: new, "meow");
925        assert!(old.name().is_none());
926        assert_eq!(new.name(), Some("cat"));
927        assert_eq!(test_sink.0.log_count(), 2);
928        assert_eq!(test_sink.0.flush_count(), 0);
929        assert_eq!(test_sink.1.log_count(), 0);
930        assert_eq!(test_sink.1.flush_count(), 0);
931
932        let old = new;
933        let new = old
934            .fork_with(|new| {
935                new.set_name(Some("dog")).unwrap();
936                new.sinks_mut().push(test_sink.1.clone());
937                Ok(())
938            })
939            .unwrap();
940        info!(logger: new, "woof");
941        assert_eq!(old.name(), Some("cat"));
942        assert_eq!(new.name(), Some("dog"));
943        assert_eq!(test_sink.0.log_count(), 3);
944        assert_eq!(test_sink.0.flush_count(), 0);
945        assert_eq!(test_sink.1.log_count(), 1);
946        assert_eq!(test_sink.1.flush_count(), 0);
947
948        assert!(matches!(
949            new.fork_with_name(Some("invalid,name")),
950            Err(Error::InvalidArgument(InvalidArgumentError::LoggerName(_)))
951        ));
952
953        assert!(new
954            .fork_with_name(None as Option<&str>)
955            .unwrap()
956            .name()
957            .is_none());
958
959        let test_sink = (Arc::new(TestSink::new()), Arc::new(TestSink::new()));
960        let old = Arc::new(build_test_logger(|b| b.sink(test_sink.0.clone())));
961        old.set_flush_period(Some(Duration::from_secs(1)));
962        std::thread::sleep(Duration::from_millis(1250));
963
964        let _new = old
965            .fork_with(|new| {
966                new.sinks_mut().clear();
967                new.sinks_mut().push(test_sink.1.clone());
968                Ok(())
969            })
970            .unwrap();
971        std::thread::sleep(Duration::from_millis(1250));
972
973        assert_eq!(test_sink.0.log_count(), 0);
974        assert_eq!(test_sink.0.flush_count(), 2);
975        assert_eq!(test_sink.1.log_count(), 0);
976        assert_eq!(test_sink.1.flush_count(), 1);
977    }
978}