defmt2log 0.1.0

Adapt full defmt logging to ordinary log records
Documentation
# `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`](https://docs.rs/log) records, so you keep:

- full `defmt` format hints and syntax in the code
- `DEFMT_LOG` compile-time filtering
- normal host `log` tooling such as `RUST_LOG`, `env_logger`, and downstream
  `log` sinks

Initialization modes:

- `init_from_current_exe()` for the normal host-binary case
- `init_from_elf_path(path)` for any ELF path with a merged `.defmt` section,
  and also for the current executable
- `init_from_merged_elf_bytes(bytes)` for pre-merged ELF bytes

## Usage

```rust,no_run
# fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
defmt2log::init_from_current_exe()?;

defmt::info!("word {=u32:#010x}", 0x1234u32);
# Ok(())
# }
```

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 `defmt` need a host `defmt` logger 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 final `rust_out` test executable keeps only
  `.defmt.end` plus `_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 `log` compile-time max-level features alone
- source locations are loaded on a best-effort basis; if they cannot be loaded,
  decoding still works and `defmt2log` warns once
- if `DEFMT_LOG` is unset or more restrictive than your callsites, normal
  `defmt::{trace,debug,info,warn,error}!` output is compiled out entirely; in
  that case `defmt2log` may still initialize successfully, but there is
  nothing for it to decode
- `DEFMT_LOG=off` and no `defmt::println!()` removes the need for a `defmt` `#[global_logger]`

## Filters

- `DEFMT_LOG` is the compile-time filter for `defmt`
- `RUST_LOG` is the runtime filter for the host `log` sink
- `max_level_*` and `release_max_level_*` affect ordinary host `log` callsites,
  not `defmt`

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=trace` with `RUST_LOG=warn` unless you intentionally want to pay
  decode cost for logs the sink will hide
- using `max_level_*` features to control `defmt`
- using `init_from_merged_elf_bytes()` for a normal host executable; that API
  is only for ELFs that already contain a merged `.defmt` section. Normal host
  binaries should use `init_from_current_exe()`.
- expecting `init_from_elf_path(path)` to synthesize a table for an arbitrary
  non-running host binary without a merged `.defmt` section; 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/maps`
- `init_from_current_exe()` is the normal Linux host path in both debug and
  release builds
- `init_from_elf_path()` and `init_from_merged_elf_bytes()` are the more
  portable modes: they work when the input already has a merged `.defmt`
  section
- 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 `.defmt` section so `defmt-decoder::Table::parse()`
  can be used directly, but that is not a supported recipe yet
- every enabled `defmt` frame is decoded in-process
- `DefmtSymbol::runtime_index` is a `u16` and can collide

## Alternatives

- only `log::*`: simplest host setup, but you give up real `defmt` format
  hints and `DEFMT_LOG` compile-time filtering. That is a real loss for
  embedded-style diagnostics, and with `defmt2log` it 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 full `defmt` syntax and hints on host too, `defmt2log` is the
  better fit.
- `defmt-logger`: also aims at `defmt` -> `log`, but `defmt2log` is built
  around current `defmt-decoder` `log` interop and preserves the normal host
  `log` pipeline end to end: `RUST_LOG` filtering, existing sinks, and other
  downstream `log` machinery. It also avoids taking a dependency on the older
  `defmt-logger 0.1.0` / `defmt-decoder 0.1.x` stack.
- external decoding such as `defmt-print`: keeps full `defmt`, but moves the
  decode and process orchestration outside the program. `defmt2log` keeps the
  same logging stream inside the host process and feeds ordinary `log` sinks
  directly.