Skip to main content

log_lazy/
lib.rs

1//! Lazy logging with bitwise level control.
2//!
3//! Expensive log messages are supplied as closures and are only evaluated when
4//! the requested level is enabled.
5//!
6//! ```
7//! use log_lazy::{levels, LogLazy};
8//!
9//! let log = LogLazy::with_level(levels::ERROR);
10//! let mut evaluated = false;
11//!
12//! log.debug(|| {
13//!     evaluated = true;
14//!     "debug details"
15//! });
16//!
17//! assert!(!evaluated);
18//! ```
19
20use chrono::{Local, SecondsFormat, Utc};
21use std::collections::BTreeMap;
22use std::fmt::{self, Debug, Display};
23use std::sync::Arc;
24
25/// Bitmask used to represent enabled log levels.
26pub type LevelMask = u16;
27
28/// Standard bitwise log level constants.
29pub mod levels {
30    use super::LevelMask;
31
32    pub const NONE: LevelMask = 0;
33    pub const FATAL: LevelMask = 1;
34    pub const ERROR: LevelMask = 2;
35    pub const WARN: LevelMask = 4;
36    pub const INFO: LevelMask = 8;
37    pub const DEBUG: LevelMask = 16;
38    pub const VERBOSE: LevelMask = 32;
39    pub const TRACE: LevelMask = 64;
40    pub const SILLY: LevelMask = 128;
41    pub const ALL: LevelMask = 255;
42    pub const PRODUCTION: LevelMask = FATAL | ERROR | WARN;
43    pub const DEVELOPMENT: LevelMask = FATAL | ERROR | WARN | INFO | DEBUG;
44}
45
46/// One standard output log level.
47#[derive(Clone, Copy, Eq, Hash, PartialEq)]
48pub struct Level {
49    mask: LevelMask,
50    name: &'static str,
51}
52
53impl Level {
54    pub const FATAL: Self = Self::new(levels::FATAL, "fatal");
55    pub const ERROR: Self = Self::new(levels::ERROR, "error");
56    pub const WARN: Self = Self::new(levels::WARN, "warn");
57    pub const INFO: Self = Self::new(levels::INFO, "info");
58    pub const DEBUG: Self = Self::new(levels::DEBUG, "debug");
59    pub const VERBOSE: Self = Self::new(levels::VERBOSE, "verbose");
60    pub const TRACE: Self = Self::new(levels::TRACE, "trace");
61    pub const SILLY: Self = Self::new(levels::SILLY, "silly");
62
63    pub const fn new(mask: LevelMask, name: &'static str) -> Self {
64        Self { mask, name }
65    }
66
67    pub const fn mask(self) -> LevelMask {
68        self.mask
69    }
70
71    pub const fn name(self) -> &'static str {
72        self.name
73    }
74
75    pub const fn from_mask(mask: LevelMask) -> Option<Self> {
76        match mask {
77            levels::FATAL => Some(Self::FATAL),
78            levels::ERROR => Some(Self::ERROR),
79            levels::WARN => Some(Self::WARN),
80            levels::INFO => Some(Self::INFO),
81            levels::DEBUG => Some(Self::DEBUG),
82            levels::VERBOSE => Some(Self::VERBOSE),
83            levels::TRACE => Some(Self::TRACE),
84            levels::SILLY => Some(Self::SILLY),
85            _ => None,
86        }
87    }
88}
89
90impl Debug for Level {
91    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
92        formatter
93            .debug_struct("Level")
94            .field("name", &self.name)
95            .field("mask", &self.mask)
96            .finish()
97    }
98}
99
100/// Standard levels in the order returned by [`LogLazy::get_enabled_levels`].
101pub const STANDARD_LEVELS: [Level; 8] = [
102    Level::FATAL,
103    Level::ERROR,
104    Level::WARN,
105    Level::INFO,
106    Level::DEBUG,
107    Level::VERBOSE,
108    Level::TRACE,
109    Level::SILLY,
110];
111
112/// Level input accepted by the logger.
113#[derive(Debug, Clone, Copy, Eq, PartialEq)]
114pub enum LevelSpec<'a> {
115    Mask(LevelMask),
116    Name(&'a str),
117}
118
119impl<'a> From<Level> for LevelSpec<'a> {
120    fn from(level: Level) -> Self {
121        Self::Mask(level.mask())
122    }
123}
124
125impl<'a> From<LevelMask> for LevelSpec<'a> {
126    fn from(mask: LevelMask) -> Self {
127        Self::Mask(mask)
128    }
129}
130
131impl<'a> From<u8> for LevelSpec<'a> {
132    fn from(mask: u8) -> Self {
133        Self::Mask(LevelMask::from(mask))
134    }
135}
136
137impl<'a> From<u32> for LevelSpec<'a> {
138    fn from(mask: u32) -> Self {
139        Self::Mask(LevelMask::try_from(mask).unwrap_or(levels::NONE))
140    }
141}
142
143impl<'a> From<usize> for LevelSpec<'a> {
144    fn from(mask: usize) -> Self {
145        Self::Mask(LevelMask::try_from(mask).unwrap_or(levels::NONE))
146    }
147}
148
149impl<'a> From<i32> for LevelSpec<'a> {
150    fn from(mask: i32) -> Self {
151        Self::Mask(LevelMask::try_from(mask).unwrap_or(levels::NONE))
152    }
153}
154
155impl<'a> From<&'a str> for LevelSpec<'a> {
156    fn from(name: &'a str) -> Self {
157        Self::Name(name)
158    }
159}
160
161impl<'a> From<&'a String> for LevelSpec<'a> {
162    fn from(name: &'a String) -> Self {
163        Self::Name(name.as_str())
164    }
165}
166
167/// Owned level input accepted by [`LogLazyOptions`].
168#[derive(Debug, Clone, Eq, PartialEq)]
169pub enum OwnedLevelSpec {
170    Mask(LevelMask),
171    Name(String),
172}
173
174impl From<Level> for OwnedLevelSpec {
175    fn from(level: Level) -> Self {
176        Self::Mask(level.mask())
177    }
178}
179
180impl From<LevelMask> for OwnedLevelSpec {
181    fn from(mask: LevelMask) -> Self {
182        Self::Mask(mask)
183    }
184}
185
186impl From<u8> for OwnedLevelSpec {
187    fn from(mask: u8) -> Self {
188        Self::Mask(LevelMask::from(mask))
189    }
190}
191
192impl From<u32> for OwnedLevelSpec {
193    fn from(mask: u32) -> Self {
194        Self::Mask(LevelMask::try_from(mask).unwrap_or(levels::NONE))
195    }
196}
197
198impl From<usize> for OwnedLevelSpec {
199    fn from(mask: usize) -> Self {
200        Self::Mask(LevelMask::try_from(mask).unwrap_or(levels::NONE))
201    }
202}
203
204impl From<i32> for OwnedLevelSpec {
205    fn from(mask: i32) -> Self {
206        Self::Mask(LevelMask::try_from(mask).unwrap_or(levels::NONE))
207    }
208}
209
210impl From<&str> for OwnedLevelSpec {
211    fn from(name: &str) -> Self {
212        Self::Name(name.to_string())
213    }
214}
215
216impl From<String> for OwnedLevelSpec {
217    fn from(name: String) -> Self {
218        Self::Name(name)
219    }
220}
221
222/// Function used to emit an already evaluated log message.
223pub type LogSink = Arc<dyn Fn(Level, String) + Send + Sync + 'static>;
224
225/// One log argument that can be evaluated lazily.
226pub struct LogArg {
227    value: LogArgValue,
228}
229
230enum LogArgValue {
231    Text(String),
232    Lazy(Box<dyn FnOnce() -> String + Send + 'static>),
233}
234
235impl LogArg {
236    /// Creates an already evaluated argument.
237    pub fn text(value: impl Into<String>) -> Self {
238        Self {
239            value: LogArgValue::Text(value.into()),
240        }
241    }
242
243    /// Creates an argument that is evaluated only after the level is enabled
244    /// and preprocessors have run.
245    pub fn lazy<F, M>(value: F) -> Self
246    where
247        F: FnOnce() -> M + Send + 'static,
248        M: Display,
249    {
250        Self {
251            value: LogArgValue::Lazy(Box::new(move || value().to_string())),
252        }
253    }
254
255    /// Returns the text for eager arguments.
256    pub fn as_text(&self) -> Option<&str> {
257        match &self.value {
258            LogArgValue::Text(value) => Some(value.as_str()),
259            LogArgValue::Lazy(_) => None,
260        }
261    }
262
263    /// Returns true when this argument still holds a lazy closure.
264    pub fn is_lazy(&self) -> bool {
265        matches!(self.value, LogArgValue::Lazy(_))
266    }
267
268    fn evaluate(self) -> String {
269        match self.value {
270            LogArgValue::Text(value) => value,
271            LogArgValue::Lazy(value) => value(),
272        }
273    }
274}
275
276impl Debug for LogArg {
277    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
278        match &self.value {
279            LogArgValue::Text(value) => formatter.debug_tuple("LogArg::Text").field(value).finish(),
280            LogArgValue::Lazy(_) => formatter.write_str("LogArg::Lazy(..)"),
281        }
282    }
283}
284
285impl From<&str> for LogArg {
286    fn from(value: &str) -> Self {
287        Self::text(value)
288    }
289}
290
291impl From<String> for LogArg {
292    fn from(value: String) -> Self {
293        Self::text(value)
294    }
295}
296
297/// Options passed to a preprocessor.
298pub struct PreprocessorOptions {
299    pub args: Vec<LogArg>,
300    pub level: Level,
301}
302
303/// Options passed to a postprocessor.
304pub struct PostprocessorOptions {
305    pub message: String,
306    pub level: Level,
307}
308
309/// Function used to transform log arguments before message compilation.
310pub type Preprocessor = Arc<dyn Fn(PreprocessorOptions) -> Vec<LogArg> + Send + Sync + 'static>;
311
312/// Function used to transform a compiled message before output.
313pub type Postprocessor = Arc<dyn Fn(PostprocessorOptions) -> String + Send + Sync + 'static>;
314
315/// Options for constructing a logger with the extensible API.
316#[derive(Clone)]
317pub struct LogLazyOptions {
318    level: OwnedLevelSpec,
319    presets: BTreeMap<String, LevelMask>,
320    sink: Option<LogSink>,
321    preprocessors: Vec<Preprocessor>,
322    postprocessors: Vec<Postprocessor>,
323}
324
325impl Debug for LogLazyOptions {
326    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
327        formatter
328            .debug_struct("LogLazyOptions")
329            .field("level", &self.level)
330            .field("presets", &self.presets)
331            .field("has_sink", &self.sink.is_some())
332            .field("preprocessors", &self.preprocessors.len())
333            .field("postprocessors", &self.postprocessors.len())
334            .finish()
335    }
336}
337
338impl Default for LogLazyOptions {
339    fn default() -> Self {
340        Self {
341            level: OwnedLevelSpec::Mask(levels::INFO),
342            presets: BTreeMap::new(),
343            sink: None,
344            preprocessors: Vec::new(),
345            postprocessors: Vec::new(),
346        }
347    }
348}
349
350impl LogLazyOptions {
351    pub fn new() -> Self {
352        Self::default()
353    }
354
355    pub fn level<L>(mut self, level: L) -> Self
356    where
357        L: Into<OwnedLevelSpec>,
358    {
359        self.level = level.into();
360        self
361    }
362
363    pub fn preset(mut self, name: impl Into<String>, mask: LevelMask) -> Self {
364        self.presets.insert(name.into(), mask);
365        self
366    }
367
368    pub fn sink<F>(mut self, sink: F) -> Self
369    where
370        F: Fn(Level, String) + Send + Sync + 'static,
371    {
372        self.sink = Some(Arc::new(sink));
373        self
374    }
375
376    pub fn preprocessor(mut self, preprocessor: Preprocessor) -> Self {
377        self.preprocessors.push(preprocessor);
378        self
379    }
380
381    pub fn preprocessor_fn<F>(mut self, preprocessor: F) -> Self
382    where
383        F: Fn(PreprocessorOptions) -> Vec<LogArg> + Send + Sync + 'static,
384    {
385        self.preprocessors.push(Arc::new(preprocessor));
386        self
387    }
388
389    pub fn postprocessor(mut self, postprocessor: Postprocessor) -> Self {
390        self.postprocessors.push(postprocessor);
391        self
392    }
393
394    pub fn postprocessor_fn<F>(mut self, postprocessor: F) -> Self
395    where
396        F: Fn(PostprocessorOptions) -> String + Send + Sync + 'static,
397    {
398        self.postprocessors.push(Arc::new(postprocessor));
399        self
400    }
401}
402
403/// Built-in preprocessor helpers.
404pub mod preprocessors {
405    use super::{LogArg, Preprocessor, PreprocessorOptions};
406    use std::sync::Arc;
407
408    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
409    pub enum ContextPosition {
410        Start,
411        End,
412    }
413
414    #[derive(Debug, Clone, Eq, PartialEq)]
415    pub struct AddContextOptions {
416        pub context: String,
417        pub position: ContextPosition,
418    }
419
420    impl AddContextOptions {
421        pub fn new(context: impl Into<String>) -> Self {
422            Self {
423                context: context.into(),
424                position: ContextPosition::End,
425            }
426        }
427
428        pub fn position(mut self, position: ContextPosition) -> Self {
429            self.position = position;
430            self
431        }
432    }
433
434    pub fn add_context(options: AddContextOptions) -> Preprocessor {
435        Arc::new(move |mut preprocessor_options: PreprocessorOptions| {
436            let context = LogArg::text(options.context.clone());
437            match options.position {
438                ContextPosition::Start => {
439                    preprocessor_options.args.insert(0, context);
440                    preprocessor_options.args
441                }
442                ContextPosition::End => {
443                    preprocessor_options.args.push(context);
444                    preprocessor_options.args
445                }
446            }
447        })
448    }
449
450    pub struct FilterPredicateOptions<'a> {
451        pub arg: &'a LogArg,
452        pub index: usize,
453        pub level: super::Level,
454    }
455
456    pub struct FilterOptions<F> {
457        pub predicate: F,
458    }
459
460    pub fn filter<F>(options: FilterOptions<F>) -> Preprocessor
461    where
462        F: for<'a> Fn(FilterPredicateOptions<'a>) -> bool + Send + Sync + 'static,
463    {
464        let predicate = options.predicate;
465        Arc::new(move |preprocessor_options: PreprocessorOptions| {
466            let level = preprocessor_options.level;
467            preprocessor_options
468                .args
469                .into_iter()
470                .enumerate()
471                .filter_map(|(index, arg)| {
472                    if predicate(FilterPredicateOptions {
473                        arg: &arg,
474                        index,
475                        level,
476                    }) {
477                        Some(arg)
478                    } else {
479                        None
480                    }
481                })
482                .collect()
483        })
484    }
485
486    pub struct MapTransformOptions {
487        pub arg: LogArg,
488        pub index: usize,
489        pub level: super::Level,
490    }
491
492    pub struct MapOptions<F> {
493        pub transform: F,
494    }
495
496    pub fn map<F>(options: MapOptions<F>) -> Preprocessor
497    where
498        F: Fn(MapTransformOptions) -> LogArg + Send + Sync + 'static,
499    {
500        let transform = options.transform;
501        Arc::new(move |preprocessor_options: PreprocessorOptions| {
502            let level = preprocessor_options.level;
503            preprocessor_options
504                .args
505                .into_iter()
506                .enumerate()
507                .map(|(index, arg)| transform(MapTransformOptions { arg, index, level }))
508                .collect()
509        })
510    }
511}
512
513/// Built-in postprocessor helpers.
514pub mod postprocessors {
515    use super::{format_timestamp, Postprocessor, PostprocessorOptions};
516    use std::sync::Arc;
517
518    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
519    pub enum TimestampFormat {
520        Iso,
521        Locale,
522        Time,
523        Millis,
524    }
525
526    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
527    pub struct TimestampOptions {
528        pub format: TimestampFormat,
529    }
530
531    impl Default for TimestampOptions {
532        fn default() -> Self {
533            Self {
534                format: TimestampFormat::Iso,
535            }
536        }
537    }
538
539    impl TimestampOptions {
540        pub fn new() -> Self {
541            Self::default()
542        }
543
544        pub fn format(mut self, format: TimestampFormat) -> Self {
545            self.format = format;
546            self
547        }
548    }
549
550    pub fn timestamp(options: TimestampOptions) -> Postprocessor {
551        Arc::new(move |postprocessor_options: PostprocessorOptions| {
552            format!(
553                "[{}] {}",
554                format_timestamp(options.format),
555                postprocessor_options.message
556            )
557        })
558    }
559
560    #[derive(Debug, Clone, Copy, Eq, PartialEq)]
561    pub struct LevelOptions {
562        pub uppercase: bool,
563    }
564
565    impl Default for LevelOptions {
566        fn default() -> Self {
567            Self { uppercase: true }
568        }
569    }
570
571    impl LevelOptions {
572        pub fn new() -> Self {
573            Self::default()
574        }
575
576        pub fn uppercase(mut self, uppercase: bool) -> Self {
577            self.uppercase = uppercase;
578            self
579        }
580    }
581
582    pub fn level(options: LevelOptions) -> Postprocessor {
583        Arc::new(move |postprocessor_options: PostprocessorOptions| {
584            let level_name = if options.uppercase {
585                postprocessor_options.level.name().to_uppercase()
586            } else {
587                postprocessor_options.level.name().to_string()
588            };
589            format!("[{}] {}", level_name, postprocessor_options.message)
590        })
591    }
592
593    #[derive(Debug, Clone, Eq, PartialEq)]
594    pub struct PidOptions {
595        pub label: String,
596    }
597
598    impl Default for PidOptions {
599        fn default() -> Self {
600            Self {
601                label: "PID".to_string(),
602            }
603        }
604    }
605
606    impl PidOptions {
607        pub fn new() -> Self {
608            Self::default()
609        }
610
611        pub fn label(mut self, label: impl Into<String>) -> Self {
612            self.label = label.into();
613            self
614        }
615    }
616
617    pub fn pid(options: PidOptions) -> Postprocessor {
618        Arc::new(move |postprocessor_options: PostprocessorOptions| {
619            format!(
620                "[{}:{}] {}",
621                options.label,
622                std::process::id(),
623                postprocessor_options.message
624            )
625        })
626    }
627
628    #[derive(Debug, Clone, Eq, PartialEq)]
629    pub struct TextOptions {
630        pub text: String,
631    }
632
633    impl TextOptions {
634        pub fn new(text: impl Into<String>) -> Self {
635            Self { text: text.into() }
636        }
637    }
638
639    pub fn prefix(options: TextOptions) -> Postprocessor {
640        Arc::new(move |postprocessor_options: PostprocessorOptions| {
641            format!("{} {}", options.text, postprocessor_options.message)
642        })
643    }
644
645    pub fn suffix(options: TextOptions) -> Postprocessor {
646        Arc::new(move |postprocessor_options: PostprocessorOptions| {
647            format!("{} {}", postprocessor_options.message, options.text)
648        })
649    }
650}
651
652/// Logger instance with a mutable bitmask and lazy message evaluation.
653#[derive(Clone)]
654pub struct LogLazy {
655    current_level: LevelMask,
656    presets: BTreeMap<String, LevelMask>,
657    sink: LogSink,
658    preprocessors: Vec<Preprocessor>,
659    postprocessors: Vec<Postprocessor>,
660}
661
662impl Debug for LogLazy {
663    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
664        formatter
665            .debug_struct("LogLazy")
666            .field("current_level", &self.current_level)
667            .field("presets", &self.presets)
668            .field("preprocessors", &self.preprocessors.len())
669            .field("postprocessors", &self.postprocessors.len())
670            .finish_non_exhaustive()
671    }
672}
673
674impl Default for LogLazy {
675    fn default() -> Self {
676        Self {
677            current_level: levels::INFO,
678            presets: default_presets(),
679            sink: Arc::new(default_sink),
680            preprocessors: Vec::new(),
681            postprocessors: Vec::new(),
682        }
683    }
684}
685
686impl LogLazy {
687    /// Creates a logger with the default `info` level.
688    pub fn new() -> Self {
689        Self::default()
690    }
691
692    /// Creates a logger from the extensible options API.
693    pub fn with_options(options: LogLazyOptions) -> Self {
694        let mut presets = default_presets();
695        for (name, mask) in options.presets {
696            presets.insert(name, mask);
697        }
698
699        let current_level = level_from_owned_spec(options.level, &presets, levels::INFO);
700
701        Self {
702            current_level,
703            presets,
704            sink: options.sink.unwrap_or_else(|| Arc::new(default_sink)),
705            preprocessors: options.preprocessors,
706            postprocessors: options.postprocessors,
707        }
708    }
709
710    /// Creates a logger with a custom level.
711    pub fn with_level<'a, L>(level: L) -> Self
712    where
713        L: Into<LevelSpec<'a>>,
714    {
715        let mut logger = Self::default();
716        logger.set_level(level);
717        logger
718    }
719
720    /// Creates a logger with a custom level and custom presets.
721    pub fn with_level_and_presets<'a, L, I, S>(level: L, presets: I) -> Self
722    where
723        L: Into<LevelSpec<'a>>,
724        I: IntoIterator<Item = (S, LevelMask)>,
725        S: Into<String>,
726    {
727        let mut logger = Self::default();
728        for (name, mask) in presets {
729            logger.add_preset(name, mask);
730        }
731        logger.set_level(level);
732        logger
733    }
734
735    /// Creates a logger with a custom sink.
736    pub fn with_sink<'a, L, F>(level: L, sink: F) -> Self
737    where
738        L: Into<LevelSpec<'a>>,
739        F: Fn(Level, String) + Send + Sync + 'static,
740    {
741        let mut logger = Self::with_level(level);
742        logger.sink = Arc::new(sink);
743        logger
744    }
745
746    /// Adds or replaces a named preset.
747    pub fn add_preset(&mut self, name: impl Into<String>, mask: LevelMask) {
748        self.presets.insert(name.into(), mask);
749    }
750
751    /// Returns the active level bitmask.
752    pub const fn level(&self) -> LevelMask {
753        self.current_level
754    }
755
756    /// Sets the active level bitmask.
757    pub fn set_level<'a, L>(&mut self, level: L)
758    where
759        L: Into<LevelSpec<'a>>,
760    {
761        self.current_level = self.level_or_default(level, levels::INFO);
762    }
763
764    /// Resolves a string or numeric level to a mask, falling back to `default`.
765    pub fn level_or_default<'a, L>(&self, level: L, default: LevelMask) -> LevelMask
766    where
767        L: Into<LevelSpec<'a>>,
768    {
769        match level.into() {
770            LevelSpec::Mask(mask) => mask,
771            LevelSpec::Name(name) => self
772                .presets
773                .get(name)
774                .copied()
775                .or_else(|| builtin_level_mask(name))
776                .or_else(|| name.parse::<LevelMask>().ok())
777                .unwrap_or(default),
778        }
779    }
780
781    /// Checks whether a level is currently enabled.
782    pub fn should_log<'a, L>(&self, level: L) -> bool
783    where
784        L: Into<LevelSpec<'a>>,
785    {
786        if self.current_level == levels::NONE {
787            return false;
788        }
789
790        let level_mask = self.level_or_default(level, levels::NONE);
791        level_mask != levels::NONE && (self.current_level & level_mask) != 0
792    }
793
794    /// Enables one level bit or mask.
795    pub fn enable_level<'a, L>(&mut self, level: L)
796    where
797        L: Into<LevelSpec<'a>>,
798    {
799        let level_mask = self.level_or_default(level, levels::NONE);
800        self.current_level |= level_mask;
801    }
802
803    /// Disables one level bit or mask.
804    pub fn disable_level<'a, L>(&mut self, level: L)
805    where
806        L: Into<LevelSpec<'a>>,
807    {
808        let level_mask = self.level_or_default(level, levels::NONE);
809        self.current_level &= !level_mask;
810    }
811
812    /// Returns enabled standard level names in stable order.
813    pub fn get_enabled_levels(&self) -> Vec<&'static str> {
814        STANDARD_LEVELS
815            .iter()
816            .filter(|level| self.should_log(level.mask()))
817            .map(|level| level.name())
818            .collect()
819    }
820
821    /// Logs at the default `info` level.
822    pub fn log<F, M>(&self, message: F)
823    where
824        F: FnOnce() -> M,
825        M: Display,
826    {
827        self.emit(Level::INFO, message);
828    }
829
830    /// Logs at an explicit level.
831    pub fn emit<'a, L, F, M>(&self, level: L, message: F)
832    where
833        L: Into<LevelSpec<'a>>,
834        F: FnOnce() -> M,
835        M: Display,
836    {
837        let level_mask = self.level_or_default(level, levels::NONE);
838        if !self.should_log(level_mask) {
839            return;
840        }
841
842        if let Some(level) = Level::from_mask(level_mask) {
843            if self.preprocessors.is_empty() && self.postprocessors.is_empty() {
844                (self.sink)(level, message().to_string());
845            } else {
846                self.emit_prepared_args(level, vec![LogArg::text(message().to_string())]);
847            }
848        }
849    }
850
851    /// Logs explicit lazy arguments at an explicit level.
852    pub fn emit_args<'a, L, I>(&self, level: L, args: I)
853    where
854        L: Into<LevelSpec<'a>>,
855        I: IntoIterator<Item = LogArg>,
856    {
857        let level_mask = self.level_or_default(level, levels::NONE);
858        if !self.should_log(level_mask) {
859            return;
860        }
861
862        if let Some(level) = Level::from_mask(level_mask) {
863            self.emit_prepared_args(level, args.into_iter().collect());
864        }
865    }
866
867    fn emit_prepared_args(&self, level: Level, args: Vec<LogArg>) {
868        let mut processable_args = args;
869        for preprocessor in &self.preprocessors {
870            processable_args = preprocessor(PreprocessorOptions {
871                args: processable_args,
872                level,
873            });
874        }
875
876        let mut message = processable_args
877            .into_iter()
878            .map(LogArg::evaluate)
879            .collect::<Vec<_>>()
880            .join(" ");
881
882        for postprocessor in &self.postprocessors {
883            message = postprocessor(PostprocessorOptions { message, level });
884        }
885
886        (self.sink)(level, message);
887    }
888
889    pub fn fatal<F, M>(&self, message: F)
890    where
891        F: FnOnce() -> M,
892        M: Display,
893    {
894        self.emit(Level::FATAL, message);
895    }
896
897    pub fn error<F, M>(&self, message: F)
898    where
899        F: FnOnce() -> M,
900        M: Display,
901    {
902        self.emit(Level::ERROR, message);
903    }
904
905    pub fn warn<F, M>(&self, message: F)
906    where
907        F: FnOnce() -> M,
908        M: Display,
909    {
910        self.emit(Level::WARN, message);
911    }
912
913    pub fn info<F, M>(&self, message: F)
914    where
915        F: FnOnce() -> M,
916        M: Display,
917    {
918        self.emit(Level::INFO, message);
919    }
920
921    pub fn debug<F, M>(&self, message: F)
922    where
923        F: FnOnce() -> M,
924        M: Display,
925    {
926        self.emit(Level::DEBUG, message);
927    }
928
929    pub fn verbose<F, M>(&self, message: F)
930    where
931        F: FnOnce() -> M,
932        M: Display,
933    {
934        self.emit(Level::VERBOSE, message);
935    }
936
937    pub fn trace<F, M>(&self, message: F)
938    where
939        F: FnOnce() -> M,
940        M: Display,
941    {
942        self.emit(Level::TRACE, message);
943    }
944
945    pub fn silly<F, M>(&self, message: F)
946    where
947        F: FnOnce() -> M,
948        M: Display,
949    {
950        self.emit(Level::SILLY, message);
951    }
952}
953
954/// Lazily formats and emits a message at an explicit level.
955#[macro_export]
956macro_rules! log_lazy {
957    ($logger:expr, $level:expr, $($arg:tt)+) => {{
958        $logger.emit($level, || format!($($arg)+));
959    }};
960}
961
962/// Lazily formats and emits an `info` message.
963#[macro_export]
964macro_rules! info_lazy {
965    ($logger:expr, $($arg:tt)+) => {{
966        $logger.info(|| format!($($arg)+));
967    }};
968}
969
970/// Lazily formats and emits a `debug` message.
971#[macro_export]
972macro_rules! debug_lazy {
973    ($logger:expr, $($arg:tt)+) => {{
974        $logger.debug(|| format!($($arg)+));
975    }};
976}
977
978/// Lazily formats and emits an `error` message.
979#[macro_export]
980macro_rules! error_lazy {
981    ($logger:expr, $($arg:tt)+) => {{
982        $logger.error(|| format!($($arg)+));
983    }};
984}
985
986fn default_presets() -> BTreeMap<String, LevelMask> {
987    BTreeMap::from([
988        ("production".to_string(), levels::PRODUCTION),
989        ("development".to_string(), levels::DEVELOPMENT),
990    ])
991}
992
993fn level_from_owned_spec(
994    level: OwnedLevelSpec,
995    presets: &BTreeMap<String, LevelMask>,
996    default: LevelMask,
997) -> LevelMask {
998    match level {
999        OwnedLevelSpec::Mask(mask) => mask,
1000        OwnedLevelSpec::Name(name) => presets
1001            .get(name.as_str())
1002            .copied()
1003            .or_else(|| builtin_level_mask(name.as_str()))
1004            .or_else(|| name.parse::<LevelMask>().ok())
1005            .unwrap_or(default),
1006    }
1007}
1008
1009fn format_timestamp(format: postprocessors::TimestampFormat) -> String {
1010    match format {
1011        postprocessors::TimestampFormat::Iso => {
1012            Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true)
1013        }
1014        postprocessors::TimestampFormat::Locale => Local::now().format("%c").to_string(),
1015        postprocessors::TimestampFormat::Time => Local::now().format("%T").to_string(),
1016        postprocessors::TimestampFormat::Millis => Utc::now().timestamp_millis().to_string(),
1017    }
1018}
1019
1020fn builtin_level_mask(name: &str) -> Option<LevelMask> {
1021    match name {
1022        "none" => Some(levels::NONE),
1023        "fatal" => Some(levels::FATAL),
1024        "error" => Some(levels::ERROR),
1025        "warn" => Some(levels::WARN),
1026        "info" => Some(levels::INFO),
1027        "debug" => Some(levels::DEBUG),
1028        "verbose" => Some(levels::VERBOSE),
1029        "trace" => Some(levels::TRACE),
1030        "silly" => Some(levels::SILLY),
1031        "all" => Some(levels::ALL),
1032        "production" => Some(levels::PRODUCTION),
1033        "development" => Some(levels::DEVELOPMENT),
1034        _ => None,
1035    }
1036}
1037
1038fn default_sink(level: Level, message: String) {
1039    match level {
1040        Level::FATAL | Level::ERROR | Level::WARN => eprintln!("{message}"),
1041        _ => println!("{message}"),
1042    }
1043}