Crates.io | nonblocking-logger |
lib.rs | nonblocking-logger |
version | 0.1.3 |
created_at | 2025-09-17 16:35:10.713755+00 |
updated_at | 2025-09-18 07:49:23.076382+00 |
description | A high-performance library with format string support |
homepage | |
repository | https://gitlab.com/Efimster/log |
max_upload_size | |
id | 1843575 |
size | 125,084 |
A high-performance logging library for Rust with format string support and WebAssembly compatibility. Nonblocking-Logger provides fast, efficient logging with minimal overhead.
println!
info!("User {} logged in", user_id)
just like println!
{:.2}
, {:?}
, {:#x}
, etc.debug_lazy!()
Browser console logging: Works seamlessly in WebAssembly targets
Same API everywhere: Write once, run on native and web platforms
simple-datetime-rs
for time formattingMultiple 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
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
The library provides macros with format string support just like println!
:
use nonblocking_logger::{info, error, debug, warning, trace};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let user_id = 42;
let status = "active";
// Simple messages
info!("Application started")?;
error!("Something went wrong!")?;
// With formatting (like println!)
info!("User {} has status: {}", user_id, status)?;
warning!("CPU usage is {:.1}%", 85.5)?;
debug!("Processing data: {:?}", vec![1, 2, 3])?;
Ok(())
}
The library provides macros that work like println!
but for logging, with full format string support.
use nonblocking_logger::{log, info, debug, warning, error, trace};
// Basic logging macros with format string support
info!("Application started")?;
error!("Something went wrong!")?;
warning!("This is a warning")?;
debug!("Debug information")?;
trace!("Very detailed trace")?;
// With formatting (like println!)
let user_id = 42;
let status = "active";
info!("User {} has status: {}", user_id, status)?;
warning!("CPU usage is {:.1}%", 85.5)?;
debug!("Processing data: {:?}", vec![1, 2, 3])?;
// Generic logging macro (always outputs, no level filtering)
use nonblocking_logger::log;
log!("Custom message with value: {}", 42)?;
When you have a specific logger instance, use the *_with
macros:
use nonblocking_logger::{log_with, info_with, error_with, debug_with, Logger};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let logger = Logger::new().stdout();
// Use specific logger with macros
log_with!(logger, "Processing {} items", 5)?;
info_with!(logger, "User {} connected", 42)?;
error_with!(logger, "Failed to process: {}", "timeout")?;
debug_with!(logger, "Debug info: {:?}", vec![1, 2, 3])?;
Ok(())
}
For expensive computations, use the lazy evaluation macros:
use nonblocking_logger::{debug_lazy, info_lazy, warning_lazy, error_lazy, trace_lazy};
fn expensive_computation() -> String {
// Some expensive work
"result".to_string()
}
// Lazy evaluation - closure only runs if log level allows
debug_lazy!(|| "Expensive computation result: {}", expensive_computation())?;
info_lazy!(|| "User {}: {}", 42, expensive_computation())?;
warning_lazy!(|| "CPU usage: {:.1}%", 85.5)?;
error_lazy!(|| "Error code: {}", 500)?;
trace_lazy!(|| "Step {} completed", "validation")?;
For lazy evaluation with specific logger instances:
use nonblocking_logger::{log_lazy_with, info_lazy_with, debug_lazy_with, Logger};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let logger = Logger::new().stdout();
// Lazy evaluation with specific logger
log_lazy_with!(logger, || "Expensive: {}", expensive_computation())?;
info_lazy_with!(logger, || "User {}: {}", 42, expensive_computation())?;
debug_lazy_with!(logger, || "Debug: {:?}", expensive_computation())?;
Ok(())
}
All macros support the same format string syntax as println!
:
let number = 42;
let float = 3.14159;
let text = "hello";
// Basic formatting
info!("Integer: {}, Float: {:.2}, Text: {}", number, float, text)?;
// Different format specifiers
info!("Hex: 0x{:x}, Binary: 0b{:b}, Octal: 0o{:o}", number, number, number)?;
info!("Scientific: {:.2e}, Percentage: {:.1}%", float * 100.0, float * 100.0)?;
// Debug formatting for complex data
let data = vec![1, 2, 3, 4, 5];
let metadata = std::collections::HashMap::from([
("version", "1.0.0"),
("environment", "development"),
]);
debug!("Data: {:?}, Metadata: {:?}", data, metadata)?;
Try the macro example:
# Run the included example
cargo run --example macro_logging
Use Macros when:
println!
Use Logger-Specific Macros (*_with
) when:
Use Function calls when:
Use Logger API when:
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 evaluationLevel-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 levelWhen to use each:
Example:
use nonblocking_logger::{log, info, log_lazy, info_lazy, warning, error, log_with, info_with, Logger, LogLevel};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Global macros (use default logger)
info!("This info message will NOT appear")?;
log!("This message will ALWAYS appear")?;
// Logger-specific macros (use custom logger)
let custom_logger = Logger::with_level(LogLevel::Debug).stdout();
info_with!(custom_logger, "This will appear with debug level")?;
log_with!(custom_logger, "This will ALWAYS appear with custom logger")?;
Ok(())
}
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"The library supports configuring log levels through environment variables, following Rust conventions:
Set the global log level using either RUST_LOG
or LOG_LEVEL
:
# Using RUST_LOG (Rust convention)
export RUST_LOG=debug
# Using LOG_LEVEL (fallback)
export LOG_LEVEL=info
error
or err
- Error messages onlywarning
or warn
- Warning and error messagesinfo
- Informational, warning, and error messages (default)debug
- Debug, info, warning, and error messagestrace
- All messages including traceLog levels are case-insensitive:
export RUST_LOG=DEBUG # Same as debug
export RUST_LOG=WARN # Same as warn
export RUST_LOG=Error # Same as error
Simple string logging without formatting (also available as function calls):
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)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)get_global_logger() -> &'static Logger
- Get the global logger instanceThe logger struct with fluent API for custom formatting or multiple targets.
new()
- Create a new logger with default settings
with_level(level)
- Create a logger with specific log level
from_env()
- Create a logger with level from environment variables
time_format(format)
- Set time format using datetime format string
no_time_prefix()
- Disable time prefix
format_for_level(level, format)
- Set custom format for specific level
add_target(target)
- Add custom Write target (any Write + Send + Sync
implementer)
custom(target)
- Set custom Write target (replaces all existing targets)
stdout()
- Add stdout as target
stderr()
- Add stderr as target
file(path)
- Add file as target
log(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)
Log levels in order of priority (higher levels include lower ones):
Error
- Error messagesWarning
- Warning messagesInfo
- Informational messagesDebug
- Debug messagesTrace
- Trace messagesA structured log message with level.
new(level, message)
- Create new log message
error(message)
- Create error message
warning(message)
- Create warning message
info(message)
- Create info message
debug(message)
- Create debug message
trace(message)
- Create trace message
{time}
- Time prefix (formatted using the time_format setting){level}
- Log level (ERROR, WARN, INFO, DEBUG, TRACE){message}
- The actual log messageThe time_format()
method sets the datetime format string used to format the {time}
placeholder. This allows you to:
// Global time format affects all levels
let logger = Logger::new()
.time_format("%H:%M:%S") // Sets time format for {time} placeholder
.stdout();
// Custom level formats can use the {time} placeholder
let logger = Logger::new()
.time_format("%Y-%m-%d %H:%M:%S")
.format_for_level(LogLevel::Error, "đĨ {time} ERROR: {message}")
.format_for_level(LogLevel::Info, "âšī¸ {time} INFO: {message}")
.stdout();
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
use nonblocking_logger::{error, warning, info, debug, trace, log, log_lazy};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Log messages (will appear in browser console)
error!("This is an error")?;
info!("This is info")?;
warning!("This is a warning")?;
debug!("Debug information")?;
trace!("Trace information")?;
// Always-output logging (no level filtering)
log!("This message will always appear")?;
log_lazy!(|| "This lazy message will always appear")?;
// With formatting
let user_id = 42;
let status = "active";
info!("User {} has status: {}", user_id, status)?;
error!("Error code: {}", 500)?;
Ok(())
}
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:
cargo run --example async_write_logging
This example shows how to:
AsyncWrite + Unpin
to a sync Write
traitfutures::block_on
MIT