Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

  1. Performance First: Optimized for high-throughput scenarios
  2. Type Safety: Leverage Rust's type system
  3. Zero Cost Abstractions: No runtime overhead
  4. Modular Design: Use only what you need
  5. Python Compatibility: Match Python logly API where possible

Comparison with Python Logly

FeaturePython LoglyLogly-rs
PerformanceFast (Rust backend)Native Rust (faster)
Memory SafetyRuntime checksCompile-time guarantees
Async Support
File Rotation
JSON Logging
Custom Colors
GPU SupportPlanned✓ (optional)

Next Steps

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

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

FeatureDescriptionDefault
asyncAsync logging support
rotationFile rotation support
jsonJSON format support
colorsANSI color support
auto-update-checkVersion checking
gpuGPU/CUDA acceleration

GPU Support

Prerequisites

  1. 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
  1. 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:

  1. Check Documentation: https://muhammad-fiaz.github.io/logly-rs
  2. Search Issues: https://github.com/muhammad-fiaz/logly-rs/issues
  3. Create Issue: https://github.com/muhammad-fiaz/logly-rs/issues/new
  4. 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

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

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 hour
  • daily - Rotate every day
  • weekly - Rotate every week
  • monthly - Rotate every 30 days
  • yearly - 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

  1. If global_console_display = false AND global_file_storage = false:

    • No output anywhere (silent mode)
  2. If global_console_display = true AND global_file_storage = false:

    • Console output only, no file storage
  3. If global_console_display = false AND global_file_storage = true:

    • File storage only, no console output
  4. 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):

