logger - Logger module
Overview
The module logger provides an implementation of the interfaces declared in util::logger.
Lifetime and copying
Logger callback, output, formatter, configuration, listener, time and persistence
interfaces are noncopyable and nonmovable. Their implementations retain this
policy; in particular, intrusive listeners must not be relocated while linked.
Their protected nonvirtual destructors prohibit destruction through interface
pointers. Destroy objects through their concrete implementation types.
The concrete ComponentConfig base remains publicly destructible and has a
virtual destructor to support its PersistentComponentConfig subclass.
EntryBuffer::EntryRef is a non-owning cursor value: copying or moving it
preserves its index and read pointer without transferring buffer ownership.
Rust API
The openbsw-logger crate (libs/bsw/logger/rust) lets Rust code log
through this C++ logger. See the crate’s cargo doc for the full API; the
notes below cover the parts that are not obvious from the signatures.
Per-component logging (preferred for first-party Rust code):
use openbsw_logger::{declare_logger_component, bsw_info};
declare_logger_component!(DEMO); // binds to C++ ::util::logger::DEMO
bsw_info!(DEMO, "value = {}", 42);
declare_logger_component!(NAME) binds a LoggerComponent to the
extern "C" getter that DECLARE_LOGGER_COMPONENT(NAME) emits on the C++
side. The component index is fetched from C++ on every log call rather than
cached. This is deliberate: the ::util::logger::<NAME> symbols hold
COMPONENT_NONE until logger::init() runs applyMapping(), so caching
an index at Rust static-init time would capture COMPONENT_NONE and never
see the real value. Fetching live removes any init-ordering constraint.
logfacade fallbackPlain
log::info!calls (e.g. from third-party crates) are routed to a single default component configured from C++ viaset_default_component; if it is unset the message is dropped. The crate name is prepended to the message so the originating module is still visible.
Warning
bsw_cpp_logger_log forwards the already-formatted Rust string via a
literal "%s" format. We do not pass it as the format string directly
because any % in the message would be read as a conversion specifier against
an empty va_list (undefined behaviour / crash).
- Level gating from Rust
Both the
bsw_*!macros and thelogfacade ask C++ whether the level is enabled (bsw_cpp_logger_is_enabled, mirroring::util::logger::Logger::isEnabled) before formatting the message, and skip all formatting work when it is disabled. Without this, a disabled formatting-heavy log would still pay its fullDisplay/Debugcost only to be dropped on the C++ side. The trade-off is one extra FFI call per enabled log site; The C++Logger::logstill re-checks the level, so the Rust check is an optimization, not the authority.
Note
A log emitted from Rust is buffered twice before it reaches an output. The Rust
side first formats the message into a stack LogBuf and passes the finished
string across the FFI; bsw_cpp_logger_log then re-enters the normal C++ path,
where BufferedLoggerOutput re-serializes the entry into its own ring buffer
for later draining. Native C++ logs only incur the second of these steps. This is
the cost of routing pre-formatted Rust strings through the shared C++ logger; the
level gate above keeps it from being paid for entries that would be dropped.