firo_logger

Crates.iofiro_logger
lib.rsfiro_logger
version1.1.2
created_at2024-10-01 09:28:36.323744+00
updated_at2025-08-15 08:07:18.387033+00
descriptionA high-performance, feature-rich logger for Rust applications with colored output, structured logging, and advanced configuration
homepage
repositoryhttps://github.com/Mightybeast12/firo_logger/
max_upload_size
id1392668
size227,152
(Mightybeast12)

documentation

README

firo_logger

Crates.io Documentation License: MIT

A high-performance, feature-rich logger for Rust applications with colored output, structured logging, file rotation, async logging, and advanced configuration.

Features

  • โœจ Colored console output with customizable colors
  • ๐Ÿ“Š Structured logging with JSON format support
  • ๐Ÿ“ File logging with automatic rotation (size-based and time-based)
  • โšก Async logging for high-performance applications
  • ๐ŸŽฏ Level filtering with module-specific filters
  • ๐Ÿ”’ Thread-safe with minimal overhead
  • ๐Ÿ“ Caller information (file, line, module)
  • ๐Ÿท๏ธ Custom metadata support
  • ๐ŸŒ Environment configuration support
  • ๐Ÿ—๏ธ Builder pattern for easy configuration
  • ๐Ÿ”„ Log rotation with configurable retention
  • ๐Ÿš€ Performance optimized with buffering and batching
  • ๐Ÿ“ˆ Statistics and monitoring built-in
  • ๐Ÿ”ง Backwards compatibility with legacy API

Quick Start

Add this to your Cargo.toml:

[dependencies]
firo_logger = "0.3.0"

Basic Usage

use firo_logger::{init_default, log_info, log_error, log_success};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize the logger with default settings
    init_default()?;

    // Log some messages
    log_info!("Application started");
    log_success!("Configuration loaded successfully");
    log_error!("Failed to connect to database: {}", "Connection timeout");

    Ok(())
}

Advanced Configuration

use firo_logger::{LoggerConfig, LogLevel, OutputFormat, init};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = LoggerConfig::builder()
        .level(LogLevel::Debug)
        .format(OutputFormat::Json)
        .console(true)
        .colors(true)
        .file("app.log")
        .rotate_by_size(10 * 1024 * 1024, 5) // 10MB, keep 5 files
        .async_logging(1000)
        .include_caller(true)
        .include_thread(true)
        .metadata("app", "my-app")
        .metadata("version", "1.0.0")
        .build();

    init(config)?;

    log_info!("Logger initialized with custom configuration");

    Ok(())
}

Configuration Options

Environment Variables

The logger can be configured using environment variables:

  • FIRO_LOG_LEVEL: Set log level (ERROR, WARNING, INFO, SUCCESS, DEBUG)
  • FIRO_LOG_FILE: Set log file path
  • FIRO_LOG_FORMAT: Set output format (text, json, plain)
  • NO_COLOR: Disable colored output
  • FORCE_COLOR: Force colored output even when not in a terminal
use firo_logger::init_from_env;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    init_from_env()?;
    log_info!("Logger configured from environment");
    Ok(())
}

Configuration Builder

use firo_logger::{LoggerConfig, LogLevel, OutputFormat, RotationFrequency};

let config = LoggerConfig::builder()
    // Basic settings
    .level(LogLevel::Info)
    .format(OutputFormat::Text)

    // Console output
    .console(true)
    .colors(true)
    .use_stderr(true) // Use stderr for errors/warnings

    // File output
    .file("logs/app.log")
    .rotate_by_size(50 * 1024 * 1024, 10) // 50MB, keep 10 files
    .rotate_by_time(RotationFrequency::Daily, 7) // Daily rotation, keep 7 days

    // Performance
    .async_logging(1000) // Enable async with 1000 message buffer

    // Metadata and context
    .include_caller(true)
    .include_thread(true)
    .datetime_format("%Y-%m-%d %H:%M:%S%.3f")
    .metadata("service", "api-server")
    .metadata("version", env!("CARGO_PKG_VERSION"))

    // Module-specific filtering
    .module_filter("hyper", LogLevel::Warning)
    .module_filter("my_app::debug", LogLevel::Debug)

    .build();

Logging Macros

Basic Logging

log_error!("Database connection failed: {}", error);
log_warning!("Deprecated API used: {}", api_name);
log_info!("User {} logged in", username);
log_success!("Payment processed: ${}", amount);
log_debug!("Processing request: {:?}", request);

Advanced Logging

Structured Logging with Metadata

use firo_logger::{log_with_metadata, LogLevel};

log_with_metadata!(
    LogLevel::Info,
    "User login",
    "user_id" => "12345",
    "ip_address" => "192.168.1.100",
    "user_agent" => "Mozilla/5.0...",
    "session_id" => session.id()
);

Conditional Logging

use firo_logger::{log_if, LogLevel};

let debug_mode = std::env::var("DEBUG").is_ok();
log_if!(debug_mode, LogLevel::Debug, "Debug mode is enabled");

let should_trace = user.is_admin();
log_if!(should_trace, LogLevel::Info, "Admin user {} performed action", user.name);

Rate-Limited Logging

use firo_logger::{log_rate_limited, LogLevel};
use std::time::Duration;

// In a high-frequency loop
for i in 0..10000 {
    // This will only log once per second maximum
    log_rate_limited!(
        Duration::from_secs(1),
        LogLevel::Info,
        "Processing item {}", i
    );
}

Function Tracing

use firo_logger::trace_function;

fn process_payment(amount: f64, user_id: u64) -> Result<(), PaymentError> {
    trace_function!("process_payment", amount, user_id);

    // Function implementation...
    // Entry and exit will be automatically logged

    Ok(())
}

Performance Timing

use firo_logger::{time_block, LogLevel};

let result = time_block!(LogLevel::Info, "Database query", {
    // Your expensive operation here
    database.complex_query().await
});

Assert with Logging

use firo_logger::log_assert;

let user_id = get_user_id();
log_assert!(user_id > 0, "User ID must be positive, got {}", user_id);

// Debug-only assertions
log_debug_assert!(expensive_invariant_check(), "Invariant violated");

Output Formats

Text Format (Default)

2024-08-14 09:33:45.123 [  ERROR]: Database connection failed: timeout
2024-08-14 09:33:45.124 [WARNING]: Deprecated API endpoint used
2024-08-14 09:33:45.125 [   INFO]: User alice logged in
2024-08-14 09:33:45.126 [SUCCESS]: Payment of $49.99 processed
2024-08-14 09:33:45.127 [  DEBUG]: Request processed in 23ms

JSON Format

{"timestamp":"2024-08-14T09:33:45.123Z","level":"ERROR","message":"Database connection failed: timeout","module":"myapp::db","caller":{"file":"src/db.rs","line":42}}
{"timestamp":"2024-08-14T09:33:45.124Z","level":"INFO","message":"User alice logged in","metadata":{"user_id":"12345","ip":"192.168.1.1"}}

Plain Format

2024-08-14 09:33:45 [ERROR]: Database connection failed: timeout
2024-08-14 09:33:45 [INFO]: User alice logged in

File Rotation

Size-Based Rotation

let config = LoggerConfig::builder()
    .file("app.log")
    .rotate_by_size(10 * 1024 * 1024, 5) // 10MB files, keep 5
    .build();

Generated files:

  • app.log (current)
  • app.log.1628934123 (backup)
  • app.log.1628934100 (backup)
  • etc.

Time-Based Rotation

use firo_logger::RotationFrequency;

let config = LoggerConfig::builder()
    .file("app.log")
    .rotate_by_time(RotationFrequency::Daily, 7) // Daily rotation, keep 7 days
    .build();

Generated files:

  • app.log (current)
  • app.log.2024-08-13 (yesterday)
  • app.log.2024-08-12 (day before)
  • etc.

Async Logging

For high-performance applications, enable async logging:

let config = LoggerConfig::builder()
    .async_logging(10000) // Buffer up to 10,000 messages
    .build();

// Logging calls return immediately, processing happens in background
for i in 0..1000000 {
    log_info!("Processing item {}", i)?; // Very fast, non-blocking
}

Integration with log Crate

Enable the log feature and use firo_logger as a backend:

[dependencies]
firo_logger = { version = "0.3.0", features = ["log"] }
log = "0.4"
use firo_logger::log_integration::init_with_log;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    init_with_log()?;

    // Now you can use the standard log macros
    log::info!("This works with firo_logger!");
    log::error!("Error handling through standard log crate");

    Ok(())
}

Performance

firo_logger is designed for high performance:

  • Async logging: Non-blocking log calls with background processing
  • Buffered I/O: Configurable buffer sizes for file output
  • Lazy formatting: Log messages are only formatted if they pass level filters
  • Zero allocation: Many operations avoid memory allocation
  • Lock-free paths: Optimized for concurrent access

