Nonblocking-Logger - A High-Performance Rust Logging Library
A high-performance logging library for Rust with format string support and WebAssembly compatibility. Nonblocking-Logger provides fast, efficient logging with minimal overhead.
đ Key Advantages Over Other Logging Crates
High Performance
- Fast I/O: Direct writing to targets with minimal overhead
- Low latency: Logs are written immediately
- Simple and reliable: Clean, straightforward implementation
Format String Support Like println!
- Familiar API: Use
info!("User {} logged in", user_id)just likeprintln! - Full format specifiers: Support for
{:.2},{:?},{:#x}, etc. - Lazy evaluation: Expensive computations only run when needed with
debug_lazy!()
WebAssembly Ready
- Browser console logging: Works seamlessly in WebAssembly targets
- Same API everywhere: Write once, run on native and web platforms
Performance Optimized
- Minimal dependencies: Only
simple-datetime-rsfor time formatting - Efficient I/O: Fast writing with minimal overhead
- Memory efficient: String formatting with minimal allocations
Developer Experience
- Multiple output targets: stdout, stderr, files, or any
Writeimplementer - Custom time formats: Use standard datetime format strings
- Level-specific formatting: Different formats for different log levels
- Comprehensive examples: 10+ examples covering all use cases
Features
- Macro-based logging with formatting -
info!(),debug!(),error!()etc. with format string support - Date and time formatting - use standard datetime format strings for timestamps
- Multiple output targets - write to stdout, stderr, files, or any
Writeimplementer - Custom Write targets - add any
Write + Send + Syncimplementer as a logging target - Custom log string formats for different levels
- Environment variable configuration - configure log levels via environment variables
- Lazy evaluation - avoid expensive computations when log level is insufficient
- WebAssembly (WASM) support - works in browsers with console logging
- Lightweight - minimal dependencies (only
simple-datetime-rs) - Simple API - easy to use and configure
Quick Start
The library provides macros with format string support just like println!:
use ;
Macro-based Logging
The library provides macros that work like println! but for logging, with full format string support.
Available Macros
use ;
// Basic logging macros with format string support
info!;
error!;
warning!;
debug!;
trace!;
// With formatting (like println!)
let user_id = 42;
let status = "active";
info!;
warning!;
debug!;
// Generic logging macro (always outputs, no level filtering)
use log;
log!;
Logger-Specific Macros
When you have a specific logger instance, use the *_with macros:
use ;
Lazy Evaluation Macros
For expensive computations, use the lazy evaluation macros:
use ;
// Lazy evaluation - closure only runs if log level allows
debug_lazy!;
info_lazy!;
warning_lazy!;
error_lazy!;
trace_lazy!;
Logger-Specific Lazy Macros
For lazy evaluation with specific logger instances:
use ;
Format String Support
All macros support the same format string syntax as println!:
let number = 42;
let float = 3.14159;
let text = "hello";
// Basic formatting
info!;
// Different format specifiers
info!;
info!;
// Debug formatting for complex data
let data = vec!;
let metadata = from;
debug!;
Try the macro example:
# Run the included example
When to Use Each Method
Use Macros when:
- You want format string support like
println! - You're building any application
- You want the most convenient API
Use Logger-Specific Macros (*_with) when:
- You have a specific logger instance with custom configuration
- You want format string support with a particular logger
- You need different loggers for different parts of your application
- You want to combine macro convenience with logger-specific settings
Use Function calls when:
- You prefer function calls over macros
Use Logger API when:
- You need custom time formats or level-specific formatting
- You want to write to multiple targets (stdout + file, etc.)
- You need different loggers for different parts of your application
- You want full control over configuration
Always-Output vs Level-Filtered Logging
The library provides two types of logging methods:
Always-Output Methods (no level filtering):
log!(format, ...)- Always outputs regardless of logger levellog_lazy!(|| format, ...)- Always outputs with lazy evaluationlogger.log(message)- Always outputs regardless of logger levellogger.log_lazy(message_fn)- Always outputs with lazy evaluation
Level-Filtered Methods (respect logger level):
info!(format, ...),error!(format, ...), etc. - Filtered by levelinfo_lazy!(|| format, ...),error_lazy!(|| format, ...), etc. - Filtered by levellogger.info(message),logger.error(message), etc. - Filtered by levellogger.info_lazy(message_fn),logger.error_lazy(message_fn), etc. - Filtered by level
When to use each:
- Use always-output methods when you want to ensure a message is always logged (e.g., critical system events, audit logs)
- Use level-filtered methods for normal application logging where you want to control verbosity via log levels
Example:
use ;
Time Format Strings
The time formatting uses standard datetime format strings for maximum flexibility:
%Y-%m-%d %H:%M:%S- Date and time (default): "2025-09-14 19:50:08"%H:%M:%S- Time only: "19:50:08"%Y-%m-%d- Date only: "2025-09-14"%Y-%m-%d %H:%M:%S%.3f- With milliseconds: "2025-09-14 19:50:08.123"%Y-%m-%d %H:%M:%S.%f- With fractional seconds: "2025-09-14 19:50:08.123456"%Y-%m-%d %H:%M:%S %z- With timezone: "2025-09-14 19:50:08 Z"
Examples:
"%Y-%m-%d %H:%M:%S"â "2025-09-14 19:50:08""%H:%M:%S"â "19:50:08""%Y-%m-%d"â "2025-09-14""%Y-%m-%d %H:%M:%S%.3f"â "2025-09-14 19:50:08.123""%Y-%m-%d %H:%M:%S.%f"â "2025-09-14 19:50:08.123456"
Environment Variable Configuration
The library supports configuring log levels through environment variables, following Rust conventions:
Basic Configuration
Set the global log level using either RUST_LOG or LOG_LEVEL:
# Using RUST_LOG (Rust convention)
# Using LOG_LEVEL (fallback)
Supported Log Levels
errororerr- Error messages onlywarningorwarn- Warning and error messagesinfo- Informational, warning, and error messages (default)debug- Debug, info, warning, and error messagestrace- All messages including trace
Case Insensitive
Log levels are case-insensitive:
# Same as debug
# Same as warn
# Same as error
API Reference
Global Convenience Functions
Simple string logging without formatting (also available as function calls):
Basic Logging Functions
error(message: &str) -> io::Result<()>- Log error message (filtered by level)warning(message: &str) -> io::Result<()>- Log warning message (filtered by level)info(message: &str) -> io::Result<()>- Log info message (filtered by level)debug(message: &str) -> io::Result<()>- Log debug message (filtered by level)trace(message: &str) -> io::Result<()>- Log trace message (filtered by level)log(message: &str) -> io::Result<()>- Log message (always outputs, no level filtering)
Lazy Evaluation Functions
error_lazy<F>(message_fn: F) -> io::Result<()>- Log error with lazy evaluation (filtered by level)warning_lazy<F>(message_fn: F) -> io::Result<()>- Log warning with lazy evaluation (filtered by level)info_lazy<F>(message_fn: F) -> io::Result<()>- Log info with lazy evaluation (filtered by level)debug_lazy<F>(message_fn: F) -> io::Result<()>- Log debug with lazy evaluation (filtered by level)trace_lazy<F>(message_fn: F) -> io::Result<()>- Log trace with lazy evaluation (filtered by level)log_lazy<F>(message_fn: F) -> io::Result<()>- Log with lazy evaluation (always outputs, no level filtering)
Initialization Functions
get_global_logger() -> &'static Logger- Get the global logger instance
Logger
The logger struct with fluent API for custom formatting or multiple targets.
Methods
new()- Create a new logger with default settingswith_level(level)- Create a logger with specific log levelfrom_env()- Create a logger with level from environment variablestime_format(format)- Set time format using datetime format stringno_time_prefix()- Disable time prefixformat(format)- Set a single custom format for all levelsformat_for_level(level, format)- Set custom format for specific leveladd_target(target)- Add custom Write target (anyWrite + Send + Syncimplementer)custom(target)- Set custom Write target (replaces all existing targets)stdout()- Add stdout as targetstderr()- Add stderr as targetfile(path)- Add file as targetlog(message)- Log message (always outputs, no level filtering)log_lazy(message_fn)- Log with lazy evaluation (always outputs, no level filtering)error(message)- Log error message (filtered by level)warning(message)- Log warning message (filtered by level)info(message)- Log info message (filtered by level)debug(message)- Log debug message (filtered by level)trace(message)- Log trace message (filtered by level)
LogLevel
Log levels in order of priority (higher levels include lower ones):
Error- Error messagesWarning- Warning messagesInfo- Informational messagesDebug- Debug messagesTrace- Trace messages
LogMessage
A structured log message with level.
Methods
new(level, message)- Create new log messageerror(message)- Create error messagewarning(message)- Create warning messageinfo(message)- Create info messagedebug(message)- Create debug messagetrace(message)- Create trace message
Format String Placeholders
{time}- Time prefix (formatted using the time_format setting){level}- Log level (ERROR, WARN, INFO, DEBUG, TRACE){message}- The actual log message
How Time Formatting Works
The time_format() method sets the datetime format string used to format the {time} placeholder. This allows you to:
- Set a global time format that applies to all log levels
- Use different time formats in custom level-specific formats
- Mix time formats with custom formatting
// Global time format affects all levels
let logger = new
.time_format // Sets time format for {time} placeholder
.stdout;
// Custom level formats can use the {time} placeholder
let logger = new
.time_format
.format_for_level
.format_for_level
.stdout;
// Or build the format string dynamically (for example, with a TLS prefix)
const LOG_PREFIX: &str = "worker-1";
let logger = new
.time_format
.format
.stdout;
WebAssembly (WASM) Support
This library supports WebAssembly targets (wasm32-unknown-unknown) for use in web browsers. When compiled for WASM:
- Logs are written directly to the browser's developer console
- File logging is not available (browsers don't allow direct file access)
- Same API as the native version for easy porting
WASM Usage
use ;
Using AsyncWrite with Logging
While the library doesn't have built-in AsyncWrite support, you can easily convert AsyncWrite to Write for logging. In most cases, short log message write operations are fast and don't benefit from async I/O, making synchronous logging more efficient. See the complete example:
This example shows how to:
- Convert any
AsyncWrite + Unpinto a syncWritetrait - Bridge async and sync I/O seamlessly using
futures::block_on - Create custom AsyncWrite implementations
- Use async streams with synchronous logging infrastructure
License
MIT