Skip to main content

log_io/
macros.rs

1//! Logging macros and the hidden helpers they expand to.
2
3/// Hidden support items for the logging macros. Not part of the public
4/// API; may change in any release.
5#[doc(hidden)]
6pub mod __private {
7    use core::fmt;
8
9    use crate::logger::Logger;
10    use crate::record::{Field, Metadata};
11
12    /// The global logger, if installed.
13    #[inline]
14    #[must_use]
15    pub fn global() -> Option<&'static Logger> {
16        crate::global::get()
17    }
18
19    /// Emit a record whose message is format arguments. The caller has
20    /// already checked `Logger::enabled`.
21    #[inline]
22    pub fn emit_args(
23        logger: &Logger,
24        metadata: Metadata<'_>,
25        args: fmt::Arguments<'_>,
26        fields: &[Field<'_>],
27    ) {
28        logger.emit_fmt(metadata, args, fields, &[]);
29    }
30
31    /// Emit a record whose message is a `&str` expression. The caller
32    /// has already checked `Logger::enabled`.
33    #[inline]
34    pub fn emit_str(logger: &Logger, metadata: Metadata<'_>, message: &str, fields: &[Field<'_>]) {
35        // Failures were already passed to the logger's error handler.
36        let _reported = logger.emit_unchecked(metadata, message, fields, &[]);
37    }
38}
39
40/// Emit a record at a runtime-chosen level.
41///
42/// ```text
43/// log_at!(LOGGER, LEVEL, [target: EXPR,] MESSAGE [, ARG | FIELD]* [,])
44/// log_at!(LEVEL, [target: EXPR,] "literal message" [, ARG | FIELD]* [,])   // global logger
45/// ```
46///
47/// The level macros ([`trace!`](crate::trace!), [`debug!`](crate::debug!),
48/// [`info!`](crate::info!), [`warn!`](crate::warn!),
49/// [`error!`](crate::error!)) take the same arguments without `LEVEL`.
50///
51/// # Logger
52///
53/// The first argument is anything that dereferences to a
54/// [`crate::Logger`] (`Logger`, `&Logger`, `Arc<Logger>`, ...). When the
55/// first argument is the message literal (or `target:`), the global
56/// logger from [`crate::init`] is used; if none is installed the call
57/// does nothing.
58///
59/// # Message
60///
61/// A string literal is a format string, exactly as in `format!`:
62/// positional arguments follow it and inline captures (`{id}`) work.
63/// Write `{{` and `}}` for literal braces. A literal without arguments
64/// is used as-is and costs nothing extra; otherwise the text is
65/// rendered into a reused thread-local buffer (no allocation in steady
66/// state). A non-literal message must be a `&str` expression and takes
67/// no format arguments.
68///
69/// # Fields
70///
71/// After the message, `key = value` pairs become structured fields. Keys
72/// are identifiers, dotted paths (`http.status = 200`), or string
73/// literals (`"content-type" = ct`). Values are converted with
74/// `Value::from`; prefix a value with `%` to record it through
75/// `Display`, or with `?` to record it through `Debug`, lazily.
76/// Because `name = value` always means a field, named format
77/// arguments (`format!("{x}", x = 1)`) are not supported; use inline
78/// captures or positional arguments instead.
79///
80/// # Evaluation
81///
82/// The filter is checked once, first. Field values and format
83/// arguments are evaluated only when the record is enabled. The target
84/// defaults to `module_path!()` and the source location (`file!()`,
85/// `line!()`) is always attached.
86///
87/// # Examples
88///
89#[cfg_attr(all(feature = "logfmt"), doc = "```")]
90#[cfg_attr(not(all(feature = "logfmt")), doc = "```ignore")]
91/// use log_io::{Level, Logger};
92///
93/// let logger = Logger::builder().stdout_logfmt().build();
94/// let user = "alice";
95/// let addr = std::net::Ipv4Addr::LOCALHOST;
96///
97/// log_io::log_at!(logger, Level::Info, "user {user} logged in from {}", addr,
98///     http.status = 200_u16, peer = %addr, attempts = ?[1, 2]);
99/// log_io::log_at!(logger, Level::Warn, target: "auth", "retrying", "retry-after" = 3_u8);
100/// ```
101#[macro_export]
102macro_rules! log_at {
103    // Global logger, explicit target.
104    ($level:expr, target: $target:expr, $msg:literal $($rest:tt)*) => {
105        $crate::__log_io_event!(@munch ((global) ($level) ($target) (lit $msg)) [] [] $($rest)*)
106    };
107    // Global logger.
108    ($level:expr, $msg:literal $($rest:tt)*) => {
109        $crate::__log_io_event!(
110            @munch ((global) ($level) (::core::module_path!()) (lit $msg)) [] [] $($rest)*
111        )
112    };
113    // Explicit logger and target.
114    ($logger:expr, $level:expr, target: $target:expr, $($rest:tt)+) => {
115        $crate::__log_io_event!(@msg (logger $logger) ($level) ($target) $($rest)+)
116    };
117    // Explicit logger.
118    ($logger:expr, $level:expr, $($rest:tt)+) => {
119        $crate::__log_io_event!(@msg (logger $logger) ($level) (::core::module_path!()) $($rest)+)
120    };
121}
122
123/// Emit a [`crate::Level::Trace`] record. See [`log_at!`](crate::log_at!) for the syntax.
124///
125/// # Examples
126///
127#[cfg_attr(all(feature = "json"), doc = "```")]
128#[cfg_attr(not(all(feature = "json")), doc = "```ignore")]
129/// # let logger = log_io::Logger::builder().stdout_json().build();
130/// log_io::trace!(logger, "entering", depth = 3_u8);
131/// ```
132#[macro_export]
133macro_rules! trace {
134    ($($args:tt)+) => { $crate::__log_io_level!($crate::Level::Trace; $($args)+) };
135}
136
137/// Emit a [`crate::Level::Debug`] record. See [`log_at!`](crate::log_at!) for the syntax.
138///
139/// # Examples
140///
141#[cfg_attr(all(feature = "json"), doc = "```")]
142#[cfg_attr(not(all(feature = "json")), doc = "```ignore")]
143/// # let logger = log_io::Logger::builder().stdout_json().build();
144/// log_io::debug!(logger, "cache miss for {}", "user:42", shard = 7_u8);
145/// ```
146#[macro_export]
147macro_rules! debug {
148    ($($args:tt)+) => { $crate::__log_io_level!($crate::Level::Debug; $($args)+) };
149}
150
151/// Emit a [`crate::Level::Info`] record. See [`log_at!`](crate::log_at!) for the syntax.
152///
153/// # Examples
154///
155#[cfg_attr(all(feature = "json"), doc = "```")]
156#[cfg_attr(not(all(feature = "json")), doc = "```ignore")]
157/// let logger = log_io::Logger::builder().stdout_json().build();
158/// log_io::info!(logger, "server started", port = 8080_u16);
159///
160/// // Without a logger argument the global logger is used (if any).
161/// log_io::info!("no global logger yet, so this is a no-op");
162/// ```
163#[macro_export]
164macro_rules! info {
165    ($($args:tt)+) => { $crate::__log_io_level!($crate::Level::Info; $($args)+) };
166}
167
168/// Emit a [`crate::Level::Warn`] record. See [`log_at!`](crate::log_at!) for the syntax.
169///
170/// # Examples
171///
172#[cfg_attr(all(feature = "json"), doc = "```")]
173#[cfg_attr(not(all(feature = "json")), doc = "```ignore")]
174/// # let logger = log_io::Logger::builder().stdout_json().build();
175/// log_io::warn!(logger, "slow request", path = "/api", ms = 412_u64);
176/// ```
177#[macro_export]
178macro_rules! warn {
179    ($($args:tt)+) => { $crate::__log_io_level!($crate::Level::Warn; $($args)+) };
180}
181
182/// Emit a [`crate::Level::Error`] record. See [`log_at!`](crate::log_at!) for the syntax.
183///
184/// With the default flush policy the record is flushed to every sink
185/// before the macro returns.
186///
187/// # Examples
188///
189#[cfg_attr(all(feature = "json"), doc = "```")]
190#[cfg_attr(not(all(feature = "json")), doc = "```ignore")]
191/// # let logger = log_io::Logger::builder().stdout_json().build();
192/// let err = std::io::Error::other("connection reset");
193/// log_io::error!(logger, "request failed", error = log_io::Value::Error(&err));
194/// ```
195#[macro_export]
196macro_rules! error {
197    ($($args:tt)+) => { $crate::__log_io_level!($crate::Level::Error; $($args)+) };
198}
199
200#[doc(hidden)]
201#[macro_export]
202macro_rules! __log_io_level {
203    // Global logger: the first argument is `target:` or the message literal.
204    ($level:expr; target: $target:expr, $msg:literal $($rest:tt)*) => {
205        $crate::__log_io_event!(@munch ((global) ($level) ($target) (lit $msg)) [] [] $($rest)*)
206    };
207    ($level:expr; $msg:literal $($rest:tt)*) => {
208        $crate::__log_io_event!(
209            @munch ((global) ($level) (::core::module_path!()) (lit $msg)) [] [] $($rest)*
210        )
211    };
212    // Explicit logger.
213    ($level:expr; $logger:expr, target: $target:expr, $($rest:tt)+) => {
214        $crate::__log_io_event!(@msg (logger $logger) ($level) ($target) $($rest)+)
215    };
216    ($level:expr; $logger:expr, $($rest:tt)+) => {
217        $crate::__log_io_event!(@msg (logger $logger) ($level) (::core::module_path!()) $($rest)+)
218    };
219}
220
221#[doc(hidden)]
222#[macro_export]
223macro_rules! __log_io_event {
224    // ---- message ---------------------------------------------------
225    (@msg $lg:tt $level:tt $target:tt $msg:literal $($rest:tt)*) => {
226        $crate::__log_io_event!(@munch ($lg $level $target (lit $msg)) [] [] $($rest)*)
227    };
228    (@msg $lg:tt $level:tt $target:tt $msg:expr $(, $($rest:tt)*)?) => {
229        $crate::__log_io_event!(@munch ($lg $level $target (expr $msg)) [] [] $(, $($rest)*)?)
230    };
231
232    // ---- arguments and fields ---------------------------------------
233    // End of input (optionally after a trailing comma).
234    (@munch $ctx:tt [$($a:tt)*] [$($f:tt)*] $(,)?) => {
235        $crate::__log_io_event!(@emit $ctx [$($a)*] [$($f)*])
236    };
237    // String-literal keys.
238    (@munch $ctx:tt [$($a:tt)*] [$($f:tt)*] , $k:literal = % $v:expr $(, $($rest:tt)*)?) => {
239        $crate::__log_io_event!(@munch $ctx [$($a)*]
240            [$($f)* ($crate::Field::new($k, $crate::Value::Display(&$v)))] $(, $($rest)*)?)
241    };
242    (@munch $ctx:tt [$($a:tt)*] [$($f:tt)*] , $k:literal = ? $v:expr $(, $($rest:tt)*)?) => {
243        $crate::__log_io_event!(@munch $ctx [$($a)*]
244            [$($f)* ($crate::Field::new($k, $crate::Value::Debug(&$v)))] $(, $($rest)*)?)
245    };
246    (@munch $ctx:tt [$($a:tt)*] [$($f:tt)*] , $k:literal = $v:expr $(, $($rest:tt)*)?) => {
247        $crate::__log_io_event!(@munch $ctx [$($a)*]
248            [$($f)* ($crate::Field::new($k, $crate::Value::from($v)))] $(, $($rest)*)?)
249    };
250    // Identifier and dotted-path keys.
251    (@munch $ctx:tt [$($a:tt)*] [$($f:tt)*]
252        , $k0:ident $(. $kn:ident)* = % $v:expr $(, $($rest:tt)*)?) => {
253        $crate::__log_io_event!(@munch $ctx [$($a)*]
254            [$($f)* ($crate::Field::new(
255                ::core::concat!(::core::stringify!($k0) $(, ".", ::core::stringify!($kn))*),
256                $crate::Value::Display(&$v),
257            ))] $(, $($rest)*)?)
258    };
259    (@munch $ctx:tt [$($a:tt)*] [$($f:tt)*]
260        , $k0:ident $(. $kn:ident)* = ? $v:expr $(, $($rest:tt)*)?) => {
261        $crate::__log_io_event!(@munch $ctx [$($a)*]
262            [$($f)* ($crate::Field::new(
263                ::core::concat!(::core::stringify!($k0) $(, ".", ::core::stringify!($kn))*),
264                $crate::Value::Debug(&$v),
265            ))] $(, $($rest)*)?)
266    };
267    (@munch $ctx:tt [$($a:tt)*] [$($f:tt)*]
268        , $k0:ident $(. $kn:ident)* = $v:expr $(, $($rest:tt)*)?) => {
269        $crate::__log_io_event!(@munch $ctx [$($a)*]
270            [$($f)* ($crate::Field::new(
271                ::core::concat!(::core::stringify!($k0) $(, ".", ::core::stringify!($kn))*),
272                $crate::Value::from($v),
273            ))] $(, $($rest)*)?)
274    };
275    // Anything else is a positional format argument.
276    (@munch $ctx:tt [$($a:tt)*] [$($f:tt)*] , $arg:expr $(, $($rest:tt)*)?) => {
277        $crate::__log_io_event!(@munch $ctx [$($a)* ($arg)] [$($f)*] $(, $($rest)*)?)
278    };
279
280    // ---- emission -----------------------------------------------------
281    (@emit ((global) $level:tt $target:tt $msg:tt) $args:tt $fields:tt) => {
282        if let ::core::option::Option::Some(__log_io_logger) = $crate::__private::global() {
283            $crate::__log_io_event!(@body __log_io_logger $level $target $msg $args $fields)
284        }
285    };
286    (@emit ((logger $logger:expr) $level:tt $target:tt $msg:tt) $args:tt $fields:tt) => {{
287        let __log_io_logger: &$crate::Logger = &$logger;
288        $crate::__log_io_event!(@body __log_io_logger $level $target $msg $args $fields)
289    }};
290    (@body $lg:ident ($level:expr) ($target:expr) (lit $msg:literal) [$($a:tt)*] [$($f:tt)*]) => {{
291        let __log_io_level: $crate::Level = $level;
292        let __log_io_target: &str = $target;
293        if $lg.enabled(__log_io_target, __log_io_level) {
294            $crate::__private::emit_args(
295                $lg,
296                $crate::Metadata::new(__log_io_level, __log_io_target)
297                    .with_location(::core::file!(), ::core::line!()),
298                ::core::format_args!($msg $(, $a)*),
299                &[$($f),*],
300            );
301        }
302    }};
303    (@body $lg:ident ($level:expr) ($target:expr) (expr $msg:expr) [] [$($f:tt)*]) => {{
304        let __log_io_level: $crate::Level = $level;
305        let __log_io_target: &str = $target;
306        if $lg.enabled(__log_io_target, __log_io_level) {
307            $crate::__private::emit_str(
308                $lg,
309                $crate::Metadata::new(__log_io_level, __log_io_target)
310                    .with_location(::core::file!(), ::core::line!()),
311                $msg,
312                &[$($f),*],
313            );
314        }
315    }};
316}