Benchmarks show firo_logger can handle:

  • 1M+ logs/second in async mode
  • Sub-microsecond latency for filtered-out messages
  • Minimal memory overhead

Migration from v0.2.x

The new version is fully backwards compatible. Your existing code will continue to work:

// Old API still works
use firo_logger::legacy::{Logger, LogLevel};

Logger::log(format_args!("Still works!"));
Logger::error(format_args!("Error handling"));

But we recommend migrating to the new API:

// New API
use firo_logger::{init_default, log_info, log_error};

init_default()?;
log_info!("Much better!")?;
log_error!("Improved error handling")?;

Examples

See the examples/ directory for more examples:

# Basic usage
cargo run --example basic_usage

# Advanced features
cargo run --example advanced_features

# Performance testing
cargo run --example performance_test --release

Features Flags

  • colors (default): ANSI color support
  • json (default): JSON output format support
  • async (default): Async logging support
  • log: Integration with the standard log crate
  • syslog: Syslog output support (future feature)
# Minimal build without async support
[dependencies]
firo_logger = { version = "0.3.0", default-features = false, features = ["colors"] }

Development & Release

Local Testing

Before pushing changes, run the comprehensive test suite:

# Run all CI checks locally
./test-local.sh

# Individual checks
cargo fmt -- --check
cargo clippy --all-features -- -D warnings
cargo test --all-features
cargo doc --no-deps --all-features

Automated Release

The project includes an automated release script that handles version bumping, testing, and publishing:

# Interactive mode - asks which increment type
./release.sh

# Direct version increment
./release.sh patch   # 1.0.0 โ†’ 1.0.1 (bug fixes)
./release.sh minor   # 1.0.0 โ†’ 1.1.0 (new features)
./release.sh major   # 1.0.0 โ†’ 2.0.0 (breaking changes)

# Preview changes without executing
./release.sh --dry-run

# Show help
./release.sh --help

What the Release Script Does:

  1. ๐Ÿ” Pre-flight checks:

    • Validates git repository state
    • Ensures on main branch with clean working directory
    • Verifies remote connectivity
    • Checks for existing tags
  2. ๐Ÿงช Quality assurance:

    • Runs comprehensive test suite
    • Validates code formatting and linting
    • Ensures documentation builds
  3. ๐Ÿ“ Version management:

    • Updates Cargo.toml version
    • Updates CHANGELOG.md with release date
    • Creates git commit and annotated tag
  4. ๐Ÿš€ Publishing:

    • Pushes changes and tags to remote
    • Triggers GitHub Actions for automated crates.io publishing

Safety Features:

  • Error recovery: Automatically reverts changes if something fails
  • Confirmation prompts: Asks before making destructive changes
  • Comprehensive validation: Multiple safety checks at each step
  • Dry-run mode: Preview all changes before executing

GitHub Actions CI/CD

The project uses GitHub Actions for continuous integration and deployment:

  • CI Pipeline: Runs on every push/PR with comprehensive testing
  • Release Pipeline: Automatically publishes to crates.io when tags are created
  • Dependency Updates: Weekly automated dependency updates with auto-merge

To set up automated publishing:

  1. Get a crates.io API token from crates.io/settings/tokens
  2. Add it as CARGO_REGISTRY_TOKEN in GitHub repo Settings โ†’ Secrets โ†’ Actions

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Development Workflow:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Run local tests (./test-local.sh)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Changelog

v0.3.0 (2024-08-14)

  • ๐ŸŽ‰ Major rewrite with backwards compatibility
  • โœจ New Features:
    • Structured logging with JSON support
    • File rotation (size and time-based)
    • Async logging for high performance
    • Module-specific log level filtering
    • Advanced macros (rate limiting, timing, tracing)
    • Environment variable configuration
    • Statistics and monitoring
    • Integration with standard log crate
  • ๐Ÿš€ Performance: Up to 10x faster than v0.2.x
  • ๐Ÿ”ง API: New builder pattern for configuration
  • ๐Ÿ“š Documentation: Comprehensive examples and guides

v0.2.1 (Previous)

  • Basic colored console logging
  • Simple file output
  • Basic log levels

Acknowledgments

  • Inspired by popular logging libraries like env_logger, slog, and tracing
  • Built with performance and usability in mind
  • Thanks to the Rust community for feedback and contributions
Commit count: 0

cargo fmt