Usage Guide¶
Quick Start¶
from logxide import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('myapp')
logger.info('Hello from LogXide!')
Basic Usage¶
LogXide provides a familiar API similar to Python's logging module:
from logxide import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('myapp')
logger.info('Hello from LogXide!')
logger.warning('This is a warning')
logger.error('This is an error')
Handler Usage¶
Using basicConfig (Recommended)¶
from logxide import logging
# Console output (default: stderr)
logging.basicConfig(level=logging.INFO)
# File output
logging.basicConfig(
filename='app.log',
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
Using addHandler¶
LogXide's Rust native handlers give the best throughput, but addHandler() also accepts standard Python logging.Handler subclasses, which run alongside the Rust pipeline:
from logxide import logging, FileHandler, StreamHandler, RotatingFileHandler
logger = logging.getLogger('myapp')
# File handler
handler = FileHandler('app.log')
handler.setLevel(logging.INFO)
logger.addHandler(handler)
# Rotating file handler
rotating = RotatingFileHandler(
'app.log',
maxBytes=10 * 1024 * 1024, # 10MB
backupCount=5
)
logger.addHandler(rotating)
# Stream handler (stdout/stderr)
stream = StreamHandler()
logger.addHandler(stream)
HTTP and OTLP Handlers¶
from logxide import HTTPHandler, OTLPHandler
# HTTP log shipping
http_handler = HTTPHandler(
url="https://logs.example.com",
global_context={"app": "myapp", "env": "production"}
)
# OpenTelemetry OTLP
otlp_handler = OTLPHandler(
url="http://localhost:4318/v1/logs",
service_name="my-service"
)
⚠️ Common Mistakes¶
1. Mixing Python stdlib handlers with Rust handlers¶
from logxide import logging, FileHandler
import logging as stdlib
logger = logging.getLogger('myapp')
# ⚠️ Accepted, but runs alongside the Rust pipeline (no zero-GIL path)
logger.addHandler(stdlib.FileHandler('app.log')) # may cause duplicate processing
# ✅ PREFERRED — Use LogXide handlers for the fast path
logger.addHandler(FileHandler('app.log'))
2. StringIO capture doesn't work¶
# ❌ WRONG — Rust writes directly to OS stdout/stderr
import io
stream = io.StringIO()
handler = logging.StreamHandler(stream) # Won't capture
# ✅ CORRECT — Use file-based testing
import tempfile
with tempfile.NamedTemporaryFile(mode='w+', delete=False, suffix='.log') as f:
logging.basicConfig(filename=f.name, level=logging.INFO, force=True)
logger.info('Test message')
logging.flush()
with open(f.name) as log_file:
assert 'Test message' in log_file.read()
3. pytest caplog — Use caplog_logxide instead¶
# ❌ caplog fixture is not compatible with LogXide
def test_with_caplog(caplog):
... # Won't capture LogXide output
# ✅ Use caplog_logxide fixture
def test_logging(caplog_logxide):
logger = logging.getLogger('test')
logger.info('Test message')
assert 'Test message' in caplog_logxide.text
assert ('test', 20, 'Test message') in caplog_logxide.record_tuples
Advanced Formatting¶
Multi-threaded Format with Padding¶
logging.basicConfig(
format='[%(asctime)s] %(threadName)-10s | %(name)-15s | %(levelname)-8s | %(message)s',
datefmt='%H:%M:%S'
)
JSON-like Structured Logging¶
logging.basicConfig(
format='{"timestamp":"%(asctime)s","level":"%(levelname)s","logger":"%(name)s","message":"%(message)s"}',
datefmt='%Y-%m-%dT%H:%M:%S'
)
Production Format¶
logging.basicConfig(
format='%(asctime)s [%(process)d:%(thread)d] %(levelname)s %(name)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
Thread Support¶
import threading
from logxide import logging
def worker(worker_id):
logging.set_thread_name(f'Worker-{worker_id}')
logger = logging.getLogger(f'worker.{worker_id}')
logger.info(f'Worker {worker_id} starting')
logger.info(f'Worker {worker_id} finished')
logging.basicConfig(
format='%(threadName)-10s | %(name)s | %(message)s'
)
threads = [threading.Thread(target=worker, args=[i]) for i in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()
Supported Format Specifiers¶
| Specifier | Description |
|---|---|
%(asctime)s |
Timestamp |
%(name)s |
Logger name |
%(levelname)s |
Log level (INFO, WARNING, etc.) |
%(levelno)d |
Log level number |
%(message)s |
Log message |
%(thread)d |
Thread ID |
%(threadName)s |
Thread name |
%(process)d |
Process ID |
%(msecs)d |
Milliseconds |
%(pathname)s |
Full pathname (Triggers caller frame introspection) |
%(filename)s |
Filename (Triggers caller frame introspection) |
%(module)s |
Module name (Triggers caller frame introspection) |
%(lineno)d |
Line number (Triggers caller frame introspection) |
%(funcName)s |
Function name (Triggers caller frame introspection) |
Caller-Info Frame Introspection
Using any of the caller-info fields (%(pathname)s, %(filename)s, %(module)s, %(lineno)d, %(funcName)s) requires CPython stack frame inspection.
- Automatic Activation: LogXide dynamically detects these placeholders and enables optimized CPython frame extraction.
- Compatibility Layer: When using standard library formatters via the compat_handlers.py path, caller-info context is automatically enabled and routed to the native backend via the activate_caller_info mechanism.
Tuple and List Serialization
To maintain uniform structured representation, passing Python tuple or list structures inside extra dictionaries or global_context will automatically serialize them as JSON arrays (e.g., (1, 2, 3) becomes [1, 2, 3]) inside native HTTP and structured output pipelines.
Advanced Formatting Features¶
- Padding:
%(levelname)-8s(left-align, 8 chars) - Zero padding:
%(msecs)03d(3 digits with leading zeros) - Custom date format:
datefmt='%Y-%m-%d %H:%M:%S'
Flush Support¶
Ensure all log messages are processed before program exit:
Handler-specific flush behavior
- FileHandler / RotatingFileHandler:
flush()flushes theBufWriterbuffer to disk (synchronous) - StreamHandler:
flush()signals the background thread to drain all queued messages, then waits for completion - HTTPHandler / OTLPHandler:
flush()triggers immediate batch send and waits for completion
Examples¶
Check out the examples/ directory for comprehensive usage examples: