| Crates.io | rust-loguru |
| lib.rs | rust-loguru |
| version | 0.1.18 |
| created_at | 2025-04-20 06:21:51.059462+00 |
| updated_at | 2025-05-03 03:20:21.920976+00 |
| description | A flexible and extensible logging library for Rust. Similar functionality as python's loguru package. |
| homepage | |
| repository | https://github.com/j-raghavan/rust-loguru |
| max_upload_size | |
| id | 1641431 |
| size | 975,260 |
A flexible and efficient logging library for Rust inspired by Python's Loguru. Designed to provide an intuitive, powerful logging experience while maintaining Rust's performance characteristics.
Add this to your Cargo.toml:
[dependencies]
rust-loguru = "0.1.15" # Or Newer version




Standard Logging Performance:
slog shows exceptional performance for most standard operations (3-6 ns)
loguru consistently performs well (160-180 ns), significantly faster than log and tracing
log and tracing show higher execution times (400-650 ns)
Concurrent Logging:
File Rotation:
High Volume Logging:
For very high volumes (10,000 elements), slog maintains exceptional performance
loguru handles large volumes significantly better than both log and tracing
use rust_loguru::{info, debug, error, init, LogLevel, Logger};
use rust_loguru::handler::console::ConsoleHandler;
use std::sync::Arc;
use parking_lot::RwLock;
fn main() {
// Initialize the global logger with a console handler
let handler = Arc::new(RwLock::new(
ConsoleHandler::stderr(LogLevel::Debug)
.with_colors(true)
));
let mut logger = Logger::new(LogLevel::Debug);
logger.add_handler(handler);
// Set the global logger (required for macros to work)
init(logger);
// Log messages at different levels
debug!("This is a debug message");
info!("This is an info message");
error!("This is an error message: {}", "something went wrong");
// Log with metadata
use rust_loguru::log_with_metadata;
log_with_metadata!(LogLevel::Info, "user_id" => "123", "session" => "abc123";
"User logged in: {}", "johndoe");
}
Rust-Loguru provides a rich set of macros for ergonomic and powerful logging, context, and error handling. Here are some advanced macro usages:
use rust_loguru::{push_context, pop_context, set_context, get_context};
push_context!("user" => "alice", "role" => "admin");
set_context!("user", "bob");
let user = get_context!("user");
println!("Current user: {}", user.unwrap());
pop_context!();
use rust_loguru::{scope, scoped_info};
// Simple scope guard for timing and indentation
let _guard = scope!("my_scope");
// Log entering and exiting a scope with timing
let _scope = scoped_info!("important_section");
use rust_loguru::{log_error, log_error_with_context, try_log};
let err = "something went wrong";
log_error!(err);
log_error!(err, "custom message");
log_error_with_context!(err, "context info");
let res: Result<i32, &str> = Err("fail");
let _ = try_log!(res, "operation failed");
let opt: Option<i32> = None;
let _ = try_log!(option opt, "missing value");
use rust_loguru::{log_if_enabled, LogLevel};
let result = log_if_enabled!(LogLevel::Info, "This will only log if enabled at compile time");
use rust_loguru::info_kv;
info_kv!("User login event"; "user_id" => "123", "ip" => "192.168.1.1");
Rust-Loguru supports the following log levels, in order of increasing severity:
Handlers determine where log messages are sent. Rust-Loguru comes with:
use rust_loguru::handler::console::ConsoleHandler;
use rust_loguru::LogLevel;
// Output to stderr with INFO level
let handler = ConsoleHandler::stderr(LogLevel::Info)
.with_colors(true)
.with_pattern("{time} {level} [{file}:{line}] {message}");
use rust_loguru::handler::file::FileHandler;
use rust_loguru::LogLevel;
use std::path::Path;
// Write to a file with rotation at 10MB and keep 5 old files
let handler = FileHandler::new(Path::new("app.log")).unwrap()
.with_rotation(10 * 1024 * 1024)
.with_retention(5)
.with_colors(false);
You can add structured metadata to log records:
use rust_loguru::{Record, LogLevel, log};
// Using the Record API directly
let record = Record::new(
LogLevel::Info,
"User logged in",
Some("auth_module".to_string()),
Some("auth.rs".to_string()),
Some(42),
)
.with_metadata("user_id", "123")
.with_metadata("ip_address", "192.168.1.1");
log(&record);
// Or using the macro
log_with_metadata!(LogLevel::Info,
"user_id" => "123",
"ip_address" => "192.168.1.1";
"User logged in: {}", "johndoe"
);
Customize how logs are formatted:
use rust_loguru::formatter::Formatter;
let formatter = Formatter::new()
.with_colors(true)
.with_timestamp(true)
.with_level(true)
.with_module(true)
.with_location(true)
.with_pattern("{time} {level} [{file}:{line}] {message}");
Use built-in configuration presets:
use rust_loguru::LoggerConfig;
// Development configuration with detailed logging
let config = LoggerConfig::development();
// Production configuration with minimal logging
let config = LoggerConfig::production();
You can load logger configuration from a TOML file or string. This is useful for externalizing configuration and making it easy to adjust logging without code changes.
Example TOML file (loguru_config.toml):
level = "Debug"
capture_source = true
use_colors = false
format = "[TOML] {level} {message}"
Loading from a TOML file:
use rust_loguru::config::LoggerConfigBuilder;
let config = LoggerConfigBuilder::new()
.from_toml_file("loguru_config.toml")
.unwrap()
.build();
Loading from a TOML string:
use rust_loguru::config::LoggerConfigBuilder;
let toml = r#"
level = "Info"
use_colors = true
"#;
let config = LoggerConfigBuilder::new()
.from_toml_str(toml)
.unwrap()
.build();
Precedence:
level, capture_source, use_colors, format.Rust-Loguru provides a ScopeGuard utility for measuring the duration of code blocks and managing indentation for nested scopes. This is useful for profiling, debugging, and structured logging of code execution.
use rust_loguru::ScopeGuard;
use std::thread;
use std::time::Duration;
fn main() {
// Enter a scope and measure its duration
let scope = ScopeGuard::enter("outer");
println!("Indent level: {}", ScopeGuard::indent_level());
thread::sleep(Duration::from_millis(50));
{
let _inner = ScopeGuard::enter("inner");
println!("Indent level: {}", ScopeGuard::indent_level());
thread::sleep(Duration::from_millis(20));
// _inner dropped here, indentation decreases
}
println!("Indent level: {}", ScopeGuard::indent_level());
println!("Elapsed in outer: {:?}", scope.elapsed());
// scope dropped here
}
ScopeGuard::elapsed() to get the time spent in a scope.Rust-Loguru provides ergonomic helpers for error handling, context, and panic reporting.
Result and Optionuse rust_loguru::{ResultExt, OptionExt};
fn might_fail() -> Result<(), std::io::Error> {
Err(std::io::Error::new(std::io::ErrorKind::Other, "fail"))
}
fn main() {
// Log an error if it occurs
might_fail().log_error("Failed to do something important");
// Add context to errors
let res = might_fail().with_context(|| "while reading config file");
if let Err(e) = res {
eprintln!("Error with context: {}", e);
}
// Log if an Option is None
let value: Option<u32> = None;
value.log_none("Expected a value but got None");
}
use rust_loguru::{ResultExt, error_chain};
fn main() {
let res: Result<(), std::io::Error> = Err(std::io::Error::new(std::io::ErrorKind::Other, "fail"));
let res = res.with_context(|| "while doing something");
if let Err(e) = res {
for cause in error_chain(&e) {
eprintln!("Cause: {}", cause);
}
}
}
use rust_loguru::install_panic_hook;
fn main() {
install_panic_hook(); // Log all panics with file/line info
// ... rest of your code ...
}
use rust_loguru::{source_location, log_error_with_location};
fn main() {
let err = "Something went wrong";
log_error_with_location!(err);
log_error_with_location!(err, "while processing request");
}
Implement the Handler trait to create custom handlers:
use rust_loguru::handler::Handler;
use rust_loguru::level::LogLevel;
use rust_loguru::record::Record;
use rust_loguru::formatter::Formatter;
#[derive(Debug)]
struct CustomHandler {
level: LogLevel,
enabled: bool,
formatter: Formatter,
}
impl Handler for CustomHandler {
fn level(&self) -> LogLevel {
self.level
}
fn set_level(&mut self, level: LogLevel) {
self.level = level;
}
fn enabled(&self) -> bool {
self.enabled
}
fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
fn formatter(&self) -> &Formatter {
&self.formatter
}
fn set_formatter(&mut self, formatter: Formatter) {
self.formatter = formatter;
}
fn handle(&mut self, record: &Record) -> bool {
if !self.enabled || record.level() < self.level {
return false;
}
let formatted = self.formatter.format(record);
// Custom handling logic here
println!("Custom handler: {}", formatted);
true
}
}
Use the structured data capability:
use rust_loguru::{Record, LogLevel, log};
use serde_json::json;
let record = Record::new(
LogLevel::Info,
"API request completed",
Some("api_module".to_string()),
Some("api.rs".to_string()),
Some(120),
)
.with_structured_data("request", &json!({
"method": "GET",
"path": "/users",
"status": 200,
"duration_ms": 42
}))
.unwrap();
log(&record);
Rust-Loguru supports structured, thread-local, and async-propagatable context for logs. This allows you to automatically attach metadata (like user IDs, request IDs, etc.) to every log line in a given scope or async task.
use rust_loguru::{info, debug, context};
fn main() {
// Add context for the current thread (e.g., user ID)
let mut ctx = context::ContextMap::new();
ctx.insert("user_id".to_string(), context::ContextValue::String("alice".to_string()));
context::push_context(ctx);
// Log a message; context will be attached if integrated into Record/formatters
info!("User logged in");
// Add more context (e.g., request ID) in a nested scope
let mut req_ctx = context::ContextMap::new();
req_ctx.insert("request_id".to_string(), context::ContextValue::String("req-123".to_string()));
context::push_context(req_ctx);
debug!("Processing request");
// Pop request context when done
context::pop_context();
// Pop user context at the end
context::pop_context();
}
You can propagate context across async tasks:
use rust_loguru::context;
use std::thread;
let mut ctx = context::ContextMap::new();
ctx.insert("trace_id".to_string(), context::ContextValue::String("abc123".to_string()));
context::push_context(ctx);
let arc_ctx = context::propagate_context_for_async();
thread::spawn(move || {
context::set_context_from_arc(arc_ctx);
// ... logging here will see the propagated context ...
});
Log records below the configured level are filtered out early for minimal overhead
File handlers use buffered I/O for efficient disk operations
Consider using appropriate log levels in production to minimize overhead
Contributions are welcome! Please feel free to submit a Pull Request.
Rust-Loguru provides an integration module for compatibility and middleware with other logging systems and frameworks. This module is designed for advanced use cases where you want to:
log crateRust-Loguru can act as the backend for the log crate. This allows you to use the standard log macros (log::info!, log::warn!, etc.) and have them routed through Rust-Loguru.
use rust_loguru::integration::log_compat;
use log::info;
fn main() {
// Set up your logger as usual
// ...
log_compat::init_loguru_as_log();
info!("This message goes through Rust-Loguru!");
}
Rust-Loguru can integrate with async runtimes like Tokio to spawn background tasks for log management.
Requires the tokio feature.
use rust_loguru::integration::async_runtime;
#[tokio::main]
async fn main() {
// This will spawn a background task for log management (e.g., flushing)
async_runtime::integrate_with_tokio().await;
// ... your async code ...
}
use rust_loguru::integration::middleware;
fn main() {
// This will eventually provide request/response logging middleware for web frameworks
// Currently, this will panic with an unimplemented message
middleware::request_response_logging();
}
Rust-Loguru provides a set of test utilities to help you capture logs, assert on log output, and use mock loggers in your tests. These are available in the rust_loguru::test_utils module.
You can capture log records during tests and inspect them:
use rust_loguru::test_utils::LogCapture;
use rust_loguru::{LogLevel, Record};
let capture = LogCapture::new();
let record = Record::new(LogLevel::Info, "test message", None, None, None);
capture.handle(&record);
assert!(capture.contains_message("test message"));
assert!(capture.contains_level(LogLevel::Info));
Rust-Loguru provides macros to assert that a log capture contains a message or a log level:
use rust_loguru::test_utils::LogCapture;
use rust_loguru::{LogLevel, Record};
use rust_loguru::{assert_log_contains, assert_log_level};
let capture = LogCapture::new();
let record = Record::new(LogLevel::Warning, "something happened", None, None, None);
capture.handle(&record);
assert_log_contains!(capture, "something happened");
assert_log_level!(capture, LogLevel::Warning);
A mock logger is provided for testing code that expects a logger:
use rust_loguru::test_utils::MockLogger;
use rust_loguru::{LogLevel, Record};
let logger = MockLogger::new(LogLevel::Debug);
let record = Record::new(LogLevel::Info, "mocked log", None, None, None);
logger.log(&record);
assert!(logger.capture.contains_message("mocked log"));
You can temporarily set the log level for a logger in a test:
use rust_loguru::test_utils::TempLogLevel;
use rust_loguru::{LogLevel, Record};
struct MyLogger { level: LogLevel }
impl MyLogger {
fn set_level(&mut self, level: LogLevel) { self.level = level; }
}
impl rust_loguru::logger::Logger for MyLogger {
fn log(&self, _record: &Record) -> bool { true }
fn level(&self) -> LogLevel { self.level }
}
let mut logger = MyLogger { level: LogLevel::Info };
{
let _temp = TempLogLevel::new(
&mut logger,
LogLevel::Debug,
|logger, level| logger.set_level(level),
|logger| logger.level(),
);
assert_eq!(logger.level(), LogLevel::Debug);
}
assert_eq!(logger.level(), LogLevel::Info);
The following is the prioritized development plan:
Performance Improvements
slog to bring down the performance numbers downFile Handler Enhancements
Configuration System Upgrades
RUST_LOGURU_LEVEL=debug)Integration & Extensibility
Advanced Handler Features
Context & Scope Improvements
Documentation & Cleanup
src/formatter.rs)Optional/Bonus
Contributions to any of these areas are welcome! See the issues tracker for more details or to discuss implementation approaches.
This project is licensed under MIT of: