Compatibility Overview¶
LogXide is a high-performance logging library with a familiar API inspired by Python's standard logging module. It delivers significant performance improvements through its Rust native core, prioritizing speed over perfect compatibility.
For common use cases, LogXide provides a highly compatible experience. Standard patterns like getLogger(), basicConfig(), dictConfig, and built-in handlers work with minimal or no code changes. However, LogXide is not a drop-in replacement for every stdlib logging scenario. Its Rust core means some advanced patterns, particularly those involving custom Python subclasses or deep monkeypatching, are not supported.
This document provides a high-level compatibility overview. For detailed comparisons against specific logging libraries, see the deep-dive guides below.
Quick Compatibility Summary¶
| Feature | Status | Notes |
|---|---|---|
Basic logging API (getLogger, info, debug, etc.) |
✅ | Familiar stdlib-like API |
basicConfig() |
✅ | Direct mapping to LogXide handlers |
dictConfig() |
✅ | Use logxide.config.dictConfig for Django/FastAPI |
Standard formatters (%-style, {}-style) |
✅ | Processed natively in Rust |
| FileHandler, StreamHandler, RotatingFileHandler | ✅ | Rust-native implementations |
Custom Python formatters (subclassed Formatter) |
❌ | Format strings work; custom format() methods don't |
| Custom Python handlers | ⚠️ | Accepted; a foreign Python handler runs once on the Python side (no fast-path GIL release) |
Subclassing LogRecord or Logger |
❌ | Rust types, not subclassable |
pytest caplog |
⚠️ | Use caplog_logxide fixture instead |
| StringIO capture | ❌ | Use file-based logging for tests |
The GIL and What Actually Runs in Rust¶
LogXide's performance comes from moving the log pipeline into a Rust core. How much of that runs without the GIL depends on the path a record takes:
Standard library logging: Creates a Python LogRecord object for every log call, then recursively bubbles it through all loggers while holding the GIL with threading.RLock().
LogXide fast path: When a record hits no Python filter, no Python handler, and no caller-info field, LogXide extracts the record's fields under the GIL and then releases it for the Rust dispatch. On that path, Rust-native handlers format and write without holding the GIL.
When the GIL is still held: A %-args call (for example logger.info("hi %s", name)) re-acquires the GIL inside emit() to run the % formatting, so args-bearing logs do not fully parallelize yet. Any Python handler or Python filter also runs under the GIL, as does caller-info collection when the format string needs it.
Because of this scoping, do not expect linear producer scaling across threads on current CPython GIL builds: the fast path shares a handler mutex and the sink I/O is serialized, so adding producer threads does not multiply throughput. Free-threaded CPython builds need separate verification.
Any custom logic that overrides standard Python implementations, such as subclassed Formatters with custom format() methods, will not execute natively.
Supported Patterns ✅¶
- Basic Configuration:
logging.basicConfig()maps directly to LogXide - Structural Configuration:
logxide.config.dictConfigtranslates Python dictionary configurations (Django, FastAPI) to native Rust objects - Logger Hierarchy: Dot-delimited logger names (e.g.,
app.db.sql) bubble matching Python's resolution logic - Standard Formatting:
%-style and{}-style placeholders, including{asctime}, map to Rust's Chrono formats - Standard Handlers: StreamHandler, FileHandler, RotatingFileHandler behavior replicated in Rust
- Exception Logging:
exc_info=Truecorrectly fetches and logs stack traces - Third-party Interception:
logxide.intercept_stdlib()captures logs from libraries using standard logging
Unsupported Patterns ❌¶
1. Custom Python Formatters¶
LogXide maps the format pattern string directly into Rust. If you subclass logging.Formatter to mutate records in a custom format(self, record) method, this method will not be called because no pure-Python LogRecord is materialized.
Alternative: Use JSON templates via logxide.HTTPHandler or transform output at the application edge.
2. Custom Python Handlers¶
If you create a custom Python handler (e.g., class MailLog(logging.Handler)), LogXide accepts it via addHandler() and routes it through its Python dispatch path, so its .handle() method runs once with a Python LogRecord. It runs synchronously on the Python side and does not benefit from the fast-path GIL release. As of 0.2.0, a Rust-backed handler (e.g. logxide.FileHandler) attached to one logger is dispatched exactly once and never leaks records to unrelated loggers; earlier releases could double-emit or misroute such records.
3. Standard Library Unit Tests¶
LogXide fails CPython's test_logging.py unit tests. These tests validate locking behavior, internal .handlers array mutability, and .disabled states using memory assertions that conflict with Rust's encapsulated states and RwLocks.
Detailed Comparison Guides¶
For side-by-side comparisons with specific logging libraries, including benchmark data and migration guidance:
| Comparison | Description |
|---|---|
| LogXide vs stdlib | Handler-by-handler performance vs Python's logging module, feature matrix, and migration path for standard use cases |
| LogXide vs Loguru | Architecture differences, performance benchmarks, feature trade-offs, and when to choose each |
| LogXide vs Structlog | Structured logging capabilities, processor pipelines, context binding, and performance comparison |
| LogXide vs Picologging | Rust vs Cython implementation, Python 3.13+ compatibility, and feature ecosystem comparison |
Migration Checklist¶
When migrating an application to LogXide:
- Initialize early: Import and initialize LogXide before framework initialization (Django/Flask/FastAPI)
- Intercept stdlib: Call
logxide.intercept_stdlib()to capture logs from third-party dependencies - Use structural config: Prefer
logxide.config.dictConfigover custom instantiation - Check custom handlers: Verify any custom Python handlers are acceptable. They are accepted via
addHandler()and run once on the Python side (synchronously, without the fast-path GIL release). Rust-backed handlers run once on the Rust path. - Update tests: Replace
caplogwithcaplog_logxideand use file-based logging instead of StringIO
For detailed third-party library compatibility information, see the Third-Party Compatibility Guide.