nros_log/macros.rs
1//! Phase 88.2 — the emission macros.
2//!
3//! Each macro formats its arguments into a stack-resident
4//! [`crate::FormatBuffer`] (capacity picked at compile time by the
5//! `buffer-size-<N>` feature), wraps the result in a [`crate::Record`],
6//! and hands it to the [`crate::Logger`]'s dispatcher.
7//!
8//! Compile-time ceiling: macros below
9//! [`severity_enabled_at_compile_time`](super::severity_enabled_at_compile_time)
10//! expand to `()` — the format call is dead-code-eliminated.
11//!
12//! ## Naming (RFC-0089, settled 2026-09-04)
13//!
14//! The three ROS 2 client libraries disagree with each other about how to
15//! spell a log line:
16//!
17//! ```text
18//! rclrs log_info!(logger, "…") <- Rust follows THIS
19//! rclcpp RCLCPP_INFO(logger, "…")
20//! rcutils RCUTILS_LOG_INFO_NAMED(…)
21//! ```
22//!
23//! There is no single "ROS 2 spelling", so each of our languages follows ITS
24//! OWN upstream. For Rust that is rclrs, and rclrs's five severity macros are
25//! [`log_debug!`](crate::log_debug), [`log_info!`](crate::log_info),
26//! [`log_warn!`](crate::log_warn), [`log_error!`](crate::log_error) and
27//! [`log_fatal!`](crate::log_fatal) — which is what this module now exports.
28//! (They carry an explicit target because `#[macro_export]` puts a macro at
29//! the CRATE ROOT, not in the module whose file declares it.) The pre-rename
30//! ~~`nros_debug!`~~ .. ~~`nros_fatal!`~~ forwarders, and the
31//! `deprecate-legacy-names` feature that armed their `#[deprecated]`, were
32//! removed in phase-417 W-B5 once every in-tree call site had moved. There is
33//! no log_trace!: see below.
34//!
35//! **A name takes rclrs's spelling exactly when rclrs HAS that name.** Two
36//! families deliberately keep the `nros_` prefix, because inventing a name
37//! inside rclrs's namespace-alike would claim an upstream twin that does not
38//! exist:
39//!
40//! * [`nros_trace!`](crate::nros_trace) — rclrs stops at `debug`. TRACE is ours (ledger
41//! `rust:nros_trace`, verdict `extension`), so it keeps a name that says so.
42//! * the `nros_*_throttle!` / `nros_*_throttle_at!` family — a macro per
43//! severity, which predates the modifier form below. rclrs has no such
44//! names.
45//!
46//! **The first argument is rclrs's [`ToLogParams`](crate::ToLogParams)**
47//! (phase-515 R3), so rclrs's modifiers chain off the logger:
48//! `log_info!(logger.once(), …)`, `.skip_first()`, `.only_if(b)`, and — with
49//! the platform clock — `.throttle(d)`. A plain `&Logger` is still the common
50//! case. See [`crate::log_params`] for the bounds.
51
52/// Internal macro emitting one log record at `$severity`.
53///
54/// Use the named helpers ([`nros_trace!`](crate::nros_trace),
55/// [`log_debug!`](crate::log_debug), etc.) — they
56/// gate on the compile-time ceiling before evaluating this body.
57#[doc(hidden)]
58#[macro_export]
59macro_rules! __nros_log_emit {
60 ($logger:expr, $severity:expr, $($arg:tt)+) => {{
61 // Cheap runtime threshold check first to short-circuit
62 // disabled call sites before formatting.
63 let __logger: &$crate::Logger = $logger;
64 let __sev = $severity;
65 if __logger.is_enabled(__sev) {
66 use ::core::fmt::Write as _;
67 let mut __buf = $crate::FormatBuffer::new();
68 // Ignoring `write!` result — `FormatBuffer::write_str`
69 // never returns `Err`; overflow is signalled via the
70 // `truncated()` accessor (not the `Result`).
71 let _ = ::core::write!(__buf, $($arg)+);
72 let __record = $crate::Record {
73 severity: __sev,
74 logger_name: __logger.name(),
75 message: __buf.as_str(),
76 file: ::core::file!(),
77 line: ::core::line!(),
78 timestamp_ns: $crate::__timestamp_ns(),
79 };
80 __logger.dispatch(&__record);
81 }
82 }};
83}
84
85/// Internal macro emitting one record described by a
86/// [`LogParams`](crate::LogParams) — the five level macros, [`log!`] and
87/// [`nros_trace!`](crate::nros_trace) all expand to this (phase-515 R3).
88///
89/// The occurrence and throttle state live HERE, as statics at the call site,
90/// which is rclrs's arrangement too: a params value has no call-site identity
91/// to hang them off. A site whose params need neither — a plain
92/// `log_info!(logger, …)` — leaves both branches dead once the params are
93/// constant-folded, and the statics go with them.
94///
95/// Order, the same as the `nros_*_throttle!` family's and for the same reason:
96/// filter and severity first, so a record the level drops consumes neither
97/// the `once` flag nor the throttle window; then occurrence; then the window.
98#[doc(hidden)]
99#[macro_export]
100macro_rules! __nros_log_params_emit {
101 ($params:expr, $($arg:tt)+) => {{
102 let __p: $crate::LogParams<'_> = $params;
103 let __logger: &$crate::Logger = __p.logger();
104 let __sev = __p.get_severity();
105 if __p.get_user_filter() && __logger.is_enabled(__sev) {
106 static __NROS_LOG_OCCURS: $crate::log_params::OccurrenceState =
107 $crate::log_params::OccurrenceState::new();
108 static __NROS_LOG_THROTTLE: $crate::ThrottleState = $crate::ThrottleState::new();
109 let __now = $crate::__timestamp_ns();
110 let __throttle = __p.get_throttle();
111 if __NROS_LOG_OCCURS.admit(__p.get_occurence())
112 && (__throttle.is_zero()
113 || __NROS_LOG_THROTTLE
114 .should_log(__now, $crate::log_params::__duration_ns(__throttle)))
115 {
116 use ::core::fmt::Write as _;
117 let mut __buf = $crate::FormatBuffer::new();
118 let _ = ::core::write!(__buf, $($arg)+);
119 let __record = $crate::Record {
120 severity: __sev,
121 logger_name: __logger.name(),
122 message: __buf.as_str(),
123 file: ::core::file!(),
124 line: ::core::line!(),
125 // The reading the window was decided against — see
126 // `__nros_log_emit_throttled!`.
127 timestamp_ns: __now,
128 };
129 __logger.dispatch(&__record);
130 }
131 }
132 }};
133}
134
135/// Emit one record at a severity chosen at RUNTIME — rclrs's `log!`.
136///
137/// phase-417 G6, ledger row `rust:log`. The severity-taking emitter existed as
138/// [`__nros_log_emit!`], `#[doc(hidden)]` and named as an implementation
139/// detail, so a ported `log!(logger, severity, …)` had no supported spelling
140/// to land on. This is that spelling.
141///
142/// ```ignore
143/// let severity = if recovered { Severity::Info } else { Severity::Error };
144/// nros_log::log!(logger, severity, "link {}", state);
145/// ```
146///
147/// **It routes through exactly what the five level macros route through**, and
148/// that is the whole risk in a macro this cheap: the compile-time ceiling, the
149/// runtime `is_enabled` threshold and the one `FormatBuffer`. If it grew its
150/// own gate, `log!(logger, Severity::Debug, …)` would obey a different
151/// threshold from [`log_debug!`](crate::log_debug) and the two spellings of one call would
152/// disagree — which is what a second implementation of a gate always ends up
153/// meaning.
154///
155/// MEASURED, and worth knowing before trusting the `is_enabled` half of that
156/// sentence: [`crate::Logger::dispatch`] re-checks the threshold itself, so
157/// removing the check inside [`__nros_log_emit!`] changes nothing observable.
158/// The runtime gate is belt-and-braces; what a second implementation could
159/// actually get wrong here is the COMPILE-TIME ceiling and the severity the
160/// record carries, and those are what the test asserts.
161///
162/// **One difference from the five, forced by the runtime severity.** Those
163/// consult [`crate::severity_enabled_at_compile_time`] with a CONSTANT, so a
164/// call below the ceiling compiles to `()` and the formatting is
165/// dead-code-eliminated. Here the severity is a value, so the same check
166/// happens — at run time, on a `Severity` the compiler does not know. A call
167/// site that wants the record and its arguments gone from the image entirely
168/// names the level macro; this one keeps the `write!` in the binary and
169/// decides per call.
170///
171/// The first argument is a [`ToLogParams`](crate::ToLogParams), like the
172/// level macros', so `log!(logger.throttle(d), severity, …)` throttles; the
173/// severity argument wins over one set in the chain.
174#[macro_export]
175macro_rules! log {
176 ($logger:expr, $severity:expr, $($arg:tt)+) => {{
177 let __sev = $severity;
178 if $crate::severity_enabled_at_compile_time(__sev) {
179 $crate::__nros_log_params_emit!(
180 $crate::ToLogParams::severity($logger, __sev),
181 $($arg)+
182 );
183 }
184 }};
185}
186
187/// Emit at [`crate::Severity::Trace`].
188///
189/// Disabled at compile time unless the `max-level-trace` feature is
190/// the active ceiling (default).
191#[macro_export]
192macro_rules! nros_trace {
193 ($logger:expr, $($arg:tt)+) => {
194 if $crate::severity_enabled_at_compile_time($crate::Severity::Trace) {
195 $crate::__nros_log_params_emit!(
196 $crate::ToLogParams::severity($logger, $crate::Severity::Trace),
197 $($arg)+
198 );
199 }
200 };
201}
202
203/// Emit at [`crate::Severity::Debug`].
204///
205/// `rclrs::log_debug!`'s spelling — see the module note on RFC-0089's
206/// "each language follows its own upstream" rule. The pre-rename
207/// ~~`nros_debug!`~~ forwarder was removed in phase-417 W-B5.
208#[macro_export]
209macro_rules! log_debug {
210 ($logger:expr, $($arg:tt)+) => {
211 if $crate::severity_enabled_at_compile_time($crate::Severity::Debug) {
212 $crate::__nros_log_params_emit!($crate::ToLogParams::debug($logger), $($arg)+);
213 }
214 };
215}
216
217/// Emit at [`crate::Severity::Info`].
218///
219/// `rclrs::log_info!`'s spelling — see the module note on RFC-0089's
220/// "each language follows its own upstream" rule. The pre-rename
221/// ~~`nros_info!`~~ forwarder was removed in phase-417 W-B5.
222#[macro_export]
223macro_rules! log_info {
224 ($logger:expr, $($arg:tt)+) => {
225 if $crate::severity_enabled_at_compile_time($crate::Severity::Info) {
226 $crate::__nros_log_params_emit!($crate::ToLogParams::info($logger), $($arg)+);
227 }
228 };
229}
230
231/// Emit at [`crate::Severity::Warn`].
232///
233/// `rclrs::log_warn!`'s spelling — see the module note on RFC-0089's
234/// "each language follows its own upstream" rule. The pre-rename
235/// ~~`nros_warn!`~~ forwarder was removed in phase-417 W-B5.
236#[macro_export]
237macro_rules! log_warn {
238 ($logger:expr, $($arg:tt)+) => {
239 if $crate::severity_enabled_at_compile_time($crate::Severity::Warn) {
240 $crate::__nros_log_params_emit!($crate::ToLogParams::warn($logger), $($arg)+);
241 }
242 };
243}
244
245/// Emit at [`crate::Severity::Error`].
246///
247/// `rclrs::log_error!`'s spelling — see the module note on RFC-0089's
248/// "each language follows its own upstream" rule. The pre-rename
249/// ~~`nros_error!`~~ forwarder was removed in phase-417 W-B5.
250#[macro_export]
251macro_rules! log_error {
252 ($logger:expr, $($arg:tt)+) => {
253 if $crate::severity_enabled_at_compile_time($crate::Severity::Error) {
254 $crate::__nros_log_params_emit!($crate::ToLogParams::error($logger), $($arg)+);
255 }
256 };
257}
258
259/// Emit at [`crate::Severity::Fatal`].
260///
261/// `rclrs::log_fatal!`'s spelling — see the module note on RFC-0089's
262/// "each language follows its own upstream" rule. The pre-rename
263/// ~~`nros_fatal!`~~ forwarder was removed in phase-417 W-B5.
264#[macro_export]
265macro_rules! log_fatal {
266 ($logger:expr, $($arg:tt)+) => {
267 if $crate::severity_enabled_at_compile_time($crate::Severity::Fatal) {
268 $crate::__nros_log_params_emit!($crate::ToLogParams::fatal($logger), $($arg)+);
269 }
270 };
271}
272
273// -----------------------------------------------------------------------------
274// phase-417 W4.d — throttled emission.
275//
276// There WAS a throttle in this tree before: `nros_core::logger::Logger::
277// *_throttle`. It sat on a DIFFERENT `Logger` — the one that forwards to the
278// `log` crate — so it never reached `nros_platform_log_write`, and the
279// `Logger` the FFI hands to C and C++ had no throttle at all. Two types, the
280// capability on the unused one. The rule itself now lives in
281// [`crate::throttle`], which both this macro family and the C shim call.
282//
283// Two spellings, and the difference is WHERE THE TIME COMES FROM:
284//
285// * `nros_*_throttle!(logger, interval_ms, ...)` reads the platform clock.
286// * `nros_*_throttle_at!(logger, now_ns, interval_ms, ...)` takes it, which is
287// `rclcpp`'s shape (`RCLCPP_INFO_THROTTLE(logger, clock, ...)` names the
288// clock) and the only form available with no platform port.
289//
290// The clock-reading form REFUSES TO COMPILE without the platform clock
291// (`cfg(nros_log_clock)`, issue 1037), rather than compiling into a throttle with no time base. Without a
292// clock `__timestamp_ns()` is a constant `0`, and a window measured against a
293// constant never elapses — the site emits its first record and then nothing,
294// forever (issue 1152; before it, the same constant admitted EVERY record),
295// while reading at the call site exactly like a working throttle. RFC-0089:
296// never compile and differ.
297//
298// WHY THIS FAMILY KEEPS THE `nros_` PREFIX. rclrs has no log_info_throttle!
299// to follow: its throttle is a MODIFIER on the one macro, and since phase-515
300// R3 ours is too — `log_info!(logger.throttle(d), …)` through `ToLogParams`
301// (`crate::log_params`). That reversed the three ledger rows this note used to
302// cite against it (`rust:ToLogParams`, `rust:LogOccurrence`,
303// `rust:ThrottleClock`); their objections each had an answer:
304//
305// * the occurrence and throttle state live in statics the LEVEL MACRO
306// declares at the call site, as rclrs's do, not in the params value;
307// * `ThrottleClock` is bounded to `SteadyTime`, the one clock we can read;
308// * the clockless refusal survives: `ToLogParams::throttle` exists only
309// under `cfg(nros_log_clock)`, so a clockless `.throttle(d)` is a compile
310// error rather than a runtime field that silently admits everything.
311//
312// This family stays as our own spelling of the same capability, and the
313// `_at` half stays the ONLY throttle on a clockless target, which the
314// modifier form cannot express.
315// -----------------------------------------------------------------------------
316
317/// Resolves the timestamp the `nros_*_throttle!` family measures against.
318///
319/// With the platform clock (`cfg(nros_log_clock)` — the platform's `clock`
320/// capability or the `platform-clock` feature, issue 1037): the platform's
321/// monotonic clock. Without: a compile error — see the family's module note
322/// for why this is not a silent degradation.
323#[cfg(nros_log_clock)]
324#[doc(hidden)]
325#[macro_export]
326macro_rules! __nros_throttle_now {
327 () => {
328 $crate::__timestamp_ns()
329 };
330}
331
332/// See the `nros_log_clock` arm above.
333#[cfg(not(nros_log_clock))]
334#[doc(hidden)]
335#[macro_export]
336macro_rules! __nros_throttle_now {
337 () => {
338 ::core::compile_error!(
339 "nros_*_throttle! needs a monotonic clock: build for a platform whose \
340 nros-platform.toml declares `[capabilities] clock = true` (the lane names it through \
341 NROS_PLATFORM_NAME), enable nros-log's `platform-clock` feature where a port is linked, \
342 or use nros_*_throttle_at!(logger, now_ns, interval_ms, ...) and pass the \
343 time yourself. Without a clock `__timestamp_ns()` is a constant 0, so the window \
344 would admit every record while looking exactly like a working throttle."
345 )
346 };
347}
348
349/// Internal macro emitting one throttled record at `$severity`.
350///
351/// `$state` is a `&'static ThrottleState`; the public macros declare it as a
352/// `static` at the call site, which is what makes the window per-SITE rather
353/// than per-logger.
354#[doc(hidden)]
355#[macro_export]
356macro_rules! __nros_log_emit_throttled {
357 ($logger:expr, $severity:expr, $state:expr, $now_ns:expr, $interval_ms:expr, $($arg:tt)+) => {{
358 let __logger: &$crate::Logger = $logger;
359 let __sev = $severity;
360 let __now: u64 = $now_ns;
361 let __interval = $crate::interval_ms_to_ns($interval_ms as u64);
362 // Severity first, window second — see `Logger::is_enabled_throttled`.
363 // Also short-circuits the format call, like the unthrottled macros.
364 if __logger.is_enabled_throttled(__sev, $state, __now, __interval) {
365 use ::core::fmt::Write as _;
366 let mut __buf = $crate::FormatBuffer::new();
367 let _ = ::core::write!(__buf, $($arg)+);
368 let __record = $crate::Record {
369 severity: __sev,
370 logger_name: __logger.name(),
371 message: __buf.as_str(),
372 file: ::core::file!(),
373 line: ::core::line!(),
374 // The timestamp the window was decided against, not a second
375 // reading — two clock reads for one record is how a record can
376 // be stamped before the window it opened.
377 timestamp_ns: __now,
378 };
379 __logger.dispatch(&__record);
380 }
381 }};
382}
383
384/// Declares the per-call-site window and forwards. Internal.
385#[doc(hidden)]
386#[macro_export]
387macro_rules! __nros_throttle_site {
388 ($logger:expr, $severity:expr, $now_ns:expr, $interval_ms:expr, $($arg:tt)+) => {{
389 static __NROS_THROTTLE_SITE: $crate::ThrottleState = $crate::ThrottleState::new();
390 $crate::__nros_log_emit_throttled!(
391 $logger, $severity, &__NROS_THROTTLE_SITE, $now_ns, $interval_ms, $($arg)+
392 );
393 }};
394}
395
396/// Emit at [`crate::Severity::Trace`], at most once per `interval_ms` at this
397/// call site.
398///
399/// Reads the platform clock; needs `cfg(nros_log_clock)` (see
400/// [`nros_trace_throttle_at!`](crate::nros_trace_throttle_at) for the form that takes the time).
401#[macro_export]
402macro_rules! nros_trace_throttle {
403 ($logger:expr, $interval_ms:expr, $($arg:tt)+) => {
404 if $crate::severity_enabled_at_compile_time($crate::Severity::Trace) {
405 $crate::__nros_throttle_site!(
406 $logger,
407 $crate::Severity::Trace,
408 $crate::__nros_throttle_now!(),
409 $interval_ms,
410 $($arg)+
411 );
412 }
413 };
414}
415
416/// Emit at [`crate::Severity::Trace`], at most once per `interval_ms` at this
417/// call site, measured against the `now_ns` the CALLER supplies.
418///
419/// `rclcpp`'s shape — `RCLCPP_TRACE_THROTTLE(logger, clock, …)` names its clock
420/// too — and the only throttled form available with no platform clock.
421#[macro_export]
422macro_rules! nros_trace_throttle_at {
423 ($logger:expr, $now_ns:expr, $interval_ms:expr, $($arg:tt)+) => {
424 if $crate::severity_enabled_at_compile_time($crate::Severity::Trace) {
425 $crate::__nros_throttle_site!(
426 $logger, $crate::Severity::Trace, $now_ns, $interval_ms, $($arg)+
427 );
428 }
429 };
430}
431
432/// Emit at [`crate::Severity::Debug`], at most once per `interval_ms` at this
433/// call site.
434///
435/// Reads the platform clock; needs `cfg(nros_log_clock)` (see
436/// [`nros_debug_throttle_at!`](crate::nros_debug_throttle_at) for the form that takes the time).
437#[macro_export]
438macro_rules! nros_debug_throttle {
439 ($logger:expr, $interval_ms:expr, $($arg:tt)+) => {
440 if $crate::severity_enabled_at_compile_time($crate::Severity::Debug) {
441 $crate::__nros_throttle_site!(
442 $logger,
443 $crate::Severity::Debug,
444 $crate::__nros_throttle_now!(),
445 $interval_ms,
446 $($arg)+
447 );
448 }
449 };
450}
451
452/// Emit at [`crate::Severity::Debug`], at most once per `interval_ms` at this
453/// call site, measured against the `now_ns` the CALLER supplies.
454///
455/// `rclcpp`'s shape — `RCLCPP_DEBUG_THROTTLE(logger, clock, …)` names its clock
456/// too — and the only throttled form available with no platform clock.
457#[macro_export]
458macro_rules! nros_debug_throttle_at {
459 ($logger:expr, $now_ns:expr, $interval_ms:expr, $($arg:tt)+) => {
460 if $crate::severity_enabled_at_compile_time($crate::Severity::Debug) {
461 $crate::__nros_throttle_site!(
462 $logger, $crate::Severity::Debug, $now_ns, $interval_ms, $($arg)+
463 );
464 }
465 };
466}
467
468/// Emit at [`crate::Severity::Info`], at most once per `interval_ms` at this
469/// call site.
470///
471/// Reads the platform clock; needs `cfg(nros_log_clock)` (see
472/// [`nros_info_throttle_at!`](crate::nros_info_throttle_at) for the form that takes the time).
473#[macro_export]
474macro_rules! nros_info_throttle {
475 ($logger:expr, $interval_ms:expr, $($arg:tt)+) => {
476 if $crate::severity_enabled_at_compile_time($crate::Severity::Info) {
477 $crate::__nros_throttle_site!(
478 $logger,
479 $crate::Severity::Info,
480 $crate::__nros_throttle_now!(),
481 $interval_ms,
482 $($arg)+
483 );
484 }
485 };
486}
487
488/// Emit at [`crate::Severity::Info`], at most once per `interval_ms` at this
489/// call site, measured against the `now_ns` the CALLER supplies.
490///
491/// `rclcpp`'s shape — `RCLCPP_INFO_THROTTLE(logger, clock, …)` names its clock
492/// too — and the only throttled form available with no platform clock.
493#[macro_export]
494macro_rules! nros_info_throttle_at {
495 ($logger:expr, $now_ns:expr, $interval_ms:expr, $($arg:tt)+) => {
496 if $crate::severity_enabled_at_compile_time($crate::Severity::Info) {
497 $crate::__nros_throttle_site!(
498 $logger, $crate::Severity::Info, $now_ns, $interval_ms, $($arg)+
499 );
500 }
501 };
502}
503
504/// Emit at [`crate::Severity::Warn`], at most once per `interval_ms` at this
505/// call site.
506///
507/// Reads the platform clock; needs `cfg(nros_log_clock)` (see
508/// [`nros_warn_throttle_at!`](crate::nros_warn_throttle_at) for the form that takes the time).
509#[macro_export]
510macro_rules! nros_warn_throttle {
511 ($logger:expr, $interval_ms:expr, $($arg:tt)+) => {
512 if $crate::severity_enabled_at_compile_time($crate::Severity::Warn) {
513 $crate::__nros_throttle_site!(
514 $logger,
515 $crate::Severity::Warn,
516 $crate::__nros_throttle_now!(),
517 $interval_ms,
518 $($arg)+
519 );
520 }
521 };
522}
523
524/// Emit at [`crate::Severity::Warn`], at most once per `interval_ms` at this
525/// call site, measured against the `now_ns` the CALLER supplies.
526///
527/// `rclcpp`'s shape — `RCLCPP_WARN_THROTTLE(logger, clock, …)` names its clock
528/// too — and the only throttled form available with no platform clock.
529#[macro_export]
530macro_rules! nros_warn_throttle_at {
531 ($logger:expr, $now_ns:expr, $interval_ms:expr, $($arg:tt)+) => {
532 if $crate::severity_enabled_at_compile_time($crate::Severity::Warn) {
533 $crate::__nros_throttle_site!(
534 $logger, $crate::Severity::Warn, $now_ns, $interval_ms, $($arg)+
535 );
536 }
537 };
538}
539
540/// Emit at [`crate::Severity::Error`], at most once per `interval_ms` at this
541/// call site.
542///
543/// Reads the platform clock; needs `cfg(nros_log_clock)` (see
544/// [`nros_error_throttle_at!`](crate::nros_error_throttle_at) for the form that takes the time).
545#[macro_export]
546macro_rules! nros_error_throttle {
547 ($logger:expr, $interval_ms:expr, $($arg:tt)+) => {
548 if $crate::severity_enabled_at_compile_time($crate::Severity::Error) {
549 $crate::__nros_throttle_site!(
550 $logger,
551 $crate::Severity::Error,
552 $crate::__nros_throttle_now!(),
553 $interval_ms,
554 $($arg)+
555 );
556 }
557 };
558}
559
560/// Emit at [`crate::Severity::Error`], at most once per `interval_ms` at this
561/// call site, measured against the `now_ns` the CALLER supplies.
562///
563/// `rclcpp`'s shape — `RCLCPP_ERROR_THROTTLE(logger, clock, …)` names its clock
564/// too — and the only throttled form available with no platform clock.
565#[macro_export]
566macro_rules! nros_error_throttle_at {
567 ($logger:expr, $now_ns:expr, $interval_ms:expr, $($arg:tt)+) => {
568 if $crate::severity_enabled_at_compile_time($crate::Severity::Error) {
569 $crate::__nros_throttle_site!(
570 $logger, $crate::Severity::Error, $now_ns, $interval_ms, $($arg)+
571 );
572 }
573 };
574}
575
576/// Emit at [`crate::Severity::Fatal`], at most once per `interval_ms` at this
577/// call site.
578///
579/// Reads the platform clock; needs `cfg(nros_log_clock)` (see
580/// [`nros_fatal_throttle_at!`](crate::nros_fatal_throttle_at) for the form that takes the time).
581#[macro_export]
582macro_rules! nros_fatal_throttle {
583 ($logger:expr, $interval_ms:expr, $($arg:tt)+) => {
584 if $crate::severity_enabled_at_compile_time($crate::Severity::Fatal) {
585 $crate::__nros_throttle_site!(
586 $logger,
587 $crate::Severity::Fatal,
588 $crate::__nros_throttle_now!(),
589 $interval_ms,
590 $($arg)+
591 );
592 }
593 };
594}
595
596/// Emit at [`crate::Severity::Fatal`], at most once per `interval_ms` at this
597/// call site, measured against the `now_ns` the CALLER supplies.
598///
599/// `rclcpp`'s shape — `RCLCPP_FATAL_THROTTLE(logger, clock, …)` names its clock
600/// too — and the only throttled form available with no platform clock.
601#[macro_export]
602macro_rules! nros_fatal_throttle_at {
603 ($logger:expr, $now_ns:expr, $interval_ms:expr, $($arg:tt)+) => {
604 if $crate::severity_enabled_at_compile_time($crate::Severity::Fatal) {
605 $crate::__nros_throttle_site!(
606 $logger, $crate::Severity::Fatal, $now_ns, $interval_ms, $($arg)+
607 );
608 }
609 };
610}