# logging — Configurable logging with compile-time level elision
The logging system provides `debug`, `info`, `warn`, and `error` macros
that wrap the built-in `log/*` functions. The level check happens
at compile time — when `DEFT_LOG_LEVEL` filters out a level, the
filtered call sites produce zero runtime code (not even a branch).
## Quick Start
require "deft/stdext/logging" [info "app started"] [debug [str "loading " $path " in " $duration "ms"]] [warn "disk usage at " $pct "%"] [error "failed to open " $path]
Use `require` (not `import`) so the macros — and the level check — are
available at compile time.
Control verbosity via the `DEFT_LOG_LEVEL` environment variable:
```sh
DEFT_LOG_LEVEL=error deft # only errors
DEFT_LOG_LEVEL=warn deft # warnings + errors
DEFT_LOG_LEVEL=info deft # info + warnings + errors (default if unset)
DEFT_LOG_LEVEL=debug deft # everything
DEFT_LOG_LEVEL=none deft # all logging suppressed
````
The level is read once at compile time (when the module is `require`d),
so changing `DEFT_LOG_LEVEL` requires a recompile for the filtering to
shift. Each macro wraps its call in `[macro/compile-when ...]` — filtered-out
call sites compile to a single `push_null`; the `if` branch and `log/*`
call never enter the bytecode.
## Macros
| Macro | Forwards to | Surfaces in error console | Included levels |
|-------|-------------|---------------------------|-----------------|
| `[debug ...]` | `[log/debug ...]` | No | debug only |
| `[info ...]` | `[log/info ...]` | Yes | debug, info |
| `[warn ...]` | `[log/warn ...]` | Yes | debug, info, warn |
| `[error ...]` | `[log/error ...]` | Yes | debug, info, warn, error |
All macros accept multiple arguments (variadic via `@args`), matching the
built-in `[log/* ...]` functions:
[info "user " $name " logged in from " $ip]
Each macro automatically appends source location — file, line, and column
of the call site — to every log message:
[info "server started"] # /tmp/deft.log: [1778100000000] INFO server started (myapp.dft:15:1)
## Level hierarchy
```
debug (most verbose)
↓
info
↓
warn
↓
error (least verbose)
↓
off / none (all suppressed)
````
Setting `DEFT_LOG_LEVEL` to a level includes that level and everything above it
in the hierarchy. For example, `DEFT_LOG_LEVEL=warn` passes warn and error but
suppresses info and debug.
## Built-in fallback
The underlying `[log/debug ...]`, `[log/info ...]`, `[log/warn ...]`, and
`[log/error ...]` functions remain available and always write to `/tmp/deft.log`.
They are not affected by `DEFT_LOG_LEVEL` — only the macros check the level.
Use the built-ins when you need guaranteed logging that bypasses the filter.
## Implementation
The macros are defined in `stdext/logging.dft`. `require` runs the
module at compile time, which sets `log-level` from the environment
and registers the macros. Each macro wraps its `log/*` call in
`[macro/compile-when ...]` — the condition (`[in? @{...} $log-level]`) is
evaluated during compilation of each call site, so filtered levels
produce no runtime code.
def log-level ([os/env "DEFT_LOG_LEVEL"] or "debug") defmacro info {|@args| [macro/compile-when [in? @{"debug" "info"} $log-level] [log/info [macro/splice $args] [str " (" [__FILE__] ":" [__LINE__] ":" [__COL__] ")"]]] }
See macro/compile-when for the full conditional-elision mechanism.
## See Also
`log/debug`, `log/info`, `log/warn`, `log/error`, `env`, `in?`