Keep writing real defmt in code that also runs on the host.
defmt2log lets a host binary decode those defmt frames and emit ordinary
log records, so you keep:
- full
defmtformat hints and syntax in the code DEFMT_LOGcompile-time filtering- normal host
logtooling such asRUST_LOG,env_logger, and downstreamlogsinks
Initialization modes:
init_from_current_exe()for the normal host-binary caseinit_from_elf_path(path)for any ELF path with a merged.defmtsection, and also for the current executableinit_from_merged_elf_bytes(bytes)for pre-merged ELF bytes
fn main() {
env_logger::init();
defmt2log::init_from_current_exe().unwrap();
defmt::info!("word {=u32:#010x}", 0x1234u32);
}- normal debug and release host binaries work as well as libtest unit
tests, examples, and integration tests; they need
defmt2log::init_from_*() - rustdoc doctest executables are worse: the bundled doctest rlib still
contains
.defmt.info.*, but the finalrust_outtest executable keeps only.defmt.endplus_defmt_version_/_defmt_encoding_, so there is no table to decode at runtime; those split metadata sections are dead-stripped from the final doctest executable
Recommended default:
- build with
DEFMT_LOG=info - run with
RUST_LOG=info - leave
logcompile-time max-level features alone - source locations are loaded on a best-effort basis; if they cannot be loaded,
decoding still works and
defmt2logwarns once - if
DEFMT_LOGis unset or more restrictive than your callsites, normaldefmt::{trace,debug,info,warn,error}!output is compiled out entirely; in that casedefmt2logmay still initialize successfully, but there is nothing for it to decode DEFMT_LOG=offand nodefmt::println!()removes the need for adefmt#[global_logger]
DEFMT_LOGis the compile-time filter fordefmtRUST_LOGis the runtime filter for the hostlogsinkmax_level_*andrelease_max_level_*affect ordinary hostlogcallsites, notdefmt
The important consequence is simple: RUST_LOG cannot bring back defmt
callsites that DEFMT_LOG compiled out.
Note that defmt::println!() is converted into INFO log level messages.
DEFMT_LOG=tracewithRUST_LOG=warnunless you intentionally want to pay decode cost for logs the sink will hide- using
max_level_*features to controldefmt - using
init_from_merged_elf_bytes()for a normal host executable; that API is only for ELFs that already contain a merged.defmtsection. - expecting
init_from_elf_path(path)to synthesize a table for an arbitrary non-running host binary without a merged.defmtsection
- no doctests yet
defmt2logtypically less efficient than purelog; the overhead is the sum of the defmt overheads: serialization, deserialization, and formatting- every compile-time-enabled
defmtframe is decoded in-process init_from_current_exe()is Linux-oriented today: the split-.defmt.*synthetic fallback depends on/proc/self/mapsinit_from_elf_path()andinit_from_merged_elf_bytes()are the more portable modes: they work when the input already has a merged.defmtsection- native macOS current-executable support is not a supported path today
- host bitflags names require linker support that preserves
.defmt.end*metadata; without that, host decoders only see the bitflags format tag and fall back to the raw numeric value - a future host-side linker setup may preserve
.defmt.end*and merge.defmt.*into one real.defmtsection sodefmt-decoder::Table::parse()can be used directly, but that is not a supported recipe yet - With the synthesized table, the naive unmerged
DefmtSymbol::runtime_index: u16can collide
- only
log::*: simplest host setup, but you give up efficientdefmtformat hints andDEFMT_LOGcompile-time filtering. defmt-or-logor thefmt.rstrick: useful when one shared codebase must compile against either backend, but the shared callsites have to stay within the portable subset.defmt-logger: also aims atdefmt->logbut it is old.defmt2logis built around currentdefmt-decoderloginterop and preserves the normal hostlogpipeline end to end:RUST_LOGfiltering, existing sinks, and other downstreamlogmachinery.- external decoding such as
defmt-print: keeps fulldefmt, but moves the decode and process orchestration outside the program.defmt2logkeeps the same logging stream inside the host process, merges with otherlogsources, and feeds ordinarylogsinks directly.