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-rs
for 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
Write
implementer - 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
Write
implementer - Custom Write targets - add any
Write + Send + Sync
implementer 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
error
orerr
- Error messages onlywarning
orwarn
- 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_for_level(level, format)
- Set custom format for specific leveladd_target(target)
- Add custom Write target (anyWrite + Send + Sync
implementer)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;
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 + Unpin
to a syncWrite
trait - Bridge async and sync I/O seamlessly using
futures::block_on
- Create custom AsyncWrite implementations
- Use async streams with synchronous logging infrastructure
License
MIT