Expand description
§defmt2log
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
§Usage
env_logger::init();
defmt2log::init_from_current_exe()?;
defmt::info!("word {=u32:#010x}", 0x1234u32);This example is no_run because rustdoc and libtest have special host-side
constraints:
- normal debug and release host binaries work
- plain libtest unit tests that emit
defmtneed a hostdefmtlogger linked into that test binary, or they fail to link on_defmt_acquire,_defmt_write, and friends - 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
Use a normal host example or binary or spawn the host binary in an integration test.
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]
§Filters
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.
§Avoid
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. Normal host binaries should useinit_from_current_exe(). - expecting
init_from_elf_path(path)to synthesize a table for an arbitrary non-running host binary without a merged.defmtsection; that fallback is current-executable-only
§Limitations
- intended for normal host binaries first: no unittests, doctests yet
- it’s typically less efficient than pure
log; the overhead is the sum of the defmt overheads: serialization, deserialization, and formatting init_from_current_exe()is Linux-oriented today: the split-.defmt.*synthetic fallback depends on/proc/self/mapsinit_from_current_exe()is the normal Linux host path in both debug and release buildsinit_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 - every enabled
defmtframe is decoded in-process DefmtSymbol::runtime_indexis au16and can collide
§Alternatives
- only
log::*: simplest host setup, but you give up realdefmtformat hints andDEFMT_LOGcompile-time filtering. That is a real loss for embedded-style diagnostics, and withdefmt2logit is unnecessary. defmt-or-log: useful when one shared codebase must compile against either backend, but the shared callsites have to stay within the portable subset. If you want fulldefmtsyntax and hints on host too,defmt2logis the better fit.defmt-logger: also aims atdefmt->log, butdefmt2logis built around currentdefmt-decoderloginterop and preserves the normal hostlogpipeline end to end:RUST_LOGfiltering, existing sinks, and other downstreamlogmachinery. It also avoids taking a dependency on the olderdefmt-logger 0.1.0/defmt-decoder 0.1.xstack.- 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 and feeds ordinarylogsinks directly.
Enums§
Functions§
- init_
from_ current_ exe - Initialize from the current host executable.
- init_
from_ elf_ path - Initialize from an explicit ELF path.
- init_
from_ merged_ elf_ bytes - Initialize from explicit ELF bytes that already contain a merged
.defmtsection.