# Logging
`log` is a backend-agnostic logging facade designed for zero global mutable
state. It formats and delivers; the only policy it bakes in is a release-build
level filter (below).
## Levels and backends
- `LogLevel` — fixed enum `Verbose(2) … Fatal(7)`, exactly Android log
priorities; `LogPriority` is an alias. Values map 1:1 to `liblog`.
- `LogBackend` — the delivery target: `Android` (liblog), `Stderr`, `Null`.
The default is selected by **compile-time dispatch** — Android `liblog` when
built for Android, otherwise `stderr` — the immutable default, no global
state.
- `Logger::new(backend)` creates an explicit instance for runtime backend
selection; `LogBackend::Null` is reachable only through an explicit
`Logger`, never the free functions or macros.
## Release-build level filter
In `debug_assertions` builds every level is delivered. In **release builds**
levels below `Error` are dropped before dispatch — on every path, including an
explicit `Logger` instance and the macros. This is an intentional tradeoff for
release size/perf, but note it is a built-in policy: callers cannot raise the
threshold at runtime. Messages are sanitized (control characters stripped)
before delivery.
## API
- `Logger::new(backend)` → `Logger`; `logger.log(level, tag, msg)`.
- Module-level convenience: `log(level, tag, msg)` and `log_write(...)` use the
platform default dispatch.
- Twelve `#[macro_export]` macros land at the crate root: `alog_verbose!` …
`alog_fatal!` and `log_verbose!` … `log_fatal!`, each taking `(tag, fmt…args)`
or a bare `(tag)` for an empty message. They route through the free `log`
function (compile-time backend, release filter, sanitize).
## Design
No global mutable configuration. The default path is decided at compile time
(Android `liblog` vs `stderr`); anything dynamic requires an explicit `Logger`
instance passed by the caller. A `Logger` is a plain `Copy` value — clone/construct as
needed, there is no hidden process-wide registry.