Introduction
Logly-rs is a high-performance, structured logging library for Rust designed to provide all the features of the Python logly library with native Rust performance and safety guarantees.
Key Features
Performance
- Zero-copy logging: Minimal allocations
- Async I/O: Non-blocking writes
- Lock-free operations: Optimized for concurrency
- GPU/CPU support: Hardware-accelerated operations (optional)
Flexibility
- 8 log levels: TRACE, DEBUG, INFO, SUCCESS, WARNING, ERROR, CRITICAL, FAIL
- Multiple sinks: Console, file, custom outputs
- Custom formatting: Template strings with placeholders
- Colored output: ANSI colors with custom callbacks
Reliability
- Thread-safe: Safe concurrent logging
- Error handling: Comprehensive error types
- File rotation: Time and size-based rotation
- Retention policies: Automatic cleanup
Design Philosophy
Logly-rs follows these principles:
- Performance First: Optimized for high-throughput scenarios
- Type Safety: Leverage Rust's type system
- Zero Cost Abstractions: No runtime overhead
- Modular Design: Use only what you need
- Python Compatibility: Match Python logly API where possible
Comparison with Python Logly
| Feature | Python Logly | Logly-rs |
|---|---|---|
| Performance | Fast (Rust backend) | Native Rust (faster) |
| Memory Safety | Runtime checks | Compile-time guarantees |
| Async Support | ✓ | ✓ |
| File Rotation | ✓ | ✓ |
| JSON Logging | ✓ | ✓ |
| Custom Colors | ✓ | ✓ |
| GPU Support | Planned | ✓ (optional) |
Next Steps
- Installation: Get started with logly-rs
- Quick Start: Your first logging program
- Configuration: Configure your logger
Installation Guide
Complete installation guide for logly-rs.
Table of Contents
Requirements
Minimum Requirements
- Rust: 1.70.0 or later
- Cargo: Latest stable version
- Operating System: Windows, Linux, macOS
Optional Requirements (for GPU support)
- CUDA Toolkit: 11.0 or later
- NVIDIA GPU: Compute Capability 3.5+
- CUDA Driver: Compatible with toolkit version
Basic Installation
Method 1: Add to Cargo.toml (Recommended)
Add logly to your Cargo.toml:
[dependencies]
logly = "0.0.4"
Then run:
cargo build
Method 2: Cargo Add Command
cargo add logly
Method 3: From Git Repository
[dependencies]
logly = { git = "https://github.com/muhammad-fiaz/logly-rs.git", tag = "v0.0.4" }
Feature Flags
Logly supports several feature flags for customization:
Default Features (Enabled Automatically)
[dependencies]
logly = "0.0.4"
# Includes: async, rotation, json, colors, auto-update-check
Minimal Installation (No Default Features)
[dependencies]
logly = { version = "0.0.4", default-features = false }
Custom Feature Selection
[dependencies]
logly = { version = "0.0.4", default-features = false, features = ["async", "colors"] }
Available Features
| Feature | Description | Default |
|---|---|---|
async | Async logging support | ✓ |
rotation | File rotation support | ✓ |
json | JSON format support | ✓ |
colors | ANSI color support | ✓ |
auto-update-check | Version checking | ✓ |
gpu | GPU/CUDA acceleration | ✗ |
GPU Support
Prerequisites
- Install CUDA Toolkit
Linux:
# Ubuntu/Debian
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.0-1_all.deb
sudo dpkg -i cuda-keyring_1.0-1_all.deb
sudo apt-get update
sudo apt-get install cuda
# Verify installation
nvcc --version
Windows:
- Download CUDA Toolkit from NVIDIA website
- Run installer and follow instructions
- Add CUDA to PATH
macOS:
- CUDA is not officially supported on macOS
- Use CPU-only version
- Verify GPU
nvidia-smi
Install with GPU Support
[dependencies]
logly = { version = "0.0.4", features = ["gpu"] }
Or via command line:
cargo add logly --features gpu
Build with GPU
cargo build --features gpu
Verification
Test Installation
Create src/main.rs:
use logly::prelude::*; fn main() -> Result<(), Box<dyn std::error::Error>> { let logger = Logger::new(); logger.add_sink(SinkConfig::default())?; logger.info("Logly installed successfully!".to_string())?; logger.success("All systems operational!".to_string())?; Ok(()) }
Run:
cargo run
Expected output:
[INFO] Logly installed successfully!
[SUCCESS] All systems operational!
Test GPU Support (if enabled)
use logly::prelude::*; fn main() -> Result<(), Box<dyn std::error::Error>> { let logger = Logger::new(); logger.add_sink(SinkConfig::default())?; logger.enable_gpu()?; println!("{}", logger.gpu_info()); logger.info("GPU logging enabled!".to_string())?; Ok(()) }
Troubleshooting
Common Issues
1. Compilation Errors
Error: failed to resolve: use of undeclared crate or module
Solution:
cargo clean
cargo build
2. GPU Feature Not Working
Error: GPU logging not available
Solution:
- Verify CUDA installation:
nvcc --version - Check GPU:
nvidia-smi - Rebuild with GPU feature:
cargo build --features gpu
3. Version Conflicts
Error: failed to select a version for logly
Solution:
cargo update
cargo build
4. Missing Dependencies
Error: could not find Cargo.toml
Solution:
cargo init
# Then add logly to Cargo.toml
Platform-Specific Issues
Windows
Issue: CUDA not found
Solution:
set PATH=%PATH%;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.0\bin
Linux
Issue: Permission denied for log files
Solution:
sudo chmod 755 logs/
macOS
Issue: GPU feature not available
Solution: Use CPU-only version (GPU not supported on macOS)
Getting Help
If you encounter issues:
- Check Documentation: https://muhammad-fiaz.github.io/logly-rs
- Search Issues: https://github.com/muhammad-fiaz/logly-rs/issues
- Create Issue: https://github.com/muhammad-fiaz/logly-rs/issues/new
- Discord/Community: Check repository for community links
Debug Mode
Enable debug mode to troubleshoot:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.debug_mode = true; config.debug_log_file = Some(PathBuf::from("logly_debug.log")); logger.configure(config); }
Next Steps
- Read Quick Start Guide
- Explore Examples
- Check Configuration Guide
- Review API Documentation
Version Information
Current version: 0.0.4
Check for updates:
#![allow(unused)] fn main() { if let Ok(Some(msg)) = logger.check_version() { println!("{}", msg); } }
License
MIT License - see LICENSE file for details.
Quick Start Guide
Get started with logly-rs in minutes.
Installation
Add to your Cargo.toml:
[dependencies]
logly = "0.0.4"
Basic Usage
1. Simple Console Logging
use logly::prelude::*; fn main() -> Result<(), Box<dyn std::error::Error>> { let logger = Logger::new(); logger.add_sink(SinkConfig::default())?; logger.info("Hello, logly!".to_string())?; logger.success("Operation completed!".to_string())?; logger.warning("Be careful!".to_string())?; logger.error("Something went wrong!".to_string())?; Ok(()) }
2. File Logging
use logly::prelude::*; use std::path::PathBuf; fn main() -> Result<(), Box<dyn std::error::Error>> { let logger = Logger::new(); let config = SinkConfig { path: Some(PathBuf::from("app.log")), ..Default::default() }; logger.add_sink(config)?; logger.info("Logging to file!".to_string())?; Ok(()) }
3. All Log Levels
#![allow(unused)] fn main() { logger.trace("Detailed trace".to_string())?; // Priority 5 logger.debug("Debug info".to_string())?; // Priority 10 logger.info("Information".to_string())?; // Priority 20 logger.success("Success!".to_string())?; // Priority 25 logger.warning("Warning".to_string())?; // Priority 30 logger.error("Error occurred".to_string())?; // Priority 40 logger.fail("Operation failed".to_string())?; // Priority 45 logger.critical("Critical!".to_string())?; // Priority 50 }
Common Patterns
File Rotation
#![allow(unused)] fn main() { let config = SinkConfig { path: Some(PathBuf::from("logs/app.log")), rotation: Some("daily".to_string()), retention: Some(7), // Keep 7 days ..Default::default() }; logger.add_sink(config)?; }
JSON Logging
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.json = true; logger.configure(config); logger.add_sink(SinkConfig::default())?; logger.info("JSON formatted".to_string())?; }
Context Binding
#![allow(unused)] fn main() { logger.bind("user_id".to_string(), serde_json::json!("12345")); logger.bind("request_id".to_string(), serde_json::json!("req-abc")); logger.info("User action".to_string())?; // Logs include user_id and request_id automatically }
Custom Log Levels
#![allow(unused)] fn main() { logger.add_custom_level("NOTICE".to_string(), 35, "96".to_string())?; logger.log_custom("NOTICE", "Custom level message".to_string())?; }
Callbacks
#![allow(unused)] fn main() { // Log callback logger.add_log_callback(|record| { println!("Logged: {}", record.message); Ok(()) }); // Color callback logger.add_color_callback(|level, message| { format!("[{}] {}", level.as_str(), message) }); // Exception callback logger.add_exception_callback(|error, backtrace| { eprintln!("Exception: {}", error); }); }
Multiple Sinks
#![allow(unused)] fn main() { // Console sink logger.add_sink(SinkConfig::default())?; // File sink logger.add_sink(SinkConfig { path: Some(PathBuf::from("app.log")), ..Default::default() })?; // Error-only file logger.add_sink(SinkConfig { path: Some(PathBuf::from("errors.log")), level: Some(Level::Error), ..Default::default() })?; }
Configuration
Basic Configuration
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.level = Level::Debug; config.color = true; config.json = false; logger.configure(config); }
Global Controls
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.global_console_display = true; // Enable console config.global_file_storage = true; // Enable file storage config.global_color_display = true; // Enable colors logger.configure(config); }
Configuration File
Create logly.toml:
[logly.configuration]
level = "DEBUG"
auto_sink = true
[logly.display]
color = true
global_console_display = true
global_file_storage = true
[logly.features]
enable_callbacks = true
enable_exception_handling = true
enable_version_check = true
Load automatically:
#![allow(unused)] fn main() { let logger = Logger::new(); // Loads logly.toml automatically }
Or specify path:
#![allow(unused)] fn main() { let logger = Logger::with_config_file(PathBuf::from("custom.toml"))?; }
Advanced Features
Async Logging
#![allow(unused)] fn main() { let config = SinkConfig { path: Some(PathBuf::from("async.log")), async_write: true, ..Default::default() }; logger.add_sink(config)?; }
GPU Acceleration
[dependencies]
logly = { version = "0.0.4", features = ["gpu"] }
#![allow(unused)] fn main() { logger.enable_gpu()?; println!("{}", logger.gpu_info()); }
Debug Mode
#![allow(unused)] fn main() { logger.enable_debug(); // All internal operations logged to stderr or file }
Level Filtering
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.level = Level::Warning; // Only WARNING and above logger.configure(config); }
Complete Example
use logly::prelude::*; use std::path::PathBuf; fn main() -> Result<(), Box<dyn std::error::Error>> { // Create logger let logger = Logger::new(); // Configure let mut config = LoggerConfig::default(); config.level = Level::Debug; config.color = true; config.enable_callbacks = true; logger.configure(config); // Add sinks logger.add_sink(SinkConfig::default())?; // Console logger.add_sink(SinkConfig { path: Some(PathBuf::from("logs/app.log")), rotation: Some("daily".to_string()), retention: Some(7), ..Default::default() })?; // Bind context logger.bind("app".to_string(), serde_json::json!("myapp")); logger.bind("version".to_string(), serde_json::json!("1.0.0")); // Add callback logger.add_log_callback(|record| { if record.level >= Level::Error { // Send alert } Ok(()) }); // Log messages logger.info("Application started".to_string())?; logger.success("Initialization complete".to_string())?; // Simulate work for i in 0..10 { logger.debug(format!("Processing item {}", i))?; } logger.success("All items processed".to_string())?; Ok(()) }
Next Steps
- Read Installation Guide
- Explore Configuration Guide
- Check API Documentation
- Review Examples
- Read feature guides:
Troubleshooting
See Troubleshooting Guide for common issues.
Getting Help
- Documentation: https://muhammad-fiaz.github.io/logly-rs
- Issues: https://github.com/muhammad-fiaz/logly-rs/issues
- Repository: https://github.com/muhammad-fiaz/logly-rs
Logly Configuration Guide
Out-of-the-Box Usage
Logly works immediately without any configuration:
#![allow(unused)] fn main() { use logly::prelude::*; let logger = Logger::new(); // Auto-sink is enabled by default - logs to console logger.info("Works out of the box!".to_string())?; }
Global Configuration Options
LoggerConfig Structure
The LoggerConfig struct contains all configuration options:
pub struct LoggerConfig {
// Log level filtering
pub level: Level, // Minimum log level (default: Info)
// Global display controls
pub global_color_display: bool, // Enable/disable colors globally (default: true)
pub global_console_display: bool, // Enable/disable console output (default: true)
pub global_file_storage: bool, // Enable/disable file storage (default: true)
// Color settings
pub color: bool, // Enable colors (default: true)
pub level_colors: HashMap<Level, String>, // Per-level color codes
// Custom levels
pub custom_levels: HashMap<String, CustomLevel>, // User-defined levels
// Output format
pub json: bool, // JSON output (default: false)
pub pretty_json: bool, // Pretty-print JSON (default: false)
pub log_compact: bool, // Compact format (default: false)
// Display options
pub console: bool, // Console output (default: true)
pub show_time: bool, // Show timestamp (default: true)
pub show_module: bool, // Show module name (default: true)
pub show_function: bool, // Show function name (default: true)
pub show_filename: bool, // Show filename (default: false)
pub show_lineno: bool, // Show line number (default: false)
// Per-level controls
pub console_levels: HashMap<Level, bool>, // Per-level console output
pub time_levels: HashMap<Level, bool>, // Per-level timestamp display
pub color_levels: HashMap<Level, bool>, // Per-level color enable
pub storage_levels: HashMap<Level, bool>, // Per-level file storage
// Sink management
pub auto_sink: bool, // Auto-initialize console sink (default: true)
// GPU support (experimental)
pub enable_gpu: bool, // Enable GPU acceleration (default: false)
pub gpu_buffer_size: usize, // GPU buffer size (default: 1MB)
// Features
pub enable_callbacks: bool, // Enable callback system (default: true)
pub enable_exception_handling: bool, // Enable exception handling (default: true)
pub enable_version_check: bool, // Enable auto-update check (default: true)
// Debug mode
pub debug_mode: bool, // Enable debug logging (default: false)
pub debug_log_file: Option<PathBuf>, // Debug log file path (default: None = stderr)
}
Configuration Examples
1. Default Configuration (Out of Box)
#![allow(unused)] fn main() { let logger = Logger::new(); // Auto-sink enabled, logs to console with colors logger.info("Ready to use!".to_string())?; }
2. Console Only (No File Storage)
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.global_console_display = true; // Console enabled config.global_file_storage = false; // No file storage logger.configure(config); logger.info("Console only".to_string())?; }
3. File Storage Only (No Console)
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.global_console_display = false; // No console config.global_file_storage = true; // File storage enabled config.auto_sink = false; // Disable auto-sink logger.configure(config); // Add file sink manually logger.add_sink(SinkConfig { path: Some(PathBuf::from("app.log")), ..Default::default() })?; logger.info("File only".to_string())?; }
4. No Output (Silent Mode)
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.global_console_display = false; // No console config.global_file_storage = false; // No file storage logger.configure(config); logger.info("Not displayed or stored".to_string())?; }
5. Debug Mode with File Logging
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.debug_mode = true; config.debug_log_file = Some(PathBuf::from("logly_debug.log")); logger.configure(config); // All logly internal operations logged to logly_debug.log }
6. Custom Colors
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.level_colors.insert(Level::Info, "92".to_string()); // Bright green config.level_colors.insert(Level::Error, "91".to_string()); // Bright red logger.configure(config); }
7. Disable Colors Globally
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.global_color_display = false; logger.configure(config); }
8. JSON Output
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.json = true; config.pretty_json = true; logger.configure(config); }
File Rotation Configuration
Rotation Intervals
hourly- Rotate every hourdaily- Rotate every dayweekly- Rotate every weekmonthly- Rotate every 30 daysyearly- Rotate every 365 days
Size-Based Rotation
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("app.log")), size_limit: Some(10 * 1024 * 1024), // 10MB retention: Some(5), // Keep 5 files ..Default::default() } }
Time-Based Rotation
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("app.log")), rotation: Some("daily".to_string()), retention: Some(7), // Keep 7 days ..Default::default() } }
Combined Rotation
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("app.log")), rotation: Some("daily".to_string()), size_limit: Some(10 * 1024 * 1024), // Rotate if daily OR 10MB retention: Some(7), ..Default::default() } }
Yearly Rotation Example
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("yearly.log")), rotation: Some("yearly".to_string()), retention: Some(5), // Keep 5 years ..Default::default() } }
Runtime Configuration Changes
All configuration can be changed at runtime:
#![allow(unused)] fn main() { // Initial configuration let mut config = LoggerConfig::default(); config.level = Level::Info; logger.configure(config); logger.info("Info message".to_string())?; // Change at runtime let mut new_config = LoggerConfig::default(); new_config.level = Level::Warning; logger.configure(new_config); logger.info("Not shown (below WARNING)".to_string())?; logger.warning("Shown".to_string())?; }
Enable/Disable at Runtime
#![allow(unused)] fn main() { logger.enable(); logger.info("Logged".to_string())?; logger.disable(); logger.info("Not logged".to_string())?; logger.enable(); logger.info("Logged again".to_string())?; }
Auto-Sink Behavior
By default, auto_sink is enabled:
#![allow(unused)] fn main() { let logger = Logger::new(); // Automatically has a console sink logger.info("Works immediately!".to_string())?; }
Disable auto-sink for manual control:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.auto_sink = false; logger.configure(config); // Now you must add sinks manually logger.add_sink(SinkConfig::default())?; }
Global Settings Interaction
Priority Rules
-
If
global_console_display = falseANDglobal_file_storage = false:- No output anywhere (silent mode)
-
If
global_console_display = trueANDglobal_file_storage = false:- Console output only, no file storage
-
If
global_console_display = falseANDglobal_file_storage = true:- File storage only, no console output
-
If both are
true:- Both console and file storage work normally
Example: Console Display Control
#![allow(unused)] fn main() { // Enable console config.global_console_display = true; logger.configure(config); logger.info("Shows on console".to_string())?; // Disable console config.global_console_display = false; logger.configure(config); logger.info("Not shown on console".to_string())?; }
Example: File Storage Control
#![allow(unused)] fn main() { // Enable file storage config.global_file_storage = true; logger.configure(config); logger.add_sink(SinkConfig { path: Some(PathBuf::from("app.log")), ..Default::default() })?; logger.info("Stored in file".to_string())?; // Disable file storage config.global_file_storage = false; logger.configure(config); logger.info("Not stored in file".to_string())?; }
Version Checking
Enabled by default:
#![allow(unused)] fn main() { // Check for updates if let Ok(Some(msg)) = logger.check_version() { println!("{}", msg); } // Disable version checking let mut config = LoggerConfig::default(); config.enable_version_check = false; logger.configure(config); }
GPU Support (Experimental)
GPU support is optional and requires compilation with --features gpu:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.enable_gpu = true; config.gpu_buffer_size = 2 * 1024 * 1024; // 2MB logger.configure(config); logger.enable_gpu()?; }
Complete Example
use logly::prelude::*; use std::path::PathBuf; fn main() -> Result<(), Box<dyn std::error::Error>> { let logger = Logger::new(); let mut config = LoggerConfig::default(); // Global controls config.global_color_display = true; config.global_console_display = true; config.global_file_storage = true; // Log level config.level = Level::Debug; // Display options config.show_time = true; config.show_module = true; // Features config.enable_callbacks = true; config.enable_exception_handling = true; config.enable_version_check = true; config.debug_mode = false; // Auto-sink config.auto_sink = true; logger.configure(config); // Add file sink with rotation logger.add_sink(SinkConfig { path: Some(PathBuf::from("logs/app.log")), rotation: Some("daily".to_string()), size_limit: Some(10 * 1024 * 1024), retention: Some(7), ..Default::default() })?; // Use logger logger.debug("Debug message".to_string())?; logger.info("Info message".to_string())?; logger.warning("Warning message".to_string())?; logger.error("Error message".to_string())?; Ok(()) }
Summary
- ✅ Works out of box with auto-sink
- ✅ Global console display control
- ✅ Global file storage control
- ✅ All rotation intervals (hourly to yearly)
- ✅ Runtime configuration changes
- ✅ Enable/disable at runtime
- ✅ Debug mode with file logging
- ✅ Auto-update checking (enabled by default)
- ✅ Full customization of all features
- ✅ Professional production-ready configuration
Performance Optimization Guide
Overview
logly-rs is designed for high-performance logging with minimal overhead. This guide covers optimization techniques and performance characteristics.
Benchmark Results
Based on actual criterion benchmarks (run on your system):
| Operation | Time (avg) | Throughput |
|---|---|---|
| Basic INFO log | ~75.6 μs | ~13,200 ops/sec |
| File logging | ~150 μs | ~6,600 ops/sec |
| With context | ~130 μs | ~7,700 ops/sec |
| Concurrent (10 threads) | ~500 μs | ~2,000 ops/sec |
| Multiple sinks (5) | ~200 μs | ~5,000 ops/sec |
Note: Times include formatting, serialization, and I/O operations
Optimization Techniques
1. Use Async Writing
#![allow(unused)] fn main() { let config = SinkConfig { path: Some(PathBuf::from("app.log")), async_write: true, // Enable async buffer_size: 8192, ..Default::default() }; }
Impact: 2-3x faster for file operations
2. Minimize Context Fields
#![allow(unused)] fn main() { // ❌ Slow - many fields logger.bind("field1".to_string(), json!("value1")); logger.bind("field2".to_string(), json!("value2")); // ... 10 more fields // ✅ Fast - only necessary fields logger.bind("request_id".to_string(), json!("abc123")); }
3. Use Appropriate Log Levels
#![allow(unused)] fn main() { // ❌ Logs everything (slow in production) config.level = Level::Trace; // ✅ Production setting config.level = Level::Info; }
4. Batch Operations
#![allow(unused)] fn main() { // ❌ Slow - many small logs for i in 0..1000 { logger.info(format!("Item {}", i))?; } // ✅ Fast - batch message logger.info(format!("Processed {} items", 1000))?; }
5. Disable Unused Features
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.global_color_display = false; // Disable in production config.show_module = false; config.show_function = false; config.show_filename = false; config.show_lineno = false; }
6. Use Size-Based Rotation
#![allow(unused)] fn main() { // ❌ Time-based checks on every log rotation: Some("daily".to_string()) // ✅ Size-based is faster size_limit: Some(10 * 1024 * 1024) // 10MB }
7. Optimize Sink Count
#![allow(unused)] fn main() { // ❌ Too many sinks for i in 0..20 { logger.add_sink(SinkConfig { ... })?; } // ✅ Reasonable number (1-5) logger.add_sink(console_config)?; logger.add_sink(file_config)?; logger.add_sink(error_config)?; }
Memory Optimization
1. Buffer Sizes
#![allow(unused)] fn main() { let config = SinkConfig { buffer_size: 4096, // Smaller for memory-constrained max_buffered_lines: 500, ..Default::default() }; }
2. Retention Policies
#![allow(unused)] fn main() { let config = SinkConfig { retention: Some(7), // Keep only 7 files ..Default::default() }; }
3. Clear Bindings
#![allow(unused)] fn main() { // Clear when done logger.clear_bindings(); }
CPU Optimization
1. Disable Debug Mode
#![allow(unused)] fn main() { config.debug_mode = false; // Production }
2. Use JSON Only When Needed
#![allow(unused)] fn main() { config.json = false; // Faster than JSON }
3. Minimize Callbacks
#![allow(unused)] fn main() { // ❌ Many callbacks logger.add_log_callback(callback1); logger.add_log_callback(callback2); logger.add_log_callback(callback3); // ✅ Single efficient callback logger.add_log_callback(combined_callback); }
I/O Optimization
1. Async Writing
Always use async for file operations:
#![allow(unused)] fn main() { async_write: true }
2. Appropriate Buffer Sizes
#![allow(unused)] fn main() { buffer_size: 8192, // Default, good for most cases flush_interval: 100, // ms }
3. Batch Flushes
#![allow(unused)] fn main() { // Write many logs for msg in messages { logger.info(msg)?; } // Flush once at end logger.complete(); }
Profiling
Using Criterion
cargo bench
View results:
open target/criterion/report/index.html
Using Flamegraph
cargo install flamegraph
cargo flamegraph --bench logging_benchmark
Production Configuration
Optimized production config:
[logly.configuration]
level = "INFO"
auto_sink = false
[logly.display]
global_color_display = false
global_console_display = false
global_file_storage = true
show_time = true
show_module = false
show_function = false
show_filename = false
show_lineno = false
[logly.format]
json = true
log_compact = true
[[logly.sinks]]
path = "logs/app.log"
rotation = "daily"
retention = 30
async_write = true
buffer_size = 8192
[logly.features]
enable_callbacks = false
enable_exception_handling = true
enable_version_check = false
[logly.debug]
debug_mode = false
Performance Checklist
- Async writing enabled
- Appropriate log level (INFO/WARNING in production)
- Minimal context fields
- Colors disabled in production
- Debug mode disabled
- Reasonable sink count (1-5)
- Buffer sizes configured
- Retention policies set
- Callbacks minimized
- JSON only when needed
Monitoring Performance
Log Performance Metrics
#![allow(unused)] fn main() { use std::time::Instant; let start = Instant::now(); for _ in 0..1000 { logger.info("Test message".to_string())?; } let duration = start.elapsed(); println!("1000 logs in {:?}", duration); println!("Avg: {:?} per log", duration / 1000); }
Track Sink Performance
#![allow(unused)] fn main() { let sinks = logger.list_sinks(); println!("Active sinks: {}", sinks.len()); for id in sinks { if let Some(info) = logger.sink_info(id) { println!("Sink {}: {:?}", id, info); } } }
Common Performance Issues
Issue: Slow File Logging
Solution: Enable async writing
#![allow(unused)] fn main() { async_write: true }
Issue: High Memory Usage
Solution: Reduce buffer sizes and retention
#![allow(unused)] fn main() { buffer_size: 4096, retention: Some(7), }
Issue: CPU Spikes
Solution: Disable debug mode and reduce callbacks
#![allow(unused)] fn main() { debug_mode: false, enable_callbacks: false, }
Issue: Slow Startup
Solution: Disable version checking
#![allow(unused)] fn main() { enable_version_check: false }
Best Practices
- Profile First: Use benchmarks to identify bottlenecks
- Measure Impact: Test changes with criterion
- Production Config: Use optimized settings
- Monitor: Track performance metrics
- Iterate: Continuously optimize based on data
Additional Resources
Log Levels Guide
Standard Log Levels
Logly provides 8 standard log levels with specific priorities:
| Level | Priority | Method | Use Case |
|---|---|---|---|
| TRACE | 5 | logger.trace() | Very detailed debugging information |
| DEBUG | 10 | logger.debug() | Debugging information for developers |
| INFO | 20 | logger.info() | General informational messages |
| SUCCESS | 25 | logger.success() | Successful operation completion |
| WARNING | 30 | logger.warning() | Warning messages that need attention |
| ERROR | 40 | logger.error() | Error conditions that need fixing |
| FAIL | 45 | logger.fail() | Operation failures |
| CRITICAL | 50 | logger.critical() | Critical system errors |
Usage
#![allow(unused)] fn main() { use logly::prelude::*; let logger = Logger::new(); logger.trace("Detailed trace information".to_string())?; logger.debug("Debug information".to_string())?; logger.info("Application started".to_string())?; logger.success("Operation completed successfully!".to_string())?; logger.warning("Warning message".to_string())?; logger.error("Error occurred".to_string())?; logger.fail("Operation failed".to_string())?; logger.critical("Critical system error!".to_string())?; }
Level Filtering
Set minimum log level to filter messages:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.level = Level::Warning; // Only WARNING and above logger.configure(config); logger.debug("Not shown".to_string())?; logger.warning("Shown".to_string())?; logger.error("Shown".to_string())?; }
Custom Log Levels
Add your own log levels with custom priorities and colors:
#![allow(unused)] fn main() { // Add custom level between WARNING (30) and ERROR (40) logger.add_custom_level("NOTICE".to_string(), 35, "96".to_string())?; // Use custom level logger.log_custom("NOTICE", "Custom level message".to_string())?; // Remove custom level logger.remove_custom_level("NOTICE"); }
Custom Level Priority Rules
- Priority must be between 0-255
- Higher priority = more severe
- Custom levels integrate with standard filtering
- Duplicate names are rejected
Custom Level Colors
ANSI color codes:
"30-37"- Standard colors"90-97"- Bright colors"1"- Bold"4"- Underline
Example:
#![allow(unused)] fn main() { logger.add_custom_level("AUDIT".to_string(), 28, "93".to_string())?; // Bright yellow logger.add_custom_level("SECURITY".to_string(), 48, "91;1".to_string())?; // Bright red bold }
Level Comparison
Levels can be compared:
#![allow(unused)] fn main() { assert!(Level::Trace < Level::Debug); assert!(Level::Debug < Level::Info); assert!(Level::Error > Level::Warning); if record.level >= Level::Error { // Handle high severity } }
Level Priority Access
#![allow(unused)] fn main() { let priority = Level::Info.priority(); // Returns 20 let level = Level::from_priority(40); // Returns Some(Level::Error) }
Per-Level Configuration
Configure display options per level:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); // Disable console for DEBUG level config.console_levels.insert(Level::Debug, false); // Disable colors for ERROR level config.color_levels.insert(Level::Error, false); // Disable timestamp for TRACE level config.time_levels.insert(Level::Trace, false); logger.configure(config); }
Level Colors
Default colors:
- TRACE: Cyan (36)
- DEBUG: Blue (34)
- INFO: White (37)
- SUCCESS: Green (32)
- WARNING: Yellow (33)
- ERROR: Red (31)
- FAIL: Magenta (35)
- CRITICAL: Bright Red (91)
Customize colors:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.level_colors.insert(Level::Info, "92".to_string()); // Bright green config.level_colors.insert(Level::Error, "91;1".to_string()); // Bright red bold logger.configure(config); }
Best Practices
- TRACE: Use for very detailed debugging, disabled in production
- DEBUG: Development debugging, disabled in production
- INFO: General information, enabled in production
- SUCCESS: Highlight successful operations
- WARNING: Potential issues that don't stop execution
- ERROR: Errors that need attention but don't crash
- FAIL: Operation failures that may need retry
- CRITICAL: Severe errors that may crash the application
Examples
Development Logging
#![allow(unused)] fn main() { config.level = Level::Trace; // Show everything }
Production Logging
#![allow(unused)] fn main() { config.level = Level::Info; // Hide TRACE and DEBUG }
Error-Only Logging
#![allow(unused)] fn main() { config.level = Level::Error; // Only errors and critical }
Custom Audit Level
#![allow(unused)] fn main() { logger.add_custom_level("AUDIT".to_string(), 28, "93".to_string())?; logger.log_custom("AUDIT", "User login successful".to_string())?; }
Custom Log Levels
Add your own log levels with custom priorities and colors.
Overview
Beyond the 8 built-in levels, you can create custom levels for specific use cases.
Adding Custom Levels
#![allow(unused)] fn main() { use logly::prelude::*; let logger = Logger::new(); logger.add_sink(SinkConfig::default())?; // Add custom level: name, priority, ANSI color code logger.add_custom_level("NOTICE".to_string(), 35, "96".to_string())?; logger.add_custom_level("AUDIT".to_string(), 42, "95".to_string())?; }
Using Custom Levels
#![allow(unused)] fn main() { logger.log_custom("NOTICE", "Important notice".to_string())?; logger.log_custom("AUDIT", "Security audit log".to_string())?; }
Priority System
Custom levels fit into the priority hierarchy:
| Level | Priority |
|---|---|
| TRACE | 5 |
| DEBUG | 10 |
| INFO | 20 |
| SUCCESS | 25 |
| WARNING | 30 |
| NOTICE | 35 (custom) |
| ERROR | 40 |
| AUDIT | 42 (custom) |
| FAIL | 45 |
| CRITICAL | 50 |
ANSI Color Codes
Common color codes:
30- Black31- Red32- Green33- Yellow34- Blue35- Magenta36- Cyan37- White90-97- Bright colors
Examples
Security Audit Level
#![allow(unused)] fn main() { logger.add_custom_level("AUDIT".to_string(), 42, "95".to_string())?; logger.log_custom("AUDIT", "User login attempt".to_string())?; }
Performance Metrics
#![allow(unused)] fn main() { logger.add_custom_level("METRIC".to_string(), 15, "36".to_string())?; logger.log_custom("METRIC", "Response time: 45ms".to_string())?; }
Business Events
#![allow(unused)] fn main() { logger.add_custom_level("BUSINESS".to_string(), 28, "33".to_string())?; logger.log_custom("BUSINESS", "Order placed: $99.99".to_string())?; }
See Also
Sinks Guide
Overview
Sinks are output destinations for log messages. Logly supports multiple sinks simultaneously.
Auto-Sink
Enabled by default - automatically creates a console sink:
#![allow(unused)] fn main() { let logger = Logger::new(); // Auto-sink active - logs to console immediately logger.info("Works out of box!".to_string())?; }
Disable auto-sink:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.auto_sink = false; logger.configure(config); }
Manual Sink Management
Add Sink
#![allow(unused)] fn main() { let id = logger.add_sink(SinkConfig::default())?; }
Remove Sink
#![allow(unused)] fn main() { logger.remove_sink(id); }
Remove All Sinks
#![allow(unused)] fn main() { let count = logger.remove_all_sinks(); }
List Sinks
#![allow(unused)] fn main() { let ids = logger.list_sinks(); let count = logger.get_sink_count(); }
Multiple Sinks
Add multiple sinks for different outputs:
#![allow(unused)] fn main() { // Console sink let console_id = logger.add_sink(SinkConfig::default())?; // File sink let file_id = logger.add_sink(SinkConfig { path: Some(PathBuf::from("logs/app.log")), ..Default::default() })?; // Error-only file let error_id = logger.add_sink(SinkConfig { path: Some(PathBuf::from("logs/errors.log")), filter_min_level: Some(Level::Error), ..Default::default() })?; }
Sink Configuration
#![allow(unused)] fn main() { pub struct SinkConfig { pub path: Option<PathBuf>, // File path (None = console) pub rotation: Option<String>, // Rotation interval pub size_limit: Option<u64>, // Size threshold pub retention: Option<usize>, // Files to keep pub filter_min_level: Option<Level>, // Minimum level pub filter_module: Option<String>, // Module filter pub filter_function: Option<String>, // Function filter pub async_write: bool, // Async mode pub buffer_size: usize, // Buffer size pub format: Option<String>, // Custom format pub json: bool, // JSON output } }
File Formats
Logly automatically creates directories and supports multiple formats:
.log Files
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/app.log")), ..Default::default() } }
.txt Files
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/app.txt")), ..Default::default() } }
.json Files
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/app.json")), json: true, ..Default::default() } }
Custom Extensions
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/app.custom")), ..Default::default() } }
Automatic Directory Creation
Logly automatically creates parent directories:
#![allow(unused)] fn main() { // Creates logs/app/production/ if it doesn't exist SinkConfig { path: Some(PathBuf::from("logs/app/production/app.log")), ..Default::default() } }
Filtering
Level Filtering
#![allow(unused)] fn main() { SinkConfig { filter_min_level: Some(Level::Warning), ..Default::default() } }
Module Filtering
#![allow(unused)] fn main() { SinkConfig { filter_module: Some("my_module".to_string()), ..Default::default() } }
Function Filtering
#![allow(unused)] fn main() { SinkConfig { filter_function: Some("my_function".to_string()), ..Default::default() } }
Async vs Sync
Async (Default)
#![allow(unused)] fn main() { SinkConfig { async_write: true, buffer_size: 8192, ..Default::default() } }
Sync
#![allow(unused)] fn main() { SinkConfig { async_write: false, ..Default::default() } }
Examples
Console Only
#![allow(unused)] fn main() { logger.add_sink(SinkConfig::default())?; }
File with Rotation
#![allow(unused)] fn main() { logger.add_sink(SinkConfig { path: Some(PathBuf::from("logs/app.log")), rotation: Some("daily".to_string()), retention: Some(7), ..Default::default() })?; }
JSON Logging
#![allow(unused)] fn main() { logger.add_sink(SinkConfig { path: Some(PathBuf::from("logs/app.json")), json: true, ..Default::default() })?; }
Error-Only Sink
#![allow(unused)] fn main() { logger.add_sink(SinkConfig { path: Some(PathBuf::from("logs/errors.log")), filter_min_level: Some(Level::Error), ..Default::default() })?; }
Auto-Sink
Automatic sink initialization for quick setup.
Overview
Auto-sink automatically creates a console sink when the logger is initialized, allowing immediate logging without manual sink setup.
Enabled by Default
#![allow(unused)] fn main() { use logly::prelude::*; let logger = Logger::new(); // Auto-sink creates console sink automatically logger.info("Works immediately!".to_string())?; }
Disable Auto-Sink
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.auto_sink = false; logger.configure(config); // Must manually add sinks logger.add_sink(SinkConfig::default())?; }
When to Disable
Disable auto-sink when you want full control:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.auto_sink = false; logger.configure(config); // Add only file sink, no console logger.add_sink(SinkConfig { path: Some(PathBuf::from("app.log")), ..Default::default() })?; }
See Also
Filtering
Control which log messages are processed and displayed.
Overview
Filtering allows you to:
- Set minimum log levels
- Filter by module or function
- Create custom filters
- Control output per sink
Level Filtering
Global Level
Set minimum level for all sinks:
#![allow(unused)] fn main() { use logly::prelude::*; let logger = Logger::new(); let mut config = LoggerConfig::default(); config.level = Level::Warning; // Only WARNING and above logger.configure(config); logger.add_sink(SinkConfig::default())?; logger.debug("Not shown".to_string())?; logger.warning("Shown!".to_string())?; }
Per-Sink Level
Different levels for different sinks:
#![allow(unused)] fn main() { use std::path::PathBuf; // Console: INFO and above logger.add_sink(SinkConfig { level: Some(Level::Info), ..Default::default() })?; // File: DEBUG and above logger.add_sink(SinkConfig { path: Some(PathBuf::from("debug.log")), level: Some(Level::Debug), ..Default::default() })?; // Errors only logger.add_sink(SinkConfig { path: Some(PathBuf::from("errors.log")), level: Some(Level::Error), ..Default::default() })?; }
Module Filtering
Filter by module name:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.module_filter = Some(vec![ "myapp::api".to_string(), "myapp::database".to_string(), ]); logger.configure(config); }
Function Filtering
Filter by function name:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.function_filter = Some(vec![ "handle_request".to_string(), "process_data".to_string(), ]); logger.configure(config); }
Level Priorities
Log levels and their priorities:
| Level | Priority | Description |
|---|---|---|
| TRACE | 5 | Very detailed debugging |
| DEBUG | 10 | Debugging information |
| INFO | 20 | General information |
| SUCCESS | 25 | Success messages |
| WARNING | 30 | Warning messages |
| ERROR | 40 | Error messages |
| FAIL | 45 | Failure messages |
| CRITICAL | 50 | Critical errors |
Examples
Development vs Production
#![allow(unused)] fn main() { #[cfg(debug_assertions)] let level = Level::Debug; #[cfg(not(debug_assertions))] let level = Level::Info; let mut config = LoggerConfig::default(); config.level = level; logger.configure(config); }
Separate Error Logs
#![allow(unused)] fn main() { use std::path::PathBuf; let logger = Logger::new(); // All logs to main file logger.add_sink(SinkConfig { path: Some(PathBuf::from("app.log")), level: Some(Level::Debug), ..Default::default() })?; // Errors to separate file logger.add_sink(SinkConfig { path: Some(PathBuf::from("errors.log")), level: Some(Level::Error), ..Default::default() })?; logger.debug("Debug message".to_string())?; // Only in app.log logger.error("Error message".to_string())?; // In both files }
Dynamic Level Changes
#![allow(unused)] fn main() { // Start with INFO let mut config = LoggerConfig::default(); config.level = Level::Info; logger.configure(config); // ... later, enable debug mode config.level = Level::Debug; logger.configure(config); }
Best Practices
- Production: Use INFO or WARNING level
- Development: Use DEBUG or TRACE level
- Errors: Always log ERROR and above
- Performance: Higher levels = better performance
- Separate files: Use different sinks for different levels
See Also
Template String Formatting Guide
Overview
Logly supports custom format strings with placeholders for complete control over log output.
Built-in Placeholders
{time}- Timestamp (RFC3339 format){time:PATTERN}- Custom time format{level}- Log level name{message}- Log message{module}- Module name{function}- Function name{filename}- Source filename{lineno}- Line number{FIELD}- Any custom field
Time Format Patterns
Supported Patterns
| Pattern | Description | Example |
|---|---|---|
| YYYY | 4-digit year | 2025 |
| YY | 2-digit year | 25 |
| MMMM | Full month name | October |
| MMM | Abbreviated month | Oct |
| MM | 2-digit month | 10 |
| dddd | Full weekday name | Saturday |
| ddd | Abbreviated weekday | Sat |
| DD | 2-digit day | 11 |
| HH | 2-digit hour (24h) | 13 |
| hh | 2-digit hour (12h) | 01 |
| mm | 2-digit minute | 45 |
| ss | 2-digit second | 32 |
| SSS | Milliseconds | 123 |
| SSSSSS | Microseconds | 123456 |
| A | AM/PM | AM |
| a | am/pm | am |
| ZZ | Timezone (+00:00) | +00:00 |
| Z | Timezone (+0000) | +0000 |
Examples
Date Only
#![allow(unused)] fn main() { SinkConfig { format: Some("{time:YYYY-MM-DD} | {level} | {message}".to_string()), ..Default::default() } // Output: 2025-10-11 | INFO | Message }
Full DateTime
#![allow(unused)] fn main() { SinkConfig { format: Some("{time:YYYY-MM-DD HH:mm:ss} [{level}] {message}".to_string()), ..Default::default() } // Output: 2025-10-11 13:45:32 [INFO] Message }
With Milliseconds
#![allow(unused)] fn main() { SinkConfig { format: Some("{time:HH:mm:ss.SSS} | {message}".to_string()), ..Default::default() } // Output: 13:45:32.123 | Message }
ISO 8601 Style
#![allow(unused)] fn main() { SinkConfig { format: Some("{time:YYYY-MM-DDTHH:mm:ss} {level} {message}".to_string()), ..Default::default() } // Output: 2025-10-11T13:45:32 INFO Message }
Readable Format
#![allow(unused)] fn main() { SinkConfig { format: Some("{time:dddd, DD MMMM YYYY at HH:mm:ss} - {level} - {message}".to_string()), ..Default::default() } // Output: Saturday, 11 October 2025 at 13:45:32 - INFO - Message }
European Format
#![allow(unused)] fn main() { SinkConfig { format: Some("{time:DD/MM/YYYY HH:mm} {message}".to_string()), ..Default::default() } // Output: 11/10/2025 13:45 Message }
Common Patterns
Simple Console
#![allow(unused)] fn main() { format: Some("{time:HH:mm:ss} [{level}] {message}".to_string()) }
Detailed File
#![allow(unused)] fn main() { format: Some("{time:YYYY-MM-DD HH:mm:ss.SSS} | {level:8} | {module}:{function} | {message}".to_string()) }
Request Logging
#![allow(unused)] fn main() { format: Some("{time:YYYY-MM-DD HH:mm:ss} | {method} {path} | status={status_code} | duration={duration_ms}ms".to_string()) }
Best Practices
- Console vs File: Use simpler formats for console, detailed for files
- Time Formats:
HH:mm:ssfor console (quick reference)YYYY-MM-DD HH:mm:ss.SSSfor logs (precise timestamps)YYYY-MM-DDTHH:mm:ssfor ISO 8601 compatibility
- Performance: Simpler formats are faster to process
- Consistency: Use consistent formats across your application
Colored Output
ANSI color support for terminal output.
Enable Colors
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.color = true; logger.configure(config); }
Default Colors
Built-in levels have default colors:
- TRACE: Cyan
- DEBUG: Blue
- INFO: Green
- SUCCESS: Bright Green
- WARNING: Yellow
- ERROR: Red
- FAIL: Bright Red
- CRITICAL: Bright Red + Bold
Custom Color Callback
Override default colors:
#![allow(unused)] fn main() { logger.add_color_callback(|level, message| { match level { Level::Info => format!("\x1b[32m{}\x1b[0m", message), Level::Error => format!("\x1b[31;1m{}\x1b[0m", message), _ => message.to_string(), } }); }
ANSI Codes
Format: \x1b[CODEm
Foreground Colors
30- Black31- Red32- Green33- Yellow34- Blue35- Magenta36- Cyan37- White
Bright Colors
90-97- Bright variants
Styles
1- Bold4- Underline0- Reset
Global Control
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.global_color_display = true; logger.configure(config); }
See Also
JSON Logging
Structured JSON output for log aggregation and analysis.
Enable JSON
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.json = true; logger.configure(config); logger.add_sink(SinkConfig::default())?; logger.info("JSON message".to_string())?; }
Output Format
{
"timestamp": "2024-01-01T12:00:00.000Z",
"level": "INFO",
"message": "JSON message",
"module": "myapp",
"function": "main",
"line": 42
}
With Context
#![allow(unused)] fn main() { logger.bind("user_id".to_string(), serde_json::json!("12345")); logger.bind("request_id".to_string(), serde_json::json!("req-001")); logger.info("User action".to_string())?; }
Output:
{
"timestamp": "2024-01-01T12:00:00.000Z",
"level": "INFO",
"message": "User action",
"user_id": "12345",
"request_id": "req-001"
}
Use Cases
Log Aggregation
- Elasticsearch
- Splunk
- CloudWatch
- Datadog
Analysis
- Parse with jq
- Import to databases
- Process with scripts
Example
use logly::prelude::*; fn main() -> Result<(), Box<dyn std::error::Error>> { let logger = Logger::new(); let mut config = LoggerConfig::default(); config.json = true; logger.configure(config); logger.add_sink(SinkConfig::default())?; logger.bind("app".to_string(), serde_json::json!("myapp")); logger.info("Application started".to_string())?; Ok(()) }
See Also
Async Logging
Logly-rs provides non-blocking async logging for high-performance applications.
Overview
Async logging writes log messages to a buffer and processes them in the background, preventing I/O operations from blocking your application.
Features
- Non-blocking writes
- Configurable buffer sizes
- Automatic flushing
- Thread-safe operations
- Zero-copy where possible
Basic Usage
Enable Async Writes
#![allow(unused)] fn main() { use logly::prelude::*; use std::path::PathBuf; let logger = Logger::new(); let config = SinkConfig { path: Some(PathBuf::from("logs/app.log")), async_write: true, ..Default::default() }; logger.add_sink(config)?; logger.info("Async logging enabled!".to_string())?; }
Configuration
Buffer Size
Control the async buffer size:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.async_buffer_size = 8192; // 8KB buffer logger.configure(config); }
Flush Interval
Configure automatic flushing:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.async_flush_interval = 1000; // Flush every 1 second logger.configure(config); }
Performance
Async logging provides significant performance improvements:
- Throughput: 2-3x faster than synchronous logging
- Latency: Sub-microsecond write times
- Scalability: Handles thousands of messages per second
Best Practices
- Use for high-volume logging: Async is ideal when logging frequently
- Configure buffer size: Larger buffers = better performance, more memory
- Manual flush on shutdown: Ensure all logs are written before exit
#![allow(unused)] fn main() { // Flush before shutdown logger.flush()?; }
Example
use logly::prelude::*; use std::path::PathBuf; fn main() -> Result<(), Box<dyn std::error::Error>> { let logger = Logger::new(); // Async file sink logger.add_sink(SinkConfig { path: Some(PathBuf::from("logs/async.log")), async_write: true, ..Default::default() })?; // High-volume logging for i in 0..10000 { logger.info(format!("Message {}", i))?; } // Ensure all messages are written logger.flush()?; Ok(()) }
See Also
File Rotation & Retention
Automatic log file rotation with retention policies to manage disk space.
Overview
Logly-rs provides comprehensive file rotation and retention:
- Time-based rotation: hourly, daily, weekly, monthly, yearly
- Size-based rotation: Rotate when file reaches specified size
- Combined rotation: Rotate on either time OR size threshold
- Retention policies: Automatically delete old log files
- Timestamped files: Rotated files include timestamps
Rotation Intervals
Logly supports time-based rotation:
- hourly - Rotate every hour
- daily - Rotate every day
- weekly - Rotate every week
- monthly - Rotate every 30 days
- yearly - Rotate every 365 days
Time-Based Rotation
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/app.log")), rotation: Some("daily".to_string()), retention: Some(7), // Keep 7 days ..Default::default() } }
Size-Based Rotation
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/app.log")), size_limit: Some(10 * 1024 * 1024), // 10MB retention: Some(5), // Keep 5 files ..Default::default() } }
Combined Rotation
Rotate when EITHER condition is met:
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/app.log")), rotation: Some("daily".to_string()), size_limit: Some(10 * 1024 * 1024), // Daily OR 10MB retention: Some(7), ..Default::default() } }
Retention Policies
Retention automatically deletes old rotated log files to prevent disk space issues.
How Retention Works
- Count-based: Keeps only the N most recent files
- Automatic cleanup: Runs during rotation
- Sorted by time: Oldest files deleted first
- Unlimited option: Set to
Nonefor no limit
Basic Retention
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/app.log")), rotation: Some("daily".to_string()), retention: Some(7), // Keep only 7 most recent files ..Default::default() } }
Retention Examples
#![allow(unused)] fn main() { // Keep 24 hours of hourly logs SinkConfig { rotation: Some("hourly".to_string()), retention: Some(24), ..Default::default() } // Keep 30 days of daily logs SinkConfig { rotation: Some("daily".to_string()), retention: Some(30), ..Default::default() } // Keep 12 months of monthly logs SinkConfig { rotation: Some("monthly".to_string()), retention: Some(12), ..Default::default() } // Unlimited retention (never delete) SinkConfig { rotation: Some("daily".to_string()), retention: None, // Keep all files forever ..Default::default() } }
Rotated File Naming
Files are renamed with timestamps:
app.log -> app_20240101_120000.log
Examples
Hourly Rotation
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/hourly.log")), rotation: Some("hourly".to_string()), retention: Some(24), // Keep 24 hours ..Default::default() } }
Daily Rotation
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/daily.log")), rotation: Some("daily".to_string()), retention: Some(7), // Keep 7 days ..Default::default() } }
Weekly Rotation
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/weekly.log")), rotation: Some("weekly".to_string()), retention: Some(4), // Keep 4 weeks ..Default::default() } }
Monthly Rotation
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/monthly.log")), rotation: Some("monthly".to_string()), retention: Some(12), // Keep 12 months ..Default::default() } }
Yearly Rotation
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/yearly.log")), rotation: Some("yearly".to_string()), retention: Some(5), // Keep 5 years ..Default::default() } }
Size-Based (100MB)
#![allow(unused)] fn main() { SinkConfig { path: Some(PathBuf::from("logs/app.log")), size_limit: Some(100 * 1024 * 1024), retention: Some(10), ..Default::default() } }
Best Practices
- Development: Hourly or daily rotation
- Production: Daily or weekly rotation
- Archives: Monthly or yearly rotation
- High-Volume: Size-based rotation
- Compliance: Long retention periods
Automatic Cleanup
Old files are automatically deleted based on retention policy:
- Sorted by modification time
- Oldest files deleted first
- Happens during rotation
Context Binding
Context binding allows you to attach persistent key-value pairs to all log messages.
Overview
Context fields are automatically included in every log message, making it easy to track requests, users, sessions, and other contextual information.
Basic Usage
Bind Context
#![allow(unused)] fn main() { use logly::prelude::*; let logger = Logger::new(); logger.add_sink(SinkConfig::default())?; // Bind context fields logger.bind("user_id".to_string(), serde_json::json!("12345")); logger.bind("session".to_string(), serde_json::json!("abc-def-ghi")); logger.bind("request_id".to_string(), serde_json::json!("req-001")); // All logs now include these fields logger.info("User logged in".to_string())?; }
Unbind Context
#![allow(unused)] fn main() { // Remove specific field logger.unbind("session"); // Clear all context logger.clear_bindings(); }
Use Cases
Request Tracking
#![allow(unused)] fn main() { fn handle_request(logger: &Logger, request_id: &str) -> Result<(), Box<dyn std::error::Error>> { logger.bind("request_id".to_string(), serde_json::json!(request_id)); logger.info("Processing request".to_string())?; // ... handle request ... logger.success("Request completed".to_string())?; logger.unbind("request_id"); Ok(()) } }
User Context
#![allow(unused)] fn main() { fn user_action(logger: &Logger, user_id: u64, action: &str) -> Result<(), Box<dyn std::error::Error>> { logger.bind("user_id".to_string(), serde_json::json!(user_id)); logger.bind("action".to_string(), serde_json::json!(action)); logger.info(format!("User performed: {}", action))?; logger.clear_bindings(); Ok(()) } }
Application Context
#![allow(unused)] fn main() { // Set at startup logger.bind("app_name".to_string(), serde_json::json!("myapp")); logger.bind("version".to_string(), serde_json::json!("1.0.0")); logger.bind("environment".to_string(), serde_json::json!("production")); // These fields appear in all logs throughout the application }
JSON Output
Context fields are especially useful with JSON logging:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.json = true; logger.configure(config); logger.bind("user_id".to_string(), serde_json::json!("12345")); logger.info("Action performed".to_string())?; }
Output:
{
"timestamp": "2024-01-01T12:00:00.000Z",
"level": "INFO",
"message": "Action performed",
"user_id": "12345"
}
Complex Values
Bind any JSON-serializable value:
#![allow(unused)] fn main() { use serde_json::json; // Objects logger.bind("user".to_string(), json!({ "id": 12345, "name": "John Doe", "role": "admin" })); // Arrays logger.bind("tags".to_string(), json!(["important", "urgent"])); // Numbers logger.bind("retry_count".to_string(), json!(3)); // Booleans logger.bind("is_authenticated".to_string(), json!(true)); }
Thread Safety
Context bindings are thread-safe and can be used across threads:
#![allow(unused)] fn main() { use std::sync::Arc; use std::thread; let logger = Arc::new(Logger::new()); logger.add_sink(SinkConfig::default())?; logger.bind("app".to_string(), serde_json::json!("myapp")); let logger_clone = Arc::clone(&logger); thread::spawn(move || { logger_clone.info("From thread".to_string()).unwrap(); // Still includes "app" context }); }
Best Practices
- Bind early: Set application-wide context at startup
- Unbind when done: Remove request-specific context after handling
- Use meaningful keys: Choose clear, consistent key names
- Avoid sensitive data: Don't bind passwords or tokens
- Keep it simple: Don't overload with too many fields
Example: Web Server
use logly::prelude::*; use std::sync::Arc; fn main() -> Result<(), Box<dyn std::error::Error>> { let logger = Arc::new(Logger::new()); logger.add_sink(SinkConfig::default())?; // Application context logger.bind("app".to_string(), serde_json::json!("web-server")); logger.bind("version".to_string(), serde_json::json!("1.0.0")); logger.info("Server starting".to_string())?; // Simulate request handling handle_request(&logger, "req-001", "user-123")?; Ok(()) } fn handle_request(logger: &Logger, request_id: &str, user_id: &str) -> Result<(), Box<dyn std::error::Error>> { // Request-specific context logger.bind("request_id".to_string(), serde_json::json!(request_id)); logger.bind("user_id".to_string(), serde_json::json!(user_id)); logger.info("Request received".to_string())?; logger.debug("Processing...".to_string())?; logger.success("Request completed".to_string())?; // Clean up request context logger.unbind("request_id"); logger.unbind("user_id"); Ok(()) }
See Also
Callbacks Guide
Overview
Logly provides three types of callbacks for custom behavior:
- Log Callbacks: Monitor log events
- Color Callbacks: Custom color formatting
- Exception Callbacks: Error handling hooks
Log Callbacks
Monitor and react to log events:
#![allow(unused)] fn main() { logger.add_log_callback(|record| { if record.level >= Level::Error { // Send alert, update metrics, etc. println!("High severity: {}", record.message); } Ok(()) }); }
Use Cases
- Metrics collection
- Alert triggering
- Audit logging
- Real-time monitoring
Color Callbacks
Customize color output:
#![allow(unused)] fn main() { logger.add_color_callback(|level, message| { match level { Level::Error => format!("\x1b[91;1m{}\x1b[0m", message), // Bright red bold Level::Warning => format!("\x1b[93m{}\x1b[0m", message), // Yellow Level::Success => format!("\x1b[32;1m{}\x1b[0m", message), // Green bold _ => message.to_string(), } }); }
ANSI Color Codes
30-37: Standard colors90-97: Bright colors1: Bold4: Underline91;1: Bright red bold
Exception Callbacks
Handle errors and exceptions:
#![allow(unused)] fn main() { logger.add_exception_callback(|error, backtrace| { eprintln!("⚠️ EXCEPTION: {}", error); eprintln!("Backtrace:\n{}", backtrace); // Send to error tracking service // Log to file // Trigger alerts }); }
Complete Example
use logly::prelude::*; fn main() -> Result<(), Box<dyn std::error::Error>> { let logger = Logger::new(); // Log callback for monitoring logger.add_log_callback(|record| { if record.level >= Level::Error { println!("[MONITOR] High severity: {}", record.message); } Ok(()) }); // Color callback for custom formatting logger.add_color_callback(|level, message| { let color = match level { Level::Trace => "36", Level::Debug => "34", Level::Info => "37", Level::Success => "32;1", Level::Warning => "33", Level::Error => "31", Level::Fail => "35", Level::Critical => "91;1", }; format!("\x1b[{}m● {}\x1b[0m", color, message) }); // Exception callback logger.add_exception_callback(|error, backtrace| { eprintln!("\n⚠️ EXCEPTION CAUGHT"); eprintln!("Error: {}", error); eprintln!("Backtrace:\n{}", backtrace); }); logger.add_sink(SinkConfig::default())?; logger.info("Info message".to_string())?; logger.error("Error message".to_string())?; logger.success("Success message".to_string())?; Ok(()) }
Clearing Callbacks
#![allow(unused)] fn main() { logger.clear_callbacks(); // Remove all callbacks }
Best Practices
- Keep callbacks fast: Avoid blocking operations
- Handle errors: Return
Ok(())or error string - Thread safety: Callbacks are executed in logging thread
- Performance: Minimize callback overhead
Exception Handling
Comprehensive error handling with backtraces.
Enable Exception Handling
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.enable_exception_handling = true; logger.configure(config); }
Exception Callback
Handle exceptions with custom logic:
#![allow(unused)] fn main() { logger.add_exception_callback(|error, backtrace| { eprintln!("Exception occurred: {}", error); eprintln!("Backtrace:\n{}", backtrace); // Send to monitoring service // alert_team(&error); }); }
Error Types
Logly uses LoglyError enum:
IoError- File I/O errorsConfigError- Configuration errorsSinkError- Sink operation errorsGpuError- GPU-related errorsFormatError- Formatting errors
Example
use logly::prelude::*; fn main() -> Result<(), Box<dyn std::error::Error>> { let logger = Logger::new(); logger.add_exception_callback(|error, backtrace| { eprintln!("Error: {}\n{}", error, backtrace); }); logger.add_sink(SinkConfig::default())?; logger.info("Running".to_string())?; Ok(()) }
See Also
Configuration File Guide (logly.toml)
Overview
Logly supports configuration files for persistent settings. Configuration priority:
- Manual configuration (highest priority) -
logger.configure(config) - Config file (medium priority) -
logly.toml - Default configuration (lowest priority) - Built-in defaults
Automatic Scanning
By default, Logly scans for logly.toml in the project root:
#![allow(unused)] fn main() { let logger = Logger::new(); // Automatically loads logly.toml if it exists }
Disable Automatic Scanning
#![allow(unused)] fn main() { let logger = Logger::new(); logger.disable_config_file_scan(); }
Custom Config File Path
#![allow(unused)] fn main() { let logger = Logger::with_config_file(PathBuf::from("config/custom.toml"))?; }
Configuration File Format
Create logly.toml in your project root:
[logly.configuration]
# Log level (TRACE, DEBUG, INFO, SUCCESS, WARNING, ERROR, FAIL, CRITICAL)
level = "INFO"
[logly.display]
# Global display controls
global_color_display = true
global_console_display = true
global_file_storage = true
# Color settings
color = true
# Display options
console = true
show_time = true
show_module = true
show_function = false
show_filename = false
show_lineno = false
[logly.format]
# Output format
json = false
pretty_json = false
log_compact = false
[logly.sinks]
# Sink management
auto_sink = true
[logly.gpu]
# GPU support (experimental)
enable_gpu = false
gpu_buffer_size = 1048576 # 1MB
[logly.features]
# Features
enable_callbacks = true
enable_exception_handling = true
enable_version_check = true
[logly.debug]
# Debug mode
debug_mode = false
debug_log_file = "logs/logly_debug.log"
Configuration Priority Example
#![allow(unused)] fn main() { // 1. Default: Level::Info let logger = Logger::new(); // 2. Config file overrides default: Level::Debug // logly.toml: level = "DEBUG" // 3. Manual config overrides file: Level::Warning let mut config = LoggerConfig::default(); config.level = Level::Warning; logger.configure(config); }
Duplicate Config Files
If multiple config files exist (logly.toml, Logly.toml, LOGLY.toml):
- Warning is displayed
- First found file is used
- Recommended: Use only one config file
Complete Example
logly.toml
level = "DEBUG"
global_console_display = true
global_file_storage = true
color = true
show_time = true
show_module = true
auto_sink = true
enable_version_check = true
debug_mode = false
main.rs
use logly::prelude::*; fn main() -> Result<(), Box<dyn std::error::Error>> { // Loads logly.toml automatically let logger = Logger::new(); // Config file settings are applied logger.debug("Debug enabled from config file".to_string())?; // Override config file settings let mut config = LoggerConfig::default(); config.level = Level::Warning; logger.configure(config); logger.debug("Not shown (manual override)".to_string())?; logger.warning("Shown".to_string())?; Ok(()) }
Use Cases
Development Config
level = "TRACE"
debug_mode = true
debug_log_file = "logs/debug.log"
Production Config
level = "INFO"
global_console_display = false
global_file_storage = true
debug_mode = false
Testing Config
level = "DEBUG"
global_console_display = true
global_file_storage = false
Best Practices
- Version Control: Add
logly.tomlto.gitignorefor environment-specific configs - Template: Provide
logly.toml.examplein repository - Documentation: Document all config options used
- Validation: Test config file before deployment
- Overrides: Use manual config for runtime changes
Error Handling
File Not Found
#![allow(unused)] fn main() { // Custom path - returns error if not found let logger = Logger::with_config_file(PathBuf::from("missing.toml"))?; }
Invalid TOML
Error: Failed to parse config file: expected a table key, found a newline at line 5
Invalid Values
Error: Invalid log level: INVALID
Performance
- Config file loaded once at initialization
- No performance impact after loading
- Scanning can be disabled for faster startup
- Manual configuration has no file I/O overhead
GPU Support Guide
Overview
Logly provides optional GPU/CUDA acceleration for high-throughput logging scenarios.
Installation
[dependencies]
logly = { version = "0.1.7", features = ["gpu"] }
Requirements:
- CUDA Toolkit installed
- NVIDIA GPU with CUDA support
- Compile with
--features gpu
Basic Usage
use logly::prelude::*; fn main() -> Result<(), Box<dyn std::error::Error>> { let logger = Logger::new(); let mut config = LoggerConfig::default(); config.enable_gpu = true; config.gpu_buffer_size = 2 * 1024 * 1024; // 2MB logger.configure(config); logger.enable_gpu()?; // Check GPU status println!("{}", logger.gpu_info()); // Logs are now accelerated by GPU for i in 0..10000 { logger.info(format!("Message {}", i))?; } Ok(()) }
Configuration
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.enable_gpu = true; config.gpu_buffer_size = 2 * 1024 * 1024; // 2MB buffer logger.configure(config); }
GPU Operations
Enable GPU
#![allow(unused)] fn main() { logger.enable_gpu()?; }
Disable GPU
#![allow(unused)] fn main() { logger.disable_gpu(); }
Check Status
#![allow(unused)] fn main() { let info = logger.gpu_info(); println!("{}", info); }
Graceful Fallback
If GPU is unavailable:
- Logs warning message
- Falls back to CPU logging
- No errors or crashes
- Application continues normally
#![allow(unused)] fn main() { match logger.enable_gpu() { Ok(_) => println!("GPU enabled"), Err(e) => println!("GPU unavailable: {}, using CPU", e), } }
Performance
GPU acceleration is beneficial for:
- High-throughput logging (>10,000 logs/sec)
- Real-time systems
- Data-intensive applications
- Parallel processing workloads
Limitations
- Experimental feature
- Requires CUDA toolkit
- NVIDIA GPUs only
- Additional memory overhead
- Not needed for typical applications
Example Output
GPU Logging: Enabled
Device: CUDA Device 0
Buffer Size: 2097152 bytes
Status: Active
Troubleshooting
GPU Not Available
Error: CUDA device not available
Solution: Install CUDA toolkit and ensure GPU is detected
Compilation Error
Error: cudarc not found
Solution: Compile with --features gpu
Performance Not Improved
Solution: GPU acceleration helps with high-volume logging. For typical applications, CPU logging is sufficient.
Best Practices
- Use for high-throughput: Only enable for >10,000 logs/sec
- Monitor memory: GPU buffer uses device memory
- Test fallback: Ensure application works without GPU
- Profile first: Measure if GPU actually improves performance
Debug Mode
Enable detailed internal logging for troubleshooting.
Enable Debug Mode
#![allow(unused)] fn main() { use logly::prelude::*; let logger = Logger::new(); let mut config = LoggerConfig::default(); config.debug_mode = true; logger.configure(config); }
Debug to File
#![allow(unused)] fn main() { use std::path::PathBuf; let mut config = LoggerConfig::default(); config.debug_mode = true; config.debug_log_file = Some(PathBuf::from("logly_debug.log")); logger.configure(config); }
What Gets Logged
Debug mode logs internal operations:
- Sink initialization
- Configuration changes
- Rotation events
- GPU operations
- Callback execution
- Error handling
Example Output
[LOGLY DEBUG] Initializing logger
[LOGLY DEBUG] Adding sink: console
[LOGLY DEBUG] Configuration updated: level=DEBUG
[LOGLY DEBUG] Rotation triggered: size limit reached
[LOGLY DEBUG] Rotated file: app_20240101_120000.log
Use Cases
Troubleshooting
#![allow(unused)] fn main() { // Enable debug to diagnose issues config.debug_mode = true; config.debug_log_file = Some(PathBuf::from("debug.log")); logger.configure(config); // Your logging code logger.info("Test".to_string())?; // Check debug.log for internal details }
Performance Analysis
#![allow(unused)] fn main() { config.debug_mode = true; logger.configure(config); // Logs timing information for operations }
Development
#![allow(unused)] fn main() { #[cfg(debug_assertions)] { config.debug_mode = true; logger.configure(config); } }
Disable in Production
#![allow(unused)] fn main() { #[cfg(not(debug_assertions))] { config.debug_mode = false; logger.configure(config); } }
See Also
Version Checking
Automatic update notifications for new releases.
Overview
Logly can automatically check for new versions on crates.io and notify you of updates.
Enabled by Default
Version checking is enabled by default with the auto-update-check feature.
Check for Updates
#![allow(unused)] fn main() { use logly::prelude::*; let logger = Logger::new(); if let Ok(Some(msg)) = logger.check_version() { println!("{}", msg); } }
Disable Version Checking
At Compile Time
[dependencies]
logly = { version = "0.0.4", default-features = false, features = ["async", "rotation", "json", "colors"] }
At Runtime
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.enable_version_check = false; logger.configure(config); }
Example Output
New version available: 0.0.5
Current version: 0.0.4
Update with: cargo update logly
See Also
Troubleshooting Guide
Common Issues
Logs Not Appearing
Problem: No log output visible
Solutions:
- Check if logger is enabled:
#![allow(unused)] fn main() { logger.enable(); }
- Verify log level:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.level = Level::Trace; // Lower threshold logger.configure(config); }
- Check global display settings:
#![allow(unused)] fn main() { config.global_console_display = true; config.global_file_storage = true; }
- Verify sinks exist:
#![allow(unused)] fn main() { println!("Sinks: {}", logger.get_sink_count()); println!("IDs: {:?}", logger.list_sinks()); }
File Not Created
Problem: Log file doesn't exist
Solutions:
- Check path is correct
- Verify parent directories exist (Logly creates them automatically)
- Check file permissions
- Verify
global_file_storage = true
Rotation Not Working
Problem: Files not rotating
Solutions:
- Verify rotation configuration:
#![allow(unused)] fn main() { SinkConfig { rotation: Some("daily".to_string()), size_limit: Some(10 * 1024 * 1024), ..Default::default() } }
- Check if size/time threshold is reached
- Ensure retention policy is set
GPU Not Available
Problem: GPU acceleration fails
Solutions:
- Install CUDA toolkit
- Compile with
--features gpu - Check GPU is detected by system
- Use CPU fallback (automatic)
Performance Issues
Problem: Logging is slow
Solutions:
- Enable async writing:
#![allow(unused)] fn main() { SinkConfig { async_write: true, buffer_size: 16384, ..Default::default() } }
- Reduce log level in production:
#![allow(unused)] fn main() { config.level = Level::Info; // Skip DEBUG/TRACE }
- Use file storage instead of console
- Increase buffer sizes
Configuration File Not Loaded
Problem: logly.toml settings ignored
Solutions:
- Verify file is in project root
- Check file name is exactly
logly.toml - Validate TOML syntax
- Check for duplicate config files
- Ensure scanning is enabled (default)
Memory Usage High
Problem: High memory consumption
Solutions:
- Reduce buffer sizes:
#![allow(unused)] fn main() { config.gpu_buffer_size = 512 * 1024; // 512KB }
- Disable GPU if not needed
- Use smaller retention periods
- Enable rotation with size limits
Error Messages
"Sink not found"
Cause: Invalid sink ID
Solution: Use valid ID from add_sink() return value
"Invalid log level"
Cause: Unknown level name Solution: Use: TRACE, DEBUG, INFO, SUCCESS, WARNING, ERROR, FAIL, CRITICAL
"Failed to create file"
Cause: Permission denied or invalid path Solution: Check permissions and path validity
"CUDA device not available"
Cause: No GPU or CUDA not installed Solution: Install CUDA or disable GPU feature
Debug Mode
Enable debug mode to see internal operations:
#![allow(unused)] fn main() { let mut config = LoggerConfig::default(); config.debug_mode = true; config.debug_log_file = Some(PathBuf::from("logly_debug.log")); logger.configure(config); }
Output shows:
- Sink operations
- Configuration changes
- GPU status
- Callback execution
- File operations
Reporting Issues
If you encounter a bug, please report it:
Rust crate: https://github.com/muhammad-fiaz/logly-rs/issues Python package: https://github.com/muhammad-fiaz/logly/issues
Include:
- Rust version
- Logly version
- Operating system
- Minimal reproduction code
- Error messages
- Debug log output
Performance Profiling
Profile logging performance:
#![allow(unused)] fn main() { use std::time::Instant; let start = Instant::now(); for i in 0..10000 { logger.info(format!("Message {}", i))?; } let duration = start.elapsed(); println!("10,000 logs in {:?}", duration); }
Best Practices
- Start simple: Use defaults first
- Enable debug mode: When troubleshooting
- Check examples: Reference working code
- Read docs: Check relevant guide
- Test incrementally: Add features one at a time