OperationTime (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

  1. Profile First: Use benchmarks to identify bottlenecks
  2. Measure Impact: Test changes with criterion
  3. Production Config: Use optimized settings
  4. Monitor: Track performance metrics
  5. Iterate: Continuously optimize based on data

Additional Resources

Log Levels Guide

Standard Log Levels

Logly provides 8 standard log levels with specific priorities:

LevelPriorityMethodUse Case
TRACE5logger.trace()Very detailed debugging information
DEBUG10logger.debug()Debugging information for developers
INFO20logger.info()General informational messages
SUCCESS25logger.success()Successful operation completion
WARNING30logger.warning()Warning messages that need attention
ERROR40logger.error()Error conditions that need fixing
FAIL45logger.fail()Operation failures
CRITICAL50logger.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

  1. TRACE: Use for very detailed debugging, disabled in production
  2. DEBUG: Development debugging, disabled in production
  3. INFO: General information, enabled in production
  4. SUCCESS: Highlight successful operations
  5. WARNING: Potential issues that don't stop execution
  6. ERROR: Errors that need attention but don't crash
  7. FAIL: Operation failures that may need retry
  8. 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:

LevelPriority
TRACE5
DEBUG10
INFO20
SUCCESS25
WARNING30
NOTICE35 (custom)
ERROR40
AUDIT42 (custom)
FAIL45
CRITICAL50

ANSI Color Codes

Common color codes:

  • 30 - Black
  • 31 - Red
  • 32 - Green
  • 33 - Yellow
  • 34 - Blue
  • 35 - Magenta
  • 36 - Cyan
  • 37 - White
  • 90-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:

LevelPriorityDescription
TRACE5Very detailed debugging
DEBUG10Debugging information
INFO20General information
SUCCESS25Success messages
WARNING30Warning messages
ERROR40Error messages
FAIL45Failure messages
CRITICAL50Critical 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

  1. Production: Use INFO or WARNING level
  2. Development: Use DEBUG or TRACE level
  3. Errors: Always log ERROR and above
  4. Performance: Higher levels = better performance
  5. 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

PatternDescriptionExample
YYYY4-digit year2025
YY2-digit year25
MMMMFull month nameOctober
MMMAbbreviated monthOct
MM2-digit month10
ddddFull weekday nameSaturday
dddAbbreviated weekdaySat
DD2-digit day11
HH2-digit hour (24h)13
hh2-digit hour (12h)01
mm2-digit minute45
ss2-digit second32
SSSMilliseconds123
SSSSSSMicroseconds123456
AAM/PMAM
aam/pmam
ZZTimezone (+00:00)+00:00
ZTimezone (+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

  1. Console vs File: Use simpler formats for console, detailed for files
  2. Time Formats:
    • HH:mm:ss for console (quick reference)
    • YYYY-MM-DD HH:mm:ss.SSS for logs (precise timestamps)
    • YYYY-MM-DDTHH:mm:ss for ISO 8601 compatibility
  3. Performance: Simpler formats are faster to process
  4. 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 - Black
  • 31 - Red
  • 32 - Green
  • 33 - Yellow
  • 34 - Blue
  • 35 - Magenta
  • 36 - Cyan
  • 37 - White

Bright Colors

  • 90-97 - Bright variants

Styles

  • 1 - Bold
  • 4 - Underline
  • 0 - 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

  1. Use for high-volume logging: Async is ideal when logging frequently
  2. Configure buffer size: Larger buffers = better performance, more memory
  3. 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

  1. Count-based: Keeps only the N most recent files
  2. Automatic cleanup: Runs during rotation
  3. Sorted by time: Oldest files deleted first
  4. Unlimited option: Set to None for 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

  1. Development: Hourly or daily rotation
  2. Production: Daily or weekly rotation
  3. Archives: Monthly or yearly rotation
  4. High-Volume: Size-based rotation
  5. 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

  1. Bind early: Set application-wide context at startup
  2. Unbind when done: Remove request-specific context after handling
  3. Use meaningful keys: Choose clear, consistent key names
  4. Avoid sensitive data: Don't bind passwords or tokens
  5. 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 colors
  • 90-97: Bright colors
  • 1: Bold
  • 4: Underline
  • 91;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

  1. Keep callbacks fast: Avoid blocking operations
  2. Handle errors: Return Ok(()) or error string
  3. Thread safety: Callbacks are executed in logging thread
  4. 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 errors
  • ConfigError - Configuration errors
  • SinkError - Sink operation errors
  • GpuError - GPU-related errors
  • FormatError - 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:

  1. Manual configuration (highest priority) - logger.configure(config)
  2. Config file (medium priority) - logly.toml
  3. 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

  1. Version Control: Add logly.toml to .gitignore for environment-specific configs
  2. Template: Provide logly.toml.example in repository
  3. Documentation: Document all config options used
  4. Validation: Test config file before deployment
  5. 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

  1. Use for high-throughput: Only enable for >10,000 logs/sec
  2. Monitor memory: GPU buffer uses device memory
  3. Test fallback: Ensure application works without GPU
  4. 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:

  1. Check if logger is enabled:
#![allow(unused)]
fn main() {
logger.enable();
}
  1. Verify log level:
#![allow(unused)]
fn main() {
let mut config = LoggerConfig::default();
config.level = Level::Trace;  // Lower threshold
logger.configure(config);
}
  1. Check global display settings:
#![allow(unused)]
fn main() {
config.global_console_display = true;
config.global_file_storage = true;
}
  1. 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:

  1. Check path is correct
  2. Verify parent directories exist (Logly creates them automatically)
  3. Check file permissions
  4. Verify global_file_storage = true

Rotation Not Working

Problem: Files not rotating

Solutions:

  1. Verify rotation configuration:
#![allow(unused)]
fn main() {
SinkConfig {
    rotation: Some("daily".to_string()),
    size_limit: Some(10 * 1024 * 1024),
    ..Default::default()
}
}
  1. Check if size/time threshold is reached
  2. Ensure retention policy is set

GPU Not Available

Problem: GPU acceleration fails

Solutions:

  1. Install CUDA toolkit
  2. Compile with --features gpu
  3. Check GPU is detected by system
  4. Use CPU fallback (automatic)

Performance Issues

Problem: Logging is slow

Solutions:

  1. Enable async writing:
#![allow(unused)]
fn main() {
SinkConfig {
    async_write: true,
    buffer_size: 16384,
    ..Default::default()
}
}
  1. Reduce log level in production:
#![allow(unused)]
fn main() {
config.level = Level::Info;  // Skip DEBUG/TRACE
}
  1. Use file storage instead of console
  2. Increase buffer sizes

Configuration File Not Loaded

Problem: logly.toml settings ignored

Solutions:

  1. Verify file is in project root
  2. Check file name is exactly logly.toml
  3. Validate TOML syntax
  4. Check for duplicate config files
  5. Ensure scanning is enabled (default)

Memory Usage High

Problem: High memory consumption

Solutions:

  1. Reduce buffer sizes:
#![allow(unused)]
fn main() {
config.gpu_buffer_size = 512 * 1024;  // 512KB
}
  1. Disable GPU if not needed
  2. Use smaller retention periods
  3. 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

  1. Start simple: Use defaults first
  2. Enable debug mode: When troubleshooting
  3. Check examples: Reference working code
  4. Read docs: Check relevant guide
  5. Test incrementally: Add features one at a time