Skip to content

Architecture

LogXide delivers high performance through its native Rust implementation, providing Python applications with fast logging while maintaining a familiar API.

Core Architecture

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Python API    │    │   Rust Core      │    │   I/O Output    │
│                 │    │                  │    │                 │
│ ┌─────────────┐ │    │ ┌──────────────┐ │    │ ┌─────────────┐ │
│ │ PyLogger    │ │───▶│ │ LogRecord    │ │───▶│ │ Files       │ │
│ │ Methods     │ │    │ │ Creation     │ │    │ │ Streams     │ │
│ └─────────────┘ │    │ └──────────────┘ │    │ │ HTTP/OTLP   │ │
│                 │    │                  │    │ └─────────────┘ │
│ ┌─────────────┐ │    │ ┌──────────────┐ │    │                 │
│ │ basicConfig │ │───▶│ │ Direct       │ │    │                 │
│ │ flush()     │ │    │ │ Handler Call │ │    │                 │
│ └─────────────┘ │    │ └──────────────┘ │    │                 │
└─────────────────┘    └──────────────────┘    └─────────────────┘

Message Processing Flow

  1. Python Call → LogXide PyLogger methods via PyO3
  2. Record Creation → Rust LogRecord with full metadata (logger name, level, timestamp, thread info)
  3. Handler Dispatch → Each handler's emit() is called (non-blocking for stream/HTTP/OTLP, synchronous for file handlers)
  4. Output → Formatted messages written to files/streams/HTTP endpoints

Key Components

PyO3 Integration (src/lib.rs, src/py_logger.rs, src/py_handlers.rs)

  • Python bindings exposing Logger, Handler, and Formatter types
  • addHandler() accepts LogXide's Rust native handlers (fast path) and standard Python logging.Handler subclasses (run alongside the Rust pipeline, no zero-GIL path; may cause duplicate processing)

Core Types (src/core.rs)

  • LogRecord — Rust struct holding log metadata (name, level, message, timestamp, thread info, extras)
  • Logger — Core logger with level filtering and handler dispatch
  • LoggerManager — Hierarchical logger registry with parent-child relationships

Fast Logger (src/fast_logger.rs)

  • Lock-free implementation using atomic operations
  • Optimized for high-performance scenarios where mutex contention is a concern
  • Uses AtomicU8 for fast level checking

FastLoggerWrapper (logxide/fast_logger_wrapper.py)

Python-side optimization wrapper that intercepts logging calls before they cross the PyO3 boundary:

Python call → FastLoggerWrapper.info() → level check (Python) → [skip if disabled]
                                                               → [delegate to Rust if enabled]
  • 2-5x speedup for disabled log calls by avoiding:
    • PyObject creation for messages
    • PyTuple/PyDict packaging for args/kwargs
    • PyO3 boundary crossing overhead
  • Caches getEffectiveLevel() on the Python side and invalidates on setLevel()/addHandler()/removeHandler()
  • Transparent delegation: all non-hot-path attributes fall through to the underlying Rust PyLogger via __getattr__

Handlers (src/handler.rs)

All handlers implement the synchronous Handler trait:

pub trait Handler: Send + Sync {
    fn emit(&self, record: &LogRecord);
    fn flush(&self);
}
Handler Description I/O Strategy
StreamHandler stdout/stderr output crossbeam 채널 + 백그라운드 스레드 (논블로킹)
FileHandler File output 동기 직접 write (Mutex<BufWriter>)
RotatingFileHandler Auto-rotating files 동기 직접 write + size-based rotation
HTTPHandler HTTP log shipping crossbeam 채널 + 백그라운드 스레드 (배치)
OTLPHandler OpenTelemetry OTLP crossbeam 채널 + 백그라운드 스레드 (Protobuf)
MemoryHandler In-memory capture 동기 Vec::push (Mutex)
NullHandler Discards all logs Zero overhead

Non-blocking Handlers (Stream/HTTP/OTLP)

StreamHandler, HTTPHandler, OTLPHandler use the channel + background thread pattern:

Logger → emit() → crossbeam-channel sender → Background thread → I/O output
  • emit() formats the message and sends it to a bounded crossbeam-channel (non-blocking)
  • A dedicated background thread performs actual I/O
  • HTTP/OTLP handlers additionally batch records before sending

Synchronous Handlers (File/RotatingFile)

FileHandler, RotatingFileHandler use direct synchronous writes:

Logger → emit() → Mutex<BufWriter<File>> → write + conditional flush
  • emit() acquires a Mutex lock and writes directly to BufWriter
  • Level-based flush: records at ERROR or above trigger immediate flush()
  • Simpler and faster for single-thread-dominant workloads

Formatters (src/formatter.rs)

  • PercentStyle%(name)s format (default)
  • StrFormatStyle{name} format
  • StringTemplateStyle$name / ${name} format
  • Full support for padding, alignment, and date formatting
  • Direct ANSI color support: Support for terminal coloring placeholders (%(ansi_level_color)s, %(ansi_reset_color)s) is natively absorbed into PercentStyle (RustFormatter/Formatter), eliminating the runtime overhead of delegating to a separate ColorFormatter wrapper.

Filters (src/filter.rs)

  • Name-based filtering matching Python's logging.Filter
  • Hierarchical name matching (e.g., "myapp" matches "myapp.database")

String Cache (src/string_cache.rs)

  • Arc<str>-based interning specifically for static logger names and level names.
  • No message-text caching: To avoid memory overhead and allocation degradation, the dynamic log message body (text) is not cached. Dead string caching states and redundant message-text cache checks have been completely removed.
  • Reduces allocation overhead for frequently used static strings.

Handler Architecture

┌─────────────────────┐
│   PyLogger          │
│   (per-logger       │
│    handler list)    │
├─────────────────────┤
│ ┌─────────────────┐ │
│ │ FileHandler     │ │─── Mutex<BufWriter> → 동기 직접 write
│ └─────────────────┘ │
│ ┌─────────────────┐ │
│ │ StreamHandler   │ │─── crossbeam-channel → Background thread → stderr/stdout
│ └─────────────────┘ │
│ ┌─────────────────┐ │
│ │ HTTPHandler     │ │─── crossbeam-channel → Background thread → HTTP batch
│ └─────────────────┘ │
│ ┌─────────────────┐ │
│ │ OTLPHandler     │ │─── crossbeam-channel → Background thread → OTLP Protobuf
│ └─────────────────┘ │
└─────────────────────┘

Each logger maintains its own handler list. When logger.addHandler() is called with a Rust handler, it is stored in the logger's local handler list. Global handlers configured via basicConfig() are also supported.

Thread Safety

  • Mutex-protected handlersparking_lot::Mutex for handler state
  • Thread-safe logger registryDashMap for concurrent logger access
  • Atomic level checksAtomicU8 for fast level filtering without locks
  • OS-level stream writes — stdout/stderr bypass Python's GIL

Memory Management

  • Rust ownership prevents memory leaks
  • Arc-based sharing for handlers and formatters across loggers
  • BufWriter with 64KB buffers reduces syscall overhead for file handlers
  • String interning via Arc<str> for repeated logger/level names

Comparison with Standard Logging

Aspect Python logging LogXide
Implementation Pure Python Native Rust via PyO3
Handler calls Python method dispatch Direct Rust function calls
String formatting Python string operations Rust native formatting
Thread safety Global lock (_lock) Per-handler mutexes
I/O Python file objects Direct OS I/O (bypasses GIL)
Custom handlers Unlimited (Python subclasses) Rust native handlers (fast path); Python subclasses also accepted, run alongside the Rust pipeline
subclassing Full support Not supported

Dependencies

Crate Purpose
pyo3 Python-Rust bindings
chrono Timestamp formatting
parking_lot Fast mutexes
dashmap Concurrent logger map
crossbeam-channel Stream/HTTP/OTLP handler channels
ureq HTTP requests (HTTPHandler)
serde / serde_json JSON serialization
prost / opentelemetry-proto OTLP Protobuf encoding

Format string parsing

The active formatter hot path uses a single-pass O(N) parser; it does not invoke the regex crate for %(field)s field extraction. The regex crate may still appear in Cargo.toml for non-hot-path uses, but it is not part of the per-record formatting path.