xbbg-log
Zero-GIL logging infrastructure for the xbbg workspace.
Provides a tracing-based setup designed for Rust-Python hybrid libraries where worker threads must never acquire the GIL. Python controls the log level via a single atomic store — no locks, no GIL, no contention.
Why this crate exists
The standard pyo3-log bridge acquires the GIL on every log event to forward to Python's logging module. In xbbg, worker threads poll live service sessions at 10ms intervals — GIL contention from logging caused measurable latency. This crate eliminates that entirely.
Architecture
tracing::debug!("...")
→ AtomicLevelFilter (reads AtomicU8, ~1ns, zero GIL)
→ fmt::layer
→ stderr
- No GIL: The atomic level check is a single
Relaxedload - Simple output path: Output goes directly to stderr; worker threads never touch Python logging
- Python and Rust logging are separate: Rust uses
tracing, Python useslogging— no bridge
Crate structure
xbbg-log/
├── Cargo.toml
└── src/
└── lib.rs AtomicLevelFilter, init(), set_level(), re-exported tracing macros
Usage
From Python
# sets AtomicU8, returns immediately
# back to quiet (default)
From Rust (other workspace crates)
use ;
info!;
debug!;
All workspace crates depend on xbbg-log instead of tracing directly, so the macro re-exports and subscriber setup are centralized.
Developer override
Set RUST_LOG to a simple level to choose the initial Rust log level:
RUST_LOG=debug
Python can still change the level later via xbbg.set_log_level().
Modes
RUST_LOG set? |
Behaviour |
|---|---|
| No (default) | AtomicLevelFilter — Python controls via set_log_level(), default WARN |
| Simple level | Sets the initial atomic level; Python can still update it |
API
| Function | Description |
|---|---|
init() |
Initialize subscriber (call once from PyO3 module init) |
set_level(Level) |
Set global log level (atomic store) |
current_level() → Level |
Get current level (atomic load) |
parse_level(&str) → Option<Level> |
Parse "debug", "warn", "0"–"4", etc. |