pub struct Logger { /* private fields */ }Expand description
A named logger with a runtime severity threshold.
A logger constructed with Logger::new has NO level of its own and
follows Logger::default_level (itself Severity::Info until someone
calls Logger::set_default_level). Logger::with_level and
Logger::set_level give it one; Logger::unset_level takes it away
again. Use register_logger to publish a 'static Logger so multiple
call sites with the same name share the same threshold.
Implementations§
Source§impl Logger
impl Logger
Sourcepub const fn new(name: &'static str) -> Logger
pub const fn new(name: &'static str) -> Logger
const-construct with NO level of its own — the logger follows
Logger::default_level, which is Severity::Info until something
moves it.
phase-467: this stored Severity::Info outright until the process
default existed, and “the author wrote new” then looked exactly like
“the author wrote with_level(.., Info)”. They are different
statements — one is a choice and the other is the absence of one — and
telling them apart is what lets Logger::set_default_level move the
first without overriding the second.
Sourcepub const fn with_level(name: &'static str, level: Severity) -> Logger
pub const fn with_level(name: &'static str, level: Severity) -> Logger
const-construct with an explicit threshold.
Sourcepub fn level(&self) -> Severity
pub fn level(&self) -> Severity
The threshold this logger actually filters on — its own level if it has
one, otherwise its nearest ancestor’s (RFC-0102 D2), otherwise
Logger::default_level.
This is rcutils_logging_get_logger_effective_level’s answer, not
rcutils_logging_get_logger_level’s: it is the number that decides
whether a record is emitted, which is the question a call site asks.
There is deliberately no accessor for “is a level set on this logger”,
because nothing in the tree has a use for the difference and an
accessor per language is three more names for one fact.
Sourcepub fn parent(&self) -> Option<&'static Logger>
pub fn parent(&self) -> Option<&'static Logger>
The nearest ancestor this logger resolves an unset level through, if
any (RFC-0102 D2). None for a root, and for every logger not yet
published to the intern table.
Sourcepub fn is_node_logger(&self) -> bool
pub fn is_node_logger(&self) -> bool
Whether a node’s accessor resolved this logger (RFC-0102 D4).
Sourcepub fn set_level(&self, level: Severity)
pub fn set_level(&self, level: Severity)
Give this logger a level of its own. It stops following
Logger::default_level until Logger::unset_level.
Sourcepub fn unset_level(&self)
pub fn unset_level(&self)
Take this logger’s own level away, so it follows
Logger::default_level again.
rcutils_logging_set_logger_level(name, RCUTILS_LOG_SEVERITY_UNSET)’s
effect, and what rclc_logger_set_level(logger, RCLC_LOG_SEVERITY_UNSET)
reaches. phase-467.
rclrs spells this logger.set_level(LogSeverity::Unset). Here it is a verb,
because Severity has no Unset: its values are the sink ABI.
Sourcepub fn default_level() -> Severity
pub fn default_level() -> Severity
The threshold every logger with no level of its own filters on.
Severity::Info until Logger::set_default_level moves it, which
is RCUTILS_DEFAULT_LOGGER_DEFAULT_LEVEL’s value and upstream’s
starting point too.
Sourcepub fn set_default_level(level: Severity)
pub fn set_default_level(level: Severity)
Move the threshold that every logger WITHOUT a level of its own filters
on — rclrs::Logger::set_default_level.
Takes effect immediately and retroactively: a static LOGGER: Logger = Logger::new("x") that was compiled into the image long before
this call follows it, because “no level of its own” is a stored state
and not a value copied in at construction. That is the whole reason
this is a capability rather than a re-export of
DEFAULT_LOGGER.set_level, which moves the threshold of the ONE logger
named "nros" (ledger row rust:Logger::set_default_level).
It does NOT override a logger that states its own level: after
with_level(.., Warn) or set_level(Warn), Warn wins.
Takes Severity, not rclrs’s LogSeverity: it has a Trace level rclrs
lacks and no Unset. Where rclrs passes Unset to restore the default
default, pass Severity::Info, which is what Unset restores upstream.
Sourcepub fn is_enabled(&self, severity: Severity) -> bool
pub fn is_enabled(&self, severity: Severity) -> bool
Whether a record at severity would be emitted by this
logger AT RUNTIME.
THE HOT PATH of every log macro, on every target, so the shape here is
measured rather than chosen (phase-467; the figures below are for the
two-level own-or-default form that preceded RFC-0102’s ancestor walk). Resolving the unset level
SELECTS the byte to compare against and compares once, instead of
comparing twice and short-circuiting: measured at an inlined call site
with rustc -O, the select form is 13 instructions on
thumbv7m-none-eabi and branch-free (LLVM predicates the whole
fallback into one ittt eq block) against 16 with a branch for the
short-circuit form, and 10 against 12 on x86_64. The cost over the
single-load original (8 thumb / 4 x86_64 instructions) is one compare
against an immediate, plus a byte load of one process-global that a
logger with its own level never performs.
LEVEL_UNSET being ABOVE Fatal rather than below Trace is what
keeps this correct if the select is ever reordered: severity >= u8::MAX is false for every Severity, so an unresolved sentinel can
only ever suppress a record, never emit one at the most verbose level —
which is the failure 0 had.
RFC-0102 D2 changed the unset arm: it now walks the parent chain before the process default. A logger WITH its own level still costs one load and one compare; an unset root pays one extra pointer load; each ancestor without a level of its own costs one more. No string work, on any arm.
The image’s compile-time CEILING (NROS_LOG_MAX_LEVEL, issue 1037) is
applied here too, first: a runtime level below it is unreachable. That
is what brings the C and C++ surfaces under the same bound — their
macros format on the C side and reach the facade through this check
(rclc_log_emit_at, nros_log_is_enabled), so the ceiling the Rust
macros fold away at compile time filters theirs at the FFI boundary.
Sourcepub fn is_enabled_throttled(
&self,
severity: Severity,
state: &ThrottleState,
now_ns: u64,
interval_ns: u64,
) -> bool
pub fn is_enabled_throttled( &self, severity: Severity, state: &ThrottleState, now_ns: u64, interval_ns: u64, ) -> bool
Whether a record at severity would be emitted, given both this
logger’s runtime threshold AND a per-call-site throttle window.
phase-417 W4.d. The order is load-bearing and is the same order
rclcpp uses: the SEVERITY test runs first, so a record the level
already filters does not consume the throttle window. Reversing them
makes a raised-then-lowered log level lose its first record for a whole
interval, which is the shape of bug nobody reports because it looks
like the throttle working.
Consumes the window when it returns true, so call it exactly once per
candidate record — the nros_*_throttle! macros do.
now_ns is the caller’s monotonic clock; the macros pass
[__timestamp_ns]. Taking it as an argument rather than reading the
clock here is what lets the whole rule be tested without a platform
port, and is the same shape rclcpp::RCLCPP_*_THROTTLE uses when it
asks for a Clock.
Source§impl Logger
impl Logger
Sourcepub fn create_child(
&'static self,
child_name: impl Borrow<str>,
) -> Result<&'static Logger, ChildError>
pub fn create_child( &'static self, child_name: impl Borrow<str>, ) -> Result<&'static Logger, ChildError>
A logger that is a descendant of this one — rclrs’s create_child,
rclcpp’s get_child (RFC-0102 D1).
Named "{self}.{child_name}", except that the catch-all
(DEFAULT_LOGGER) has top-level children: its child is named
child_name, as rclrs does for its empty-named default logger.
A child with no level of its own filters at its nearest ancestor’s
(rcutils’s rule, RFC-0102 D2), so parent.set_level(Debug) reaches it.
Idempotent: the same name returns the same logger, so a call inside a
log statement costs one intern-table lookup.
Takes &'static self because the child links to its parent by
reference. self is published to the intern table first if it was
not, so a static parent nobody registered still passes its level on.
§Errors
ChildError when the full name is over MAX_LOGGER_NAME_LEN or
empty, or no slot is left. The name is built in a stack buffer; nothing
is allocated.