# minimal_logger
A minimal-resource, multi-platform logger for Rust with optional file output,
automatic flushing, platform-native log rotation, and change-aware runtime
reconfiguration via a builder API.
## Features
- Shared buffered file logging with `BufWriter`
- Platform-specific log rotation
- Linux / macOS: `SIGHUP`
- Windows: `Local\\RustLogger_LogRotate` named event
- Periodic background flush thread with configurable interval
- Builder-based configuration for log level, output file, buffer size, and format
- Environment-variable bootstrap via `config_from_env()`
- Falls back to `stderr` when file output is unavailable
- Change-aware runtime reconfiguration — only updated subsystems are re-initialised
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
minimal_logger = "0.4"
log = "0.4"
```
## Getting started
Construct a `MinimalLoggerConfig` and pass it to `init()` once at startup:
```rust
use log::{error, info};
fn main() {
let _guard = minimal_logger::init(
minimal_logger::MinimalLoggerConfig::new()
.level(log::LevelFilter::Info)
).expect("failed to initialise logger");
info!("application started");
error!("shutdown due to error");
}
```
Or seed configuration from the standard `RUST_LOG*` environment variables:
```rust
fn main() {
let _guard = minimal_logger::init(minimal_logger::config_from_env())
.expect("failed to initialise logger");
}
```
## Runtime reconfiguration
Call `reinit()` with a new `MinimalLoggerConfig` to update a running logger.
`reinit()` is change-aware and only updates components whose effective
configuration changed:
- log filters and max level (`.level()` / `.filter()`)
- output destination (`.file()` / `.stderr()`)
- periodic flush worker interval (`.flush_ms()`)
- rendering format (`.format()`)
- buffer capacity for newly opened file writers (`.buf_capacity()`)
Unset fields keep their **current** value — you can change a single subsystem
without repeating the full configuration. If nothing changed, `reinit()` returns
immediately.
```rust
use log::info;
fn reconfigure() {
// Switch to debug level; all other settings unchanged.
minimal_logger::reinit(
minimal_logger::MinimalLoggerConfig::new()
.level(log::LevelFilter::Debug)
.filter("myapp::db", log::LevelFilter::Trace)
);
info!("logger reconfigured");
}
```
## Configuration
| `.level(l)` | `Info` | Global log level |
| `.filter(t, l)` | *(none)* | Per-target level override; may be called many times |
| `.file(path)` | *(stderr)* | Append log records to a file (`O_APPEND`) |
| `.stderr()` | *(default)* | Explicitly route output back to stderr |
| `.buf_capacity(n)` | `4096` | Shared file `BufWriter` capacity in bytes |
| `.flush_ms(ms)` | `1000` | Periodic flush interval; `0` flushes every record |
| `.format(tmpl)` | *see below* | Log-line template with `{field}` placeholders |
New Unix log files are created with owner-only permissions, subject to the
process umask. Oversized buffer sizes, flush intervals, format templates,
format widths, and filter lists are bounded to avoid accidental memory or
latency spikes from environment variables.
### Level and filter syntax
```rust
// All targets at DEBUG
MinimalLoggerConfig::new().level(log::LevelFilter::Debug)
// Global WARN; myapp at DEBUG
MinimalLoggerConfig::new()
.level(log::LevelFilter::Warn)
.filter("myapp", log::LevelFilter::Debug)
// Layered per-module overrides
MinimalLoggerConfig::new()
.level(log::LevelFilter::Info)
.filter("myapp::db", log::LevelFilter::Trace)
.filter("hyper", log::LevelFilter::Off)
```
Recognised levels: `Off`, `Error`, `Warn`, `Info`, `Debug`, `Trace`.
When multiple filters match a target the most specific (longest prefix) wins.
### Format fields
| `{timestamp}` | `2026-04-18T12:34:56.789012Z` |
| `{level}` | `INFO` |
| `{thread_name}` | `main` |
| `{target}` | `myapp::server` |
| `{module_path}` | `myapp::server` |
| `{file}` | `src/server.rs` |
| `{line}` | `42` |
| `{args}` | `listening on :8080` |
Width and alignment: `{level:<5}` left-aligns in a field of width 5;
`{line:>4}` right-aligns. Use `{{` and `}}` for literal brace characters.
Default format string:
```text
{timestamp} [{level:<5}] T[{thread_name}] [{target}] {args}
```
## Log rotation
The logger reopens its log file on a platform-native signal or event, with no
gap in output and no lost bytes.
| Linux / macOS | `SIGHUP` |
| Other Unix | `SIGHUP` |
| Windows | `Local\RustLogger_LogRotate` named event |
When the signal fires, the logger opens a new file and atomically swaps the
active writer. Buffered output on the old writer is flushed and synced before
the old descriptor is closed.
Example logrotate configuration (Linux):
```text
/var/log/myapp.log {
daily
rotate 7
postrotate
kill -HUP $(cat /var/run/myapp.pid)
endscript
}
```
## API summary
| `init(MinimalLoggerConfig)` | Register the logger; returns a [`ShutdownGuard`] that flushes on drop. |
| `reinit(MinimalLoggerConfig)` | Apply a new config; update only changed subsystems. |
| `config_from_env()` | Build a `MinimalLoggerConfig` from `RUST_LOG*` environment variables. |
| `shutdown()` | Flush active buffered output; called automatically by `ShutdownGuard`. |
## Cargo metadata
This crate exposes docs on [docs.rs](https://docs.rs/minimal_logger).