Skip to main content

lean_log/
lib.rs

1#![forbid(unsafe_code)]
2
3use core::sync::atomic::{AtomicU8, Ordering};
4use std::env;
5use std::fs::{File, OpenOptions};
6use std::io::{self, BufWriter, Write};
7use std::path::Path;
8use std::sync::{Mutex, OnceLock};
9
10#[repr(u8)]
11#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
12pub enum Level {
13    Off = 0,
14    Error = 1,
15    Warn = 2,
16    Notice = 3,
17    Info = 4,
18    Debug = 5,
19    Trace = 6,
20}
21
22impl Level {
23    #[inline]
24    pub fn as_str(self) -> &'static str {
25        match self {
26            Level::Off => "OFF",
27            Level::Error => "ERROR",
28            Level::Warn => "WARN",
29            Level::Notice => "NOTICE",
30            Level::Info => "INFO",
31            Level::Debug => "DEBUG",
32            Level::Trace => "TRACE",
33        }
34    }
35
36    #[inline]
37    pub fn parse(s: &str) -> Option<Self> {
38        match s.trim().to_ascii_lowercase().as_str() {
39            "off" => Some(Level::Off),
40            "error" => Some(Level::Error),
41            "warn" | "warning" => Some(Level::Warn),
42            "notice" => Some(Level::Notice),
43            "info" => Some(Level::Info),
44            "debug" => Some(Level::Debug),
45            "trace" => Some(Level::Trace),
46            _ => None,
47        }
48    }
49}
50
51static RUNTIME_LEVEL: AtomicU8 = AtomicU8::new(Level::Info as u8);
52
53#[inline]
54pub fn set_level(level: Level) {
55    RUNTIME_LEVEL.store(level as u8, Ordering::Relaxed);
56}
57
58#[inline]
59#[must_use]
60pub fn level() -> Level {
61    match RUNTIME_LEVEL.load(Ordering::Relaxed) {
62        0 => Level::Off,
63        1 => Level::Error,
64        2 => Level::Warn,
65        3 => Level::Notice,
66        4 => Level::Info,
67        5 => Level::Debug,
68        _ => Level::Trace,
69    }
70}
71
72/// Initialize log level from an environment variable.
73///
74/// Checks the specified environment variable and parses it as a log level.
75/// If the variable is not set or cannot be parsed, uses the provided default.
76///
77/// # Examples
78///
79/// ```no_run
80/// use lean_log::Level;
81/// // Check RUST_LOG, default to Info
82/// lean_log::init_from_env("RUST_LOG", Level::Info);
83/// ```
84pub fn init_from_env(var_name: &str, default: Level) {
85    let level = env::var(var_name)
86        .ok()
87        .and_then(|s| Level::parse(&s))
88        .unwrap_or(default);
89    set_level(level);
90}
91
92/// Initialize log level from RUST_LOG environment variable.
93///
94/// This is a convenience wrapper around `init_from_env("RUST_LOG", default)`.
95/// Follows the common Rust convention of using RUST_LOG for log level configuration.
96///
97/// # Examples
98///
99/// ```no_run
100/// use lean_log::Level;
101/// // Check RUST_LOG env var, default to Info if not set
102/// lean_log::init();
103/// // Or with custom default:
104/// lean_log::init_with_default(Level::Warn);
105/// ```
106pub fn init() {
107    init_from_env("RUST_LOG", Level::Info);
108}
109
110/// Initialize log level from RUST_LOG with a custom default.
111pub fn init_with_default(default: Level) {
112    init_from_env("RUST_LOG", default);
113}
114
115pub const COMPILED_MAX_LEVEL: Level = compiled_max_level();
116
117const fn compiled_max_level() -> Level {
118    #[cfg(feature = "max_trace")]
119    {
120        return Level::Trace;
121    }
122    #[cfg(all(not(feature = "max_trace"), feature = "max_debug"))]
123    {
124        return Level::Debug;
125    }
126    #[cfg(all(
127        not(feature = "max_trace"),
128        not(feature = "max_debug"),
129        feature = "max_info"
130    ))]
131    {
132        return Level::Info;
133    }
134
135    #[cfg(all(
136        not(feature = "max_trace"),
137        not(feature = "max_debug"),
138        not(feature = "max_info"),
139        feature = "max_notice"
140    ))]
141    {
142        return Level::Notice;
143    }
144
145    #[cfg(all(
146        not(feature = "max_trace"),
147        not(feature = "max_debug"),
148        not(feature = "max_info"),
149        not(feature = "max_notice"),
150        feature = "max_warn"
151    ))]
152    {
153        return Level::Warn;
154    }
155    #[cfg(all(
156        not(feature = "max_trace"),
157        not(feature = "max_debug"),
158        not(feature = "max_info"),
159        not(feature = "max_notice"),
160        not(feature = "max_warn"),
161        feature = "max_error"
162    ))]
163    {
164        return Level::Error;
165    }
166
167    #[allow(unreachable_code)]
168    Level::Off
169}
170
171#[inline(always)]
172pub fn enabled(_level: Level) -> bool {
173    #[cfg(feature = "disabled")]
174    {
175        return false;
176    }
177
178    #[cfg(not(feature = "disabled"))]
179    {
180        if (_level as u8) > (COMPILED_MAX_LEVEL as u8) {
181            return false;
182        }
183        RUNTIME_LEVEL.load(Ordering::Relaxed) >= _level as u8
184    }
185}
186
187pub fn init_from_args<I, S>(args: I)
188where
189    I: IntoIterator<Item = S>,
190    S: AsRef<str>,
191{
192    let mut it = args.into_iter();
193    while let Some(arg) = it.next() {
194        let a = arg.as_ref();
195
196        // --log=info or --log-level=info
197        if let Some(v) = a
198            .strip_prefix("--log=")
199            .or_else(|| a.strip_prefix("--log-level="))
200        {
201            if let Some(lvl) = Level::parse(v) {
202                set_level(lvl);
203            }
204            continue;
205        }
206
207        // --log info  OR  --log-level info
208        if a == "--log" || a == "--log-level" {
209            if let Some(next) = it.next() {
210                if let Some(lvl) = Level::parse(next.as_ref()) {
211                    set_level(lvl);
212                }
213            }
214            continue;
215        }
216    }
217}
218
219enum Sink {
220    Stderr,
221    File(FileSink),
222}
223
224struct FileSink {
225    writer: Mutex<BufWriter<File>>,
226}
227
228impl FileSink {
229    fn new(file: File) -> Self {
230        Self {
231            writer: Mutex::new(BufWriter::new(file)),
232        }
233    }
234
235    fn write(
236        &self,
237        timestamp: &str,
238        level: &str,
239        module: &str,
240        line: u32,
241        msg: core::fmt::Arguments<'_>,
242    ) {
243        let mut writer = match self.writer.lock() {
244            Ok(guard) => guard,
245            Err(poisoned) => poisoned.into_inner(),
246        };
247
248        let _ = writer.write_fmt(format_args!("{} {} {}:{} ", timestamp, level, module, line));
249        let _ = writer.write_fmt(msg);
250        let _ = writer.write_all(b"\n");
251    }
252
253    fn write_with_fields(
254        &self,
255        timestamp: &str,
256        level: &str,
257        module: &str,
258        line: u32,
259        msg: core::fmt::Arguments<'_>,
260        fields: core::fmt::Arguments<'_>,
261    ) {
262        let mut writer = match self.writer.lock() {
263            Ok(guard) => guard,
264            Err(poisoned) => poisoned.into_inner(),
265        };
266
267        let _ = writer.write_fmt(format_args!("{} {} {}:{} ", timestamp, level, module, line));
268        let _ = writer.write_fmt(msg);
269        let _ = writer.write_all(b" ");
270        let _ = writer.write_fmt(fields);
271        let _ = writer.write_all(b"\n");
272    }
273}
274
275static ACTIVE_SINK: OnceLock<Sink> = OnceLock::new();
276
277fn active_sink() -> &'static Sink {
278    ACTIVE_SINK.get_or_init(|| Sink::Stderr)
279}
280
281pub fn set_file_logging(path: impl AsRef<Path>) -> io::Result<()> {
282    configure_file_sink(path.as_ref(), false)
283}
284
285pub fn append_file_logging(path: impl AsRef<Path>) -> io::Result<()> {
286    configure_file_sink(path.as_ref(), true)
287}
288
289fn configure_file_sink(path: &Path, append: bool) -> io::Result<()> {
290    let file = OpenOptions::new()
291        .create(true)
292        .write(true)
293        .append(append)
294        .truncate(!append)
295        .open(path)?;
296
297    let sink = Sink::File(FileSink::new(file));
298    ACTIVE_SINK
299        .set(sink)
300        .map_err(|_| io::Error::new(io::ErrorKind::AlreadyExists, "log sink already initialized"))
301}
302
303struct LevelStyle {
304    ansi: &'static str,
305    display: &'static str,
306    plain: &'static str,
307}
308
309const ERROR_STYLE: LevelStyle = LevelStyle {
310    ansi: "\x1b[91m",
311    display: "ERROR",
312    plain: "ERROR",
313};
314
315const NOTICE_STYLE: LevelStyle = LevelStyle {
316    ansi: "\x1b[95m",
317    display: "NOTICE",
318    plain: "NOTICE",
319};
320
321const WARN_STYLE: LevelStyle = LevelStyle {
322    ansi: "\x1b[93m",
323    display: " WARN",
324    plain: "WARN",
325};
326
327const INFO_STYLE: LevelStyle = LevelStyle {
328    ansi: "\x1b[92m",
329    display: " INFO",
330    plain: "INFO",
331};
332
333const DEBUG_STYLE: LevelStyle = LevelStyle {
334    ansi: "\x1b[96m",
335    display: "DEBUG",
336    plain: "DEBUG",
337};
338
339const TRACE_STYLE: LevelStyle = LevelStyle {
340    ansi: "\x1b[94m",
341    display: "TRACE",
342    plain: "TRACE",
343};
344
345#[inline]
346fn emit_with_style(style: &LevelStyle, msg: core::fmt::Arguments<'_>, _line: u32, _module: &str) {
347    emit_with_style_and_fields(style, msg, None, _line, _module)
348}
349
350#[inline]
351fn emit_with_style_and_fields(
352    style: &LevelStyle,
353    msg: core::fmt::Arguments<'_>,
354    fields: Option<core::fmt::Arguments<'_>>,
355    _line: u32,
356    _module: &str,
357) {
358    #[cfg(not(feature = "disabled"))]
359    {
360        let timestamp = format_timestamp();
361        match active_sink() {
362            Sink::Stderr => {
363                if let Some(fields) = fields {
364                    eprintln!(
365                        "\x1b[90m{}\x1b[0m {}{}\x1b[0m \x1b[1;37m{}:{}\x1b[0m {} \x1b[36m{}\x1b[0m",
366                        timestamp, style.ansi, style.display, _module, _line, msg, fields
367                    );
368                } else {
369                    eprintln!(
370                        "\x1b[90m{}\x1b[0m {}{}\x1b[0m \x1b[1;37m{}:{}\x1b[0m {}",
371                        timestamp, style.ansi, style.display, _module, _line, msg
372                    );
373                }
374            }
375            Sink::File(file_sink) => {
376                if let Some(fields) = fields {
377                    file_sink.write_with_fields(
378                        &timestamp,
379                        style.plain,
380                        _module,
381                        _line,
382                        msg,
383                        fields,
384                    );
385                } else {
386                    file_sink.write(&timestamp, style.plain, _module, _line, msg);
387                }
388            }
389        }
390    }
391}
392
393#[inline]
394fn format_timestamp() -> String {
395    use std::time::{SystemTime, UNIX_EPOCH};
396
397    let duration = SystemTime::now()
398        .duration_since(UNIX_EPOCH)
399        .expect("Time went backwards");
400
401    let secs = duration.as_secs();
402    let micros = duration.subsec_micros();
403
404    let days_since_epoch = secs / 86400;
405    let secs_today = secs % 86400;
406
407    let hours = secs_today / 3600;
408    let minutes = (secs_today % 3600) / 60;
409    let seconds = secs_today % 60;
410
411    let mut year = 1970;
412    let mut days = days_since_epoch;
413
414    loop {
415        let days_in_year = if year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) {
416            366
417        } else {
418            365
419        };
420        if days < days_in_year {
421            break;
422        }
423        days -= days_in_year;
424        year += 1;
425    }
426
427    let is_leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
428    let days_in_months = if is_leap {
429        [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
430    } else {
431        [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
432    };
433
434    let mut month = 1;
435    for &days_in_month in &days_in_months {
436        if days < days_in_month {
437            break;
438        }
439        days -= days_in_month;
440        month += 1;
441    }
442    let day = days + 1;
443
444    format!(
445        "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:06}Z",
446        year, month, day, hours, minutes, seconds, micros
447    )
448}
449
450#[cold]
451#[inline(never)]
452#[doc(hidden)]
453pub fn emit_error(_msg: core::fmt::Arguments<'_>, _line: u32, _module: &str) {
454    emit_with_style(&ERROR_STYLE, _msg, _line, _module);
455}
456
457#[cold]
458#[inline(never)]
459#[doc(hidden)]
460pub fn emit_error_with_fields(
461    _msg: core::fmt::Arguments<'_>,
462    _fields: core::fmt::Arguments<'_>,
463    _line: u32,
464    _module: &str,
465) {
466    emit_with_style_and_fields(&ERROR_STYLE, _msg, Some(_fields), _line, _module);
467}
468
469#[cold]
470#[inline(never)]
471#[doc(hidden)]
472pub fn emit_warn(_msg: core::fmt::Arguments<'_>, _line: u32, _module: &str) {
473    emit_with_style(&WARN_STYLE, _msg, _line, _module);
474}
475
476#[cold]
477#[inline(never)]
478#[doc(hidden)]
479pub fn emit_warn_with_fields(
480    _msg: core::fmt::Arguments<'_>,
481    _fields: core::fmt::Arguments<'_>,
482    _line: u32,
483    _module: &str,
484) {
485    emit_with_style_and_fields(&WARN_STYLE, _msg, Some(_fields), _line, _module);
486}
487
488#[cold]
489#[inline(never)]
490#[doc(hidden)]
491pub fn emit_notice(_msg: core::fmt::Arguments<'_>, _line: u32, _module: &str) {
492    emit_with_style(&NOTICE_STYLE, _msg, _line, _module);
493}
494
495#[cold]
496#[inline(never)]
497#[doc(hidden)]
498pub fn emit_notice_with_fields(
499    _msg: core::fmt::Arguments<'_>,
500    _fields: core::fmt::Arguments<'_>,
501    _line: u32,
502    _module: &str,
503) {
504    emit_with_style_and_fields(&NOTICE_STYLE, _msg, Some(_fields), _line, _module);
505}
506
507#[cold]
508#[inline(never)]
509#[doc(hidden)]
510pub fn emit_info(_msg: core::fmt::Arguments<'_>, _line: u32, _module: &str) {
511    emit_with_style(&INFO_STYLE, _msg, _line, _module);
512}
513
514#[cold]
515#[inline(never)]
516#[doc(hidden)]
517pub fn emit_info_with_fields(
518    _msg: core::fmt::Arguments<'_>,
519    _fields: core::fmt::Arguments<'_>,
520    _line: u32,
521    _module: &str,
522) {
523    emit_with_style_and_fields(&INFO_STYLE, _msg, Some(_fields), _line, _module);
524}
525
526#[cold]
527#[inline(never)]
528#[doc(hidden)]
529pub fn emit_debug(_msg: core::fmt::Arguments<'_>, _line: u32, _module: &str) {
530    emit_with_style(&DEBUG_STYLE, _msg, _line, _module);
531}
532
533#[cold]
534#[inline(never)]
535#[doc(hidden)]
536pub fn emit_debug_with_fields(
537    _msg: core::fmt::Arguments<'_>,
538    _fields: core::fmt::Arguments<'_>,
539    _line: u32,
540    _module: &str,
541) {
542    emit_with_style_and_fields(&DEBUG_STYLE, _msg, Some(_fields), _line, _module);
543}
544
545#[cold]
546#[inline(never)]
547#[doc(hidden)]
548pub fn emit_trace(_msg: core::fmt::Arguments<'_>, _line: u32, _module: &str) {
549    emit_with_style(&TRACE_STYLE, _msg, _line, _module);
550}
551
552#[cold]
553#[inline(never)]
554#[doc(hidden)]
555pub fn emit_trace_with_fields(
556    _msg: core::fmt::Arguments<'_>,
557    _fields: core::fmt::Arguments<'_>,
558    _line: u32,
559    _module: &str,
560) {
561    emit_with_style_and_fields(&TRACE_STYLE, _msg, Some(_fields), _line, _module);
562}
563
564// -------- Macros (compile-time elimination + runtime check) --------
565
566#[macro_export]
567#[doc(hidden)]
568macro_rules! __format_fields {
569    () => { "" };
570
571    (? $name:ident) => {
572        format_args!("{}={:?}", stringify!($name), $name)
573    };
574
575    (% $name:ident) => {
576        format_args!("{}={}", stringify!($name), $name)
577    };
578
579    ($key:ident = ? $value:expr) => {
580        format_args!("{}={:?}", stringify!($key), $value)
581    };
582
583    ($key:ident = $value:expr) => {
584        format_args!("{}={}", stringify!($key), $value)
585    };
586
587    (? $name:ident, $($rest:tt)*) => {
588        format_args!("{}={:?} {}", stringify!($name), $name, $crate::__format_fields!($($rest)*))
589    };
590
591    (% $name:ident, $($rest:tt)*) => {
592        format_args!("{}={} {}", stringify!($name), $name, $crate::__format_fields!($($rest)*))
593    };
594
595    ($key:ident = ? $value:expr, $($rest:tt)*) => {
596        format_args!("{}={:?} {}", stringify!($key), $value, $crate::__format_fields!($($rest)*))
597    };
598
599    ($key:ident = $value:expr, $($rest:tt)*) => {
600        format_args!("{}={} {}", stringify!($key), $value, $crate::__format_fields!($($rest)*))
601    };
602}
603
604#[macro_export]
605macro_rules! error {
606    ($msg:literal; $($field:tt)+) => {{
607        if $crate::enabled($crate::Level::Error) {
608            $crate::emit_error_with_fields(
609                format_args!($msg),
610                $crate::__format_fields!($($field)+),
611                line!(),
612                module_path!()
613            );
614        }
615    }};
616
617    ($msg:literal, $($arg:expr),+; $($field:tt)+) => {{
618        if $crate::enabled($crate::Level::Error) {
619            $crate::emit_error_with_fields(
620                format_args!($msg, $($arg),+),
621                $crate::__format_fields!($($field)+),
622                line!(),
623                module_path!()
624            );
625        }
626    }};
627
628    ($msg:literal, ? $($field:tt)+) => {{
629        if $crate::enabled($crate::Level::Error) {
630            $crate::emit_error_with_fields(
631                format_args!($msg),
632                $crate::__format_fields!(? $($field)+),
633                line!(),
634                module_path!()
635            );
636        }
637    }};
638
639    ($msg:literal, % $($field:tt)+) => {{
640        if $crate::enabled($crate::Level::Error) {
641            $crate::emit_error_with_fields(
642                format_args!($msg),
643                $crate::__format_fields!(% $($field)+),
644                line!(),
645                module_path!()
646            );
647        }
648    }};
649
650    ($($arg:tt)+) => {{
651        if $crate::enabled($crate::Level::Error) {
652            $crate::emit_error(format_args!($($arg)+), line!(), module_path!());
653        }
654    }};
655}
656
657#[macro_export]
658macro_rules! warn {
659    ($msg:literal; $($field:tt)+) => {{
660        if $crate::enabled($crate::Level::Warn) {
661            $crate::emit_warn_with_fields(
662                format_args!($msg),
663                $crate::__format_fields!($($field)+),
664                line!(),
665                module_path!()
666            );
667        }
668    }};
669
670    ($msg:literal, $($arg:expr),+; $($field:tt)+) => {{
671        if $crate::enabled($crate::Level::Warn) {
672            $crate::emit_warn_with_fields(
673                format_args!($msg, $($arg),+),
674                $crate::__format_fields!($($field)+),
675                line!(),
676                module_path!()
677            );
678        }
679    }};
680
681    ($msg:literal, ? $($field:tt)+) => {{
682        if $crate::enabled($crate::Level::Warn) {
683            $crate::emit_warn_with_fields(
684                format_args!($msg),
685                $crate::__format_fields!(? $($field)+),
686                line!(),
687                module_path!()
688            );
689        }
690    }};
691
692    ($msg:literal, % $($field:tt)+) => {{
693        if $crate::enabled($crate::Level::Warn) {
694            $crate::emit_warn_with_fields(
695                format_args!($msg),
696                $crate::__format_fields!(% $($field)+),
697                line!(),
698                module_path!()
699            );
700        }
701    }};
702
703    ($($arg:tt)+) => {{
704        if $crate::enabled($crate::Level::Warn) {
705            $crate::emit_warn(format_args!($($arg)+), line!(), module_path!());
706        }
707    }};
708}
709
710#[macro_export]
711macro_rules! notice {
712    ($msg:literal; $($field:tt)+) => {{
713        if $crate::enabled($crate::Level::Notice) {
714            $crate::emit_notice_with_fields(
715                format_args!($msg),
716                $crate::__format_fields!($($field)+),
717                line!(),
718                module_path!()
719            );
720        }
721    }};
722
723    ($msg:literal, $($arg:expr),+; $($field:tt)+) => {{
724        if $crate::enabled($crate::Level::Notice) {
725            $crate::emit_notice_with_fields(
726                format_args!($msg, $($arg),+),
727                $crate::__format_fields!($($field)+),
728                line!(),
729                module_path!()
730            );
731        }
732    }};
733
734    ($msg:literal, ? $($field:tt)+) => {{
735        if $crate::enabled($crate::Level::Notice) {
736            $crate::emit_notice_with_fields(
737                format_args!($msg),
738                $crate::__format_fields!(? $($field)+),
739                line!(),
740                module_path!()
741            );
742        }
743    }};
744
745    ($msg:literal, % $($field:tt)+) => {{
746        if $crate::enabled($crate::Level::Notice) {
747            $crate::emit_notice_with_fields(
748                format_args!($msg),
749                $crate::__format_fields!(% $($field)+),
750                line!(),
751                module_path!()
752            );
753        }
754    }};
755
756    ($($arg:tt)+) => {{
757        if $crate::enabled($crate::Level::Notice) {
758            $crate::emit_notice(format_args!($($arg)+), line!(), module_path!());
759        }
760    }};
761}
762
763#[macro_export]
764macro_rules! info {
765    ($msg:literal; $($field:tt)+) => {{
766        if $crate::enabled($crate::Level::Info) {
767            $crate::emit_info_with_fields(
768                format_args!($msg),
769                $crate::__format_fields!($($field)+),
770                line!(),
771                module_path!()
772            );
773        }
774    }};
775
776    ($msg:literal, $($arg:expr),+; $($field:tt)+) => {{
777        if $crate::enabled($crate::Level::Info) {
778            $crate::emit_info_with_fields(
779                format_args!($msg, $($arg),+),
780                $crate::__format_fields!($($field)+),
781                line!(),
782                module_path!()
783            );
784        }
785    }};
786
787    ($msg:literal, ? $($field:tt)+) => {{
788        if $crate::enabled($crate::Level::Info) {
789            $crate::emit_info_with_fields(
790                format_args!($msg),
791                $crate::__format_fields!(? $($field)+),
792                line!(),
793                module_path!()
794            );
795        }
796    }};
797
798    ($msg:literal, % $($field:tt)+) => {{
799        if $crate::enabled($crate::Level::Info) {
800            $crate::emit_info_with_fields(
801                format_args!($msg),
802                $crate::__format_fields!(% $($field)+),
803                line!(),
804                module_path!()
805            );
806        }
807    }};
808
809    ($($arg:tt)+) => {{
810        if $crate::enabled($crate::Level::Info) {
811            $crate::emit_info(format_args!($($arg)+), line!(), module_path!());
812        }
813    }};
814}
815
816#[macro_export]
817macro_rules! debug {
818    ($msg:literal; $($field:tt)+) => {{
819        if $crate::enabled($crate::Level::Debug) {
820            $crate::emit_debug_with_fields(
821                format_args!($msg),
822                $crate::__format_fields!($($field)+),
823                line!(),
824                module_path!()
825            );
826        }
827    }};
828
829    ($msg:literal, $($arg:expr),+; $($field:tt)+) => {{
830        if $crate::enabled($crate::Level::Debug) {
831            $crate::emit_debug_with_fields(
832                format_args!($msg, $($arg),+),
833                $crate::__format_fields!($($field)+),
834                line!(),
835                module_path!()
836            );
837        }
838    }};
839
840    ($msg:literal, ? $($field:tt)+) => {{
841        if $crate::enabled($crate::Level::Debug) {
842            $crate::emit_debug_with_fields(
843                format_args!($msg),
844                $crate::__format_fields!(? $($field)+),
845                line!(),
846                module_path!()
847            );
848        }
849    }};
850
851    ($msg:literal, % $($field:tt)+) => {{
852        if $crate::enabled($crate::Level::Debug) {
853            $crate::emit_debug_with_fields(
854                format_args!($msg),
855                $crate::__format_fields!(% $($field)+),
856                line!(),
857                module_path!()
858            );
859        }
860    }};
861
862    ($($arg:tt)+) => {{
863        if $crate::enabled($crate::Level::Debug) {
864            $crate::emit_debug(format_args!($($arg)+), line!(), module_path!());
865        }
866    }};
867}
868
869#[macro_export]
870macro_rules! trace {
871    ($msg:literal; $($field:tt)+) => {{
872        if $crate::enabled($crate::Level::Trace) {
873            $crate::emit_trace_with_fields(
874                format_args!($msg),
875                $crate::__format_fields!($($field)+),
876                line!(),
877                module_path!()
878            );
879        }
880    }};
881
882    ($msg:literal, $($arg:expr),+; $($field:tt)+) => {{
883        if $crate::enabled($crate::Level::Trace) {
884            $crate::emit_trace_with_fields(
885                format_args!($msg, $($arg),+),
886                $crate::__format_fields!($($field)+),
887                line!(),
888                module_path!()
889            );
890        }
891    }};
892
893    ($msg:literal, ? $($field:tt)+) => {{
894        if $crate::enabled($crate::Level::Trace) {
895            $crate::emit_trace_with_fields(
896                format_args!($msg),
897                $crate::__format_fields!(? $($field)+),
898                line!(),
899                module_path!()
900            );
901        }
902    }};
903
904    ($msg:literal, % $($field:tt)+) => {{
905        if $crate::enabled($crate::Level::Trace) {
906            $crate::emit_trace_with_fields(
907                format_args!($msg),
908                $crate::__format_fields!(% $($field)+),
909                line!(),
910                module_path!()
911            );
912        }
913    }};
914
915    ($($arg:tt)+) => {{
916        if $crate::enabled($crate::Level::Trace) {
917            $crate::emit_trace(format_args!($($arg)+), line!(), module_path!());
918        }
919    }};
920}