Skip to content

Latest commit

 

History

40 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

X-Log

A lightweight, asynchronous, and thread-safe logging library for Rust. It offloads string formatting and disk I/O to a dedicated background thread to ensure that logging doesn't block your application's main execution path.

Features

  • Asynchronous Logging: Uses a bounded MPSC channel to move work to a background thread.
  • Lazy Formatting: Uses closures to defer string formatting until the background thread is ready, minimizing latency in the caller thread.
  • Log Rotation: Automatically rotates log files when they reach a specified size limit.
  • Thread Safe: Designed for use across multiple threads via a global static sender.
  • Buffered I/O: Uses BufWriter to minimize system calls and improve performance.

Reliability & Coverage

  • Memory Safety: Verified with cargo miri (100% clean).
  • Robustness: Fuzzed with cargo fuzz (100% clean).

Installation

Add this to your Cargo.toml:

[dependencies]
# planned: x-log = "0.7.5"
x-log = { git = "https://github.com/hardglitch/x-log" }

Basic usage

To use the logger, initialize it once at the start of your application.

use x_log::{log, log_init};

fn main() {
    log_init!(); // Always place this in main.rs

    log!("Application started!");
    log!("The answer is {}", 42);

    for i in 0..5 {
        log!("Processing item number: {}", i);
    }
}

log.log

[2026-09-13T13:35:56.6214121Z]: Application started!
[2026-09-13T13:35:56.6214709Z]: The answer is 42
[2026-09-13T13:35:56.6214898Z]: Processing item number: 0
[2026-09-13T13:35:56.6214953Z]: Processing item number: 1
[2026-09-13T13:35:56.6214995Z]: Processing item number: 2
[2026-09-13T13:35:56.6215062Z]: Processing item number: 3
[2026-09-13T13:35:56.6215108Z]: Processing item number: 4

Normal usage

If you want to only set the path and max size.

use x_log::{log_eager, log_init_with};

fn func(answer: &str) {
    // log!("The answer is {answer}"); <-- This won't compile
    // Use `log_eager!` macro to own data in main thread
    log_eager!("The answer is {answer}");
}

fn main() {
    log_init_with!("logs/normal.log", 1000); // Always place this in main.rs
    func("some_str");
}

Advanced usage: Customizing via Builder

If you need more control use the LogBackendBuilder:

use x_log::backend::{LogBackendBuilder};
use x_log::{Log, log};

fn main() {
    let backend = LogBackendBuilder::new()
        .path("logs/advanced.log")
        .max_size(5 * 1024 * 1024)  // 5MB
        .buffer_size(32 * 1024)     // 32KB buffer
        .channel_size(500)          // Queue up to 500 messages
        .build();

    // Always place this in main.rs.
    let _guard = Log::init_with_backend(backend);
    // or log_init_with_backend!(backend);
    
    log!("Application started!");
    log!("The answer is {}", 42);

    for i in 0..5 {
        log!("Processing item number: {}", i);
    }

    // Ensure all logs are flushed to disk.
    // The logger will automatically shut down when `_guard` goes out of scope,
    // or you can call it manually: Log::shutdown();
}

Configuration Details

Feature Description Default
Path Path to log file log.log
Max Size When a file reaches max_size, it is renamed and a new file is created. 10 Mb
Buffer Size Internal buffer size for BufWriter (bytes). 64 KB
Channel Size Number of messages that can be queued before the caller blocks. 1024

Performance Tips

  1. Avoid Heavy Logic in Macros: The log! macro uses a closure. While this prevents formatting unless the logger is active, try to keep the logic inside the format! call simple.
  2. Shutdown Gracefully: Always ensure the Log instance is dropped or that Log::shutdown() is called before your program exits to ensure all buffered messages are flushed to disk.

License

About

My own lightweight, thread-safe logging library

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages