🪵 nanologger

A minimal logger for Rust.
Part of the nano crate family — minimal, zero-dependency building blocks for Rust:
- nanocolor — terminal colors and styles
- nanospinner — terminal spinners
- nanoprogress — progress bars
- nanologger — minimal logger
- nanotime — time utilities
Colored, leveled logging to stderr with format!-style macros, optional timestamps, source locations, thread info, module filtering, file logging, and log facade integration — all in a single file with minimal dependencies.

Motivation
Most Rust logging crates are feature-rich but pull in large dependency trees or offer far more than you need. If all you want is colored leveled output to stderr and maybe a log file, those crates are overkill.
nanologger solves this by providing the essentials and nothing more:
- Colored, bold level prefixes (via nanocolor) with automatic TTY detection
- Five log macros:
error!,warn!,info!,debug!,trace! - One-liner initialization with sensible defaults
- Optional timestamps, source locations, and thread info
- File/writer logging in plain text
- Combined logger with per-output level filtering
- Module allow/deny filtering
- Runtime level changes
- Environment variable configuration (
NANOLOGGER_LEVEL) - Optional
logfacade backend (feature flag)
Comparison
nanologger is intentionally minimal. If you need structured logging, spans, async instrumentation, or regex-based env filtering, use tracing or env_logger — they're great crates.
nanologger is for when you just want colored leveled output to stderr and maybe a log file, without pulling in a dependency tree.
| Feature | nanologger |
simplelog |
env_logger |
tracing |
|---|---|---|---|---|
| Dependencies (transitive) | 2* | 11 | 15 | 16† |
| Clean build (release) | ~0.3s | ~2.0s | ~3.8s | ~3.0s |
| Colored output | ✓ | ✓ | ✓ | ✓‡ |
| File / Write logging | ✓ | ✓ | ✗ | ✓‡ |
| Combined logger | ✓ | ✓ | ✗ | ✓‡ |
| Per-output level filter | ✓ | ✓ | ✗ | ✓‡ |
| Module filtering | ✓ | ✓ | ✓ | ✓‡ |
| Source location | ✓ | ✓ | ✗ | ✓‡ |
| Thread info | ✓ | ✓ | ✗ | ✓‡ |
| Timestamps | ✓ | ✓ | ✓ | ✓‡ |
| Runtime level changes | ✓ | ✗ | ✓ | ✓ |
| Env var configuration | ✓ | ✗ | ✓ | ✓‡ |
log facade backend |
✓ | ✓ | ✓ | ✓ |
| Structured logging / spans | ✗ | ✗ | ✗ | ✓ |
| Regex-based filtering | ✗ | ✗ | ✓ | ✓‡ |
| Async instrumentation | ✗ | ✗ | ✗ | ✓ |
* nanocolor and nanotime, both zero-dependency crates themselves. † tracing + tracing-subscriber (the typical setup). ‡ Via tracing-subscriber.
Build times measured on Apple M1 with cargo build --release from a clean state.
Quick Start (demo)
use ;
Or with the simplest possible setup:
Usage
Basic logging
use ;
new
.level
.init
.unwrap;
error!;
warn!;
info!;
debug!;
trace!;
Messages at or above the configured level are written to stderr with colored prefixes. Messages below the level are silently discarded. Calling a log macro before initialization is safe — it's a no-op.
Timestamps
new
.level
.timestamps
.init
.unwrap;
info!; // "14:30:05.042 [INFO] with timestamp"
Source location (example)
new
.level
.source_location
.init
.unwrap;
info!; // "[INFO] [src/main.rs:8] started"
Thread info (example)
new
.level
.thread_info
.timestamps
.init
.unwrap;
info!; // "14:30:05.042 (main) [INFO] from main"
new
.name
.spawn
.unwrap
.join
.unwrap;
Named threads show their name; unnamed threads show ThreadId(N).
Environment variable (example)
The builder reads NANOLOGGER_LEVEL from the environment automatically. If unset or invalid, it defaults to Info.
NANOLOGGER_LEVEL=trace
Runtime level changes
set_level; // open the floodgates
set_level; // quiet down
File logging (example)
use ;
use File;
let file = create.expect;
new
.level
.add_output
.init
.unwrap;
Writer outputs always produce plain text (no ANSI codes).
Combined logger (example)
Route different severity levels to different destinations:
use ;
use File;
let file = create.unwrap;
new
.level
.add_output // terminal: Warn and above
.add_output // file: everything
.init
.unwrap;
Each output applies its own level filter independently.
Module filtering (example)
new
.level
.module_allow
.module_deny
.add_output
.init
.unwrap;
- Allow list: only emit messages from matching module prefixes (empty = allow all)
- Deny list: discard messages from matching module prefixes
- Allow is applied first, then deny
Test logger (example)
For use in #[test] functions — output is captured by Rust's test harness:
new
.level
.add_output
.init
.unwrap;
Output is plain text via print!(), so it only appears on test failure (or with --nocapture).
Colored message content (example)
nanologger re-exports nanocolor's Colorize trait, style() helper, and StyledString, so you can style log message content without adding nanocolor as a separate dependency:
use ;
info!;
error!;
let version = style.cyan.bold;
info!;
Disabling colors (example)
Colors are on by default when stderr is a TTY, and off when piped. You can also control them explicitly:
# Via environment variable (respected by nanocolor)
NO_COLOR=1
// Or programmatically
set_colors_override; // force colors off
clear_colors_override; // restore automatic TTY detection
log facade integration (example)
Enable the log feature to use nanologger as a backend for the log crate:
[]
= { = "0.1.0", = ["log"] }
use ;
new
.level
.init
.unwrap;
// log crate macros now route through nanologger
info!;
// nanologger's own macros still work alongside
info!;
Kitchen sink (example)
Every feature in one place — timestamps, source location, thread info, module filtering, combined logger with per-output levels, styled content, runtime level changes, and log facade integration:
use ;
use File;
Terminal shows Info+, the file gets everything. Check kitchen_sink.log for the full trace-level output.
Message format
{timestamp} {(thread)} {[LEVEL]} {[file:line]} {message}
Each segment is omitted when its feature is disabled. Colors and bold are applied to the level tag when stderr is a TTY; plain text otherwise.
| Level | Color | Tag |
|---|---|---|
| Error | Red bold | [ERROR] |
| Warn | Yellow bold | [WARN] |
| Info | Green bold | [INFO] |
| Debug | Blue bold | [DEBUG] |
| Trace | Magenta bold | [TRACE] |
API Reference
LogLevel
| Method | Returns | Description |
|---|---|---|
LogLevel::from_str(s) |
Result<LogLevel, ParseLevelError> |
Parse from string (case-insensitive) |
Display (format!) |
— | Lowercase: "error", "warn", etc. |
.as_u8() |
u8 |
Numeric representation (0–4) |
LogLevel::from_u8(val) |
Option<LogLevel> |
From numeric value. None for > 4 |
.tag() |
String |
Bracketed uppercase tag, e.g. [ERROR] |
LoggerBuilder
| Method | Returns | Description |
|---|---|---|
LoggerBuilder::new() |
LoggerBuilder |
New builder. Reads NANOLOGGER_LEVEL env var, defaults to Info |
.level(level) |
Self |
Set minimum log level |
.get_level() |
LogLevel |
Get currently configured level |
.timestamps(enabled) |
Self |
Enable/disable HH:MM:SS.mmm timestamp prefix |
.source_location(enabled) |
Self |
Enable/disable [file:line] in output |
.thread_info(enabled) |
Self |
Enable/disable thread name/ID in output |
.module_allow(modules) |
Self |
Set module allow list (prefix matching) |
.module_deny(modules) |
Self |
Set module deny list (prefix matching) |
.add_output(output) |
Self |
Add an output destination |
.init() |
Result<(), InitError> |
Initialize the global logger. Errors if already initialized |
LogOutput
| Constructor | Description |
|---|---|
LogOutput::term(level) |
Stderr with color support. Applies its own level filter |
LogOutput::writer(level, w) |
Any impl Write + Send + 'static. Plain text, own level filter |
LogOutput::test(level) |
Via print!(), captured by test harness. Plain text, own level filter |
Free functions
| Function | Description |
|---|---|
nanologger::init() |
Initialize with defaults (Info level, stderr, no timestamps) |
nanologger::set_level(level) |
Change global log level at runtime. No-op if not initialized |
nanologger::matches_module_filter(path, allow, deny) |
Check if a module path passes the filter |
Macros
| Macro | Level |
|---|---|
nanologger::error!(...) |
Error |
nanologger::warn!(...) |
Warn |
nanologger::info!(...) |
Info |
nanologger::debug!(...) |
Debug |
nanologger::trace!(...) |
Trace |
All macros accept format!-style arguments. Safe to call before initialization (no-op).
Re-exports from nanocolor
| Item | Description |
|---|---|
Colorize |
Trait for .red(), .bold(), .cyan(), etc. on strings and values |
style(value) |
Wrap a value for styling (useful for formatted/dynamic values) |
StyledString |
The styled string type returned by Colorize methods |
Contributing
Contributions are welcome. To get started:
- Fork the repository
- Create a feature branch (
git checkout -b my-feature) - Make your changes
- Run the tests:
cargo test - Submit a pull request
Please keep changes minimal and focused. This crate's goal is to stay small and dependency-light.
License
This project is licensed under the MIT License.