Skip to content

API Reference

logxide

LogXide: High-performance, Rust-powered drop-in replacement for Python's logging module.

LogXide provides a fast, async-capable logging system that maintains full compatibility with Python's standard logging module while delivering superior performance through its Rust backend.

FileHandler

Bases: StreamHandler

File handler class.

Source code in logxide/__init__.py
"__version__",
"__author__",
"__email__",
"__license__",
"__description__",
"__url__",
# Logging levels (for convenience)
"DEBUG",
"INFO",
"WARNING",

Filter

Basic Filter implementation for compatibility

Source code in logxide/__init__.py
# Automatically install logxide when logging module is accessed
if not self._installed:
    _install()
    self._installed = True

Formatter

Enhanced formatter - compatible with logging.Formatter, supports extra fields

Source code in logxide/compat_handlers.py
class Formatter:
    """Enhanced formatter - compatible with logging.Formatter, supports extra fields"""

    def __init__(self, fmt=None, datefmt=None, style="%", validate=True, **kwargs):
        self.fmt = fmt if fmt else "%(message)s"  # Default format if not provided
        self.datefmt = datefmt
        self.style = style
        self.validate = validate
        self._kwargs = kwargs

    def format(self, record):
        """
        Format a log record using the format string.

        This handles both dict records (from Rust) and LogRecord objects.
        """
        # Convert record to dict for formatting
        if isinstance(record, dict):
            record_dict = record.copy()
        else:
            record_dict = record.__dict__ if hasattr(record, "__dict__") else {}

        # Ensure 'message' key exists (some formats use it)
        if "message" not in record_dict:
            record_dict["message"] = record_dict.get("msg", "")

        # Add asctime if not present and format string requires it
        if "asctime" not in record_dict and "%(asctime)" in self.fmt:
            record_dict["asctime"] = self.formatTime(record, self.datefmt)

        # Apply format string
        try:
            # Use Python's % formatting with record dict
            s = self.fmt % record_dict
            return s
        except (KeyError, ValueError, TypeError):
            # Fallback to just the message if formatting fails
            return record_dict.get("msg", str(record))

    def formatTime(self, record, datefmt=None):
        """
        Format the time for a record.

        Args:
            record: LogRecord instance
            datefmt: Date format string (if None, uses default format)

        Returns:
            Formatted time string
        """
        if isinstance(record, dict):
            ct = record.get("created", time.time())
        else:
            ct = getattr(record, "created", time.time())

        if datefmt:
            s = time.strftime(datefmt, time.localtime(ct))
        else:
            t = time.localtime(ct)
            s = time.strftime("%Y-%m-%d %H:%M:%S", t)
            if isinstance(record, dict):
                msecs = record.get("msecs", 0)
            else:
                msecs = getattr(record, "msecs", 0)
            s = f"{s},{int(msecs)}"
        return s

    def formatException(self, ei):
        """
        Format exception information.

        Args:
            ei: Exception info tuple (type, value, traceback)

        Returns:
            Formatted exception string
        """
        import io

        sio = io.StringIO()
        tb = ei[2]
        traceback.print_exception(ei[0], ei[1], tb, None, sio)
        s = sio.getvalue()
        sio.close()
        if s[-1:] == "\n":
            s = s[:-1]
        return s

    def formatStack(self, stack_info):
        """
        Format stack information.

        Args:
            stack_info: Stack info string

        Returns:
            Formatted stack string
        """
        return stack_info

format(record)

Format a log record using the format string.

This handles both dict records (from Rust) and LogRecord objects.

Source code in logxide/compat_handlers.py
def format(self, record):
    """
    Format a log record using the format string.

    This handles both dict records (from Rust) and LogRecord objects.
    """
    # Convert record to dict for formatting
    if isinstance(record, dict):
        record_dict = record.copy()
    else:
        record_dict = record.__dict__ if hasattr(record, "__dict__") else {}

    # Ensure 'message' key exists (some formats use it)
    if "message" not in record_dict:
        record_dict["message"] = record_dict.get("msg", "")

    # Add asctime if not present and format string requires it
    if "asctime" not in record_dict and "%(asctime)" in self.fmt:
        record_dict["asctime"] = self.formatTime(record, self.datefmt)

    # Apply format string
    try:
        # Use Python's % formatting with record dict
        s = self.fmt % record_dict
        return s
    except (KeyError, ValueError, TypeError):
        # Fallback to just the message if formatting fails
        return record_dict.get("msg", str(record))

formatException(ei)

Format exception information.

Parameters:

Name Type Description Default
ei

Exception info tuple (type, value, traceback)

required

Returns:

Type Description

Formatted exception string

Source code in logxide/compat_handlers.py
def formatException(self, ei):
    """
    Format exception information.

    Args:
        ei: Exception info tuple (type, value, traceback)

    Returns:
        Formatted exception string
    """
    import io

    sio = io.StringIO()
    tb = ei[2]
    traceback.print_exception(ei[0], ei[1], tb, None, sio)
    s = sio.getvalue()
    sio.close()
    if s[-1:] == "\n":
        s = s[:-1]
    return s

formatStack(stack_info)

Format stack information.

Parameters:

Name Type Description Default
stack_info

Stack info string

required

Returns:

Type Description

Formatted stack string

Source code in logxide/compat_handlers.py
def formatStack(self, stack_info):
    """
    Format stack information.

    Args:
        stack_info: Stack info string

    Returns:
        Formatted stack string
    """
    return stack_info

formatTime(record, datefmt=None)

Format the time for a record.

Parameters:

Name Type Description Default
record

LogRecord instance

required
datefmt

Date format string (if None, uses default format)

None

Returns:

Type Description

Formatted time string

Source code in logxide/compat_handlers.py
def formatTime(self, record, datefmt=None):
    """
    Format the time for a record.

    Args:
        record: LogRecord instance
        datefmt: Date format string (if None, uses default format)

    Returns:
        Formatted time string
    """
    if isinstance(record, dict):
        ct = record.get("created", time.time())
    else:
        ct = getattr(record, "created", time.time())

    if datefmt:
        s = time.strftime(datefmt, time.localtime(ct))
    else:
        t = time.localtime(ct)
        s = time.strftime("%Y-%m-%d %H:%M:%S", t)
        if isinstance(record, dict):
            msecs = record.get("msecs", 0)
        else:
            msecs = getattr(record, "msecs", 0)
        s = f"{s},{int(msecs)}"
    return s

Handler

Basic handler class - compatible with logging.Handler

Note: This is a compatibility shim. LogXide uses Rust native handlers for actual log processing. Python handlers are not supported for performance reasons.

Source code in logxide/compat_handlers.py
class Handler:
    """Basic handler class - compatible with logging.Handler

    Note: This is a compatibility shim. LogXide uses Rust native handlers
    for actual log processing. Python handlers are not supported for
    performance reasons.
    """

    def __init__(self):
        self.formatter = None
        self.level = NOTSET

    def handle(self, record):
        """Handle a log record - compatibility method only"""
        pass

    def emit(self, record):
        """Emit a log record - must be overridden by subclasses"""
        pass

    def handleError(self, record):
        """Handle errors during emit()"""
        import traceback

        if sys.stderr:
            sys.stderr.write("--- Logging error ---\n")
            traceback.print_exc(file=sys.stderr)
            sys.stderr.write("--- End of logging error ---\n")

    @property
    def terminator(self):
        return "\n"

    def setFormatter(self, formatter):
        """Set the formatter for this handler - not supported in LogXide"""
        self.formatter = formatter
        # Note: Formatter setting is currently ignored by Rust handlers

    def setLevel(self, level):
        """Set the effective level for this handler"""
        self.level = level

    def format(self, record):
        """Format the specified record."""
        if self.formatter:
            return self.formatter.format(record)
        else:
            if isinstance(record, dict):
                return record.get("msg", str(record))
            else:
                return getattr(record, "msg", str(record))

    def close(self):
        """Close the handler - compatibility method only"""
        pass

    def __call__(self, record):
        """Make it callable for logxide compatibility"""
        pass

__call__(record)

Make it callable for logxide compatibility

Source code in logxide/compat_handlers.py
def __call__(self, record):
    """Make it callable for logxide compatibility"""
    pass

close()

Close the handler - compatibility method only

Source code in logxide/compat_handlers.py
def close(self):
    """Close the handler - compatibility method only"""
    pass

emit(record)

Emit a log record - must be overridden by subclasses

Source code in logxide/compat_handlers.py
def emit(self, record):
    """Emit a log record - must be overridden by subclasses"""
    pass

format(record)

Format the specified record.

Source code in logxide/compat_handlers.py
def format(self, record):
    """Format the specified record."""
    if self.formatter:
        return self.formatter.format(record)
    else:
        if isinstance(record, dict):
            return record.get("msg", str(record))
        else:
            return getattr(record, "msg", str(record))

handle(record)

Handle a log record - compatibility method only

Source code in logxide/compat_handlers.py
def handle(self, record):
    """Handle a log record - compatibility method only"""
    pass

handleError(record)

Handle errors during emit()

Source code in logxide/compat_handlers.py
def handleError(self, record):
    """Handle errors during emit()"""
    import traceback

    if sys.stderr:
        sys.stderr.write("--- Logging error ---\n")
        traceback.print_exc(file=sys.stderr)
        sys.stderr.write("--- End of logging error ---\n")

setFormatter(formatter)

Set the formatter for this handler - not supported in LogXide

Source code in logxide/compat_handlers.py
def setFormatter(self, formatter):
    """Set the formatter for this handler - not supported in LogXide"""
    self.formatter = formatter

setLevel(level)

Set the effective level for this handler

Source code in logxide/compat_handlers.py
def setLevel(self, level):
    """Set the effective level for this handler"""
    self.level = level

LogRecord

Basic LogRecord implementation for compatibility

Source code in logxide/__init__.py
self._wrapped = wrapped
self._installed = False
super().__init__("logxide.logging")

LoggerAdapter

Basic LoggerAdapter implementation for compatibility

Source code in logxide/__init__.py
def __dir__(self):
    return dir(self._wrapped)

LoggingManager

Mock logging manager for compatibility

Source code in logxide/compat_handlers.py
class LoggingManager:
    """Mock logging manager for compatibility"""

    def __init__(self):
        self.disable = 0  # SQLAlchemy checks this attribute

NullHandler

A handler that does nothing.

Source code in logxide/__init__.py
# Create wrapped logging module
_logging_module = _LoggingModule(logging)

# Make the logging module available as a submodule
sys.modules[__name__ + ".logging"] = _logging_module

# Replace the logging reference with the wrapped module

PyLogger

High-performance logger implementation.

Source code in logxide/__init__.py
    captureWarnings,
    disable,
    getHandlerByName,
    getHandlerNames,
    getLevelName,
    getLevelNamesMapping,
    getLoggerClass,
    getLogRecordFactory,
    makeLogRecord,
    setLoggerClass,
    setLogRecordFactory,
)
from .compat_handlers import (
    CRITICAL,
    DEBUG,
    ERROR,
    FATAL,
    INFO,
    NOTSET,
    WARN,
    WARNING,
    Formatter,
    Handler,
    LoggingManager,
)
from .compat_handlers import (
    NullHandler as _CompatNullHandler,
)

# Import Rust native handlers from the extension module
FileHandler = logxide.FileHandler
StreamHandler = logxide.StreamHandler
RotatingFileHandler = logxide.RotatingFileHandler
NullHandler = _CompatNullHandler  # Use compat NullHandler for now
from .logger_wrapper import basicConfig, getLogger
from .module_system import _install, logging, uninstall

# Import clear_handlers from Rust extension
clear_handlers = logxide.logging.clear_handlers

# Optional Sentry integration (imported lazily to avoid dependency issues)
try:
    from .sentry_integration import SentryHandler, auto_configure_sentry

    _sentry_available = True
except ImportError:
    _sentry_available = False
    SentryHandler = None
    auto_configure_sentry = None


class _LoggingModule(ModuleType):
    """

SentryHandler

Bases: Handler

A handler that sends log records to Sentry.

Automatically detects if Sentry SDK is available and configured. Only sends events at WARNING level and above to avoid noise.

Source code in logxide/sentry_integration.py
class SentryHandler(Handler):
    """
    A handler that sends log records to Sentry.

    Automatically detects if Sentry SDK is available and configured.
    Only sends events at WARNING level and above to avoid noise.
    """

    def __init__(self, level: int = WARNING, with_breadcrumbs: bool = True):
        """
        Initialize the Sentry handler.

        Args:
            level: Minimum log level to send to Sentry (default: WARNING)
            with_breadcrumbs: Whether to add breadcrumbs for lower-level logs
        """
        super().__init__()
        self.level = level
        self.with_breadcrumbs = with_breadcrumbs
        self._sentry_sdk = None
        self._sentry_available = False

        # Try to import Sentry SDK
        self._init_sentry()

    def _init_sentry(self) -> None:
        """Initialize Sentry SDK if available and configured."""
        try:
            import sentry_sdk

            self._sentry_sdk = sentry_sdk

            # Check if Sentry is actually configured
            hub = sentry_sdk.Hub.current
            if hub.client is not None:
                self._sentry_available = True
            else:
                # Sentry SDK is available but not configured
                self._sentry_available = False

        except ImportError:
            # Sentry SDK is not installed
            self._sentry_available = False

    def emit(self, record) -> None:
        """
        Emit a log record to Sentry.

        Args:
            record: The log record to emit
        """
        if not self._sentry_available:
            return

        try:
            # Extract information from the record
            level_no = getattr(record, "levelno", WARNING)
            level_name = getattr(record, "levelname", "WARNING")
            message = self._get_message(record)
            logger_name = getattr(record, "name", "logxide")

            # Only send events at or above our threshold
            if level_no >= self.level:
                self._send_sentry_event(
                    record, level_no, level_name, message, logger_name
                )
            elif self.with_breadcrumbs and level_no >= WARNING:
                # Add as breadcrumb for context
                self._add_breadcrumb(record, level_name, message, logger_name)

        except Exception as e:
            # Prevent logging errors from causing infinite loops
            self._handle_error(e)

    def _get_message(self, record) -> str:
        """Extract the message from a log record."""
        if hasattr(record, "getMessage"):
            return record.getMessage()
        elif hasattr(record, "msg"):
            return str(record.msg)
        elif hasattr(record, "message"):
            return str(record.message)
        elif isinstance(record, dict):
            return record.get("msg", str(record))
        else:
            return str(record)

    def _send_sentry_event(
        self, record, level_no: int, level_name: str, message: str, logger_name: str
    ) -> None:
        """Send an event to Sentry."""
        if not self._sentry_sdk:
            return

        # Map LogXide levels to Sentry levels
        sentry_level = self._map_level_to_sentry(level_no)

        # Prepare extra context
        extra = self._extract_extra_context(record)

        # Prepare tags
        tags = {
            "logger": logger_name,
            "logxide": True,
        }

        # Add thread and process info if available
        if hasattr(record, "thread"):
            tags["thread"] = str(record.thread)
        if hasattr(record, "process"):
            tags["process"] = str(record.process)

        # Send the event
        with self._sentry_sdk.configure_scope() as scope:
            # Set tags
            for key, value in tags.items():
                scope.set_tag(key, value)

            # Set extra context
            for key, value in extra.items():
                scope.set_extra(key, value)

            # Set level
            scope.level = sentry_level

            # Check if this is an exception record
            if hasattr(record, "exc_info") and record.exc_info:
                # This is an exception - capture it with the exception info
                self._sentry_sdk.capture_exception(error=record.exc_info)
            else:
                # Regular log message - capture as message
                self._sentry_sdk.capture_message(message, level=sentry_level)

    def _add_breadcrumb(
        self, record, level_name: str, message: str, logger_name: str
    ) -> None:
        """Add a breadcrumb to Sentry for context."""
        if not self._sentry_sdk:
            return

        self._sentry_sdk.add_breadcrumb(
            message=message,
            category="log",
            level=self._map_level_to_sentry_breadcrumb(level_name),
            data={
                "logger": logger_name,
                "level": level_name,
            },
        )

    def _map_level_to_sentry(
        self, level_no: int
    ) -> Literal["fatal", "critical", "error", "warning", "info", "debug"]:
        """Map Python logging levels to Sentry levels.

        Returns a Sentry-compatible log level string that matches the
        LogLevelStr type from sentry_sdk.
        """
        if level_no >= CRITICAL:
            return "fatal"
        elif level_no >= ERROR:
            return "error"
        elif level_no >= WARNING:
            return "warning"
        else:
            return "info"

    def _map_level_to_sentry_breadcrumb(
        self, level_name: str
    ) -> Literal["fatal", "critical", "error", "warning", "info", "debug"]:
        """Map Python logging level names to Sentry breadcrumb levels.

        Returns a Sentry-compatible log level string that matches the
        LogLevelStr type from sentry_sdk.
        """
        level_mapping = {
            "CRITICAL": "fatal",
            "ERROR": "error",
            "WARNING": "warning",
            "INFO": "info",
            "DEBUG": "debug",
        }
        result = level_mapping.get(level_name.upper(), "info")
        # Cast is safe because we control the mapping and default value
        return cast(
            Literal["fatal", "critical", "error", "warning", "info", "debug"], result
        )

    def _extract_extra_context(self, record) -> dict[str, Any]:
        """Extract extra context from a log record."""
        extra = {}

        # Standard record attributes
        for attr in ["filename", "lineno", "funcName", "pathname", "module"]:
            if hasattr(record, attr):
                extra[attr] = getattr(record, attr)

        # Thread and process info
        for attr in ["thread", "threadName", "process", "processName"]:
            if hasattr(record, attr):
                extra[attr] = getattr(record, attr)

        # Timestamp
        if hasattr(record, "created"):
            extra["timestamp"] = record.created

        # Any additional attributes that aren't standard
        if hasattr(record, "__dict__"):
            record_dict = record.__dict__
            standard_attrs = {
                "name",
                "msg",
                "args",
                "levelname",
                "levelno",
                "pathname",
                "filename",
                "module",
                "exc_info",
                "exc_text",
                "stack_info",
                "lineno",
                "funcName",
                "created",
                "msecs",
                "relativeCreated",
                "thread",
                "threadName",
                "processName",
                "process",
                "getMessage",
            }

            for key, value in record_dict.items():
                if key not in standard_attrs and not key.startswith("_"):
                    try:
                        # Only include JSON-serializable values
                        import json

                        json.dumps(value)  # Test if serializable
                        extra[f"custom_{key}"] = value
                    except (TypeError, ValueError):
                        # Skip non-serializable values
                        pass

        return extra

    def _handle_error(self, error: Exception) -> None:
        """Handle errors that occur during Sentry emission."""
        # Write to stderr to avoid logging loops
        if sys.stderr:
            sys.stderr.write(f"SentryHandler error: {error}\n")

    @property
    def is_available(self) -> bool:
        """Check if Sentry is available and configured."""
        return self._sentry_available

    def __call__(self, record) -> None:
        """Make the handler callable for LogXide compatibility."""
        self.handle(record)

is_available property

Check if Sentry is available and configured.

__call__(record)

Make the handler callable for LogXide compatibility.

Source code in logxide/sentry_integration.py
def __call__(self, record) -> None:
    """Make the handler callable for LogXide compatibility."""
    self.handle(record)

__init__(level=WARNING, with_breadcrumbs=True)

Initialize the Sentry handler.

Parameters:

Name Type Description Default
level int

Minimum log level to send to Sentry (default: WARNING)

WARNING
with_breadcrumbs bool

Whether to add breadcrumbs for lower-level logs

True
Source code in logxide/sentry_integration.py
def __init__(self, level: int = WARNING, with_breadcrumbs: bool = True):
    """
    Initialize the Sentry handler.

    Args:
        level: Minimum log level to send to Sentry (default: WARNING)
        with_breadcrumbs: Whether to add breadcrumbs for lower-level logs
    """
    super().__init__()
    self.level = level
    self.with_breadcrumbs = with_breadcrumbs
    self._sentry_sdk = None
    self._sentry_available = False

    # Try to import Sentry SDK
    self._init_sentry()

emit(record)

Emit a log record to Sentry.

Parameters:

Name Type Description Default
record

The log record to emit

required
Source code in logxide/sentry_integration.py
def emit(self, record) -> None:
    """
    Emit a log record to Sentry.

    Args:
        record: The log record to emit
    """
    if not self._sentry_available:
        return

    try:
        # Extract information from the record
        level_no = getattr(record, "levelno", WARNING)
        level_name = getattr(record, "levelname", "WARNING")
        message = self._get_message(record)
        logger_name = getattr(record, "name", "logxide")

        # Only send events at or above our threshold
        if level_no >= self.level:
            self._send_sentry_event(
                record, level_no, level_name, message, logger_name
            )
        elif self.with_breadcrumbs and level_no >= WARNING:
            # Add as breadcrumb for context
            self._add_breadcrumb(record, level_name, message, logger_name)

    except Exception as e:
        # Prevent logging errors from causing infinite loops
        self._handle_error(e)

StreamHandler

Bases: Handler

Stream handler class.

Source code in logxide/__init__.py
__all__ = [
    # Core functionality
    "logging",
    "uninstall",
    "LoggingManager",

addLevelName(level, levelName)

Add a level name - compatibility function

Source code in logxide/compat_functions.py
def addLevelName(level, levelName):
    """Add a level name - compatibility function"""
    global _levelToName, _nameToLevel
    _levelToName[level] = levelName
    _nameToLevel[levelName.upper()] = level

auto_configure_sentry(enable=None)

Automatically configure Sentry integration if available.

Parameters:

Name Type Description Default
enable Optional[bool]

Explicitly enable/disable Sentry (None for auto-detect)

None

Returns:

Type Description
Optional[SentryHandler]

SentryHandler instance if configured, None otherwise

Source code in logxide/sentry_integration.py
def auto_configure_sentry(enable: Optional[bool] = None) -> Optional[SentryHandler]:
    """
    Automatically configure Sentry integration if available.

    Args:
        enable: Explicitly enable/disable Sentry (None for auto-detect)

    Returns:
        SentryHandler instance if configured, None otherwise
    """
    if enable is False:
        return None

    try:
        import sentry_sdk

        # Check if Sentry is configured
        hub = sentry_sdk.Hub.current
        if hub.client is None and enable is not True:
            # Sentry SDK available but not configured, and not explicitly enabled
            return None

        # Create and return handler
        handler = SentryHandler()

        if handler.is_available or enable is True:
            return handler
        else:
            return None

    except ImportError:
        if enable is True:
            # Explicitly requested but not available
            import warnings

            warnings.warn(
                "Sentry integration requested but sentry-sdk is not installed. "
                "Install with: pip install logxide[sentry]",
                UserWarning,
                stacklevel=2,
            )
        return None

basicConfig(**kwargs)

Basic configuration for the logging system.

Supported parameters: - level: Set the effective level for the root logger - format: Format string for log messages (not implemented in Rust handlers) - datefmt: Date format string (not implemented in Rust handlers) - stream: Stream to write log output to (sys.stdout or sys.stderr supported) - filename: Log to a file instead of a stream - force: If True, remove any existing handlers and reconfigure (default: False)

Note: LogXide uses Rust native handlers for performance. All handler configuration is done through this function. Direct handler registration via addHandler() is not supported.

Like Python's standard logging, basicConfig() will do nothing if the root logger already has handlers configured, unless force=True is specified.

Source code in logxide/logger_wrapper.py
def basicConfig(**kwargs):
    """
    Basic configuration for the logging system.

    Supported parameters:
    - level: Set the effective level for the root logger
    - format: Format string for log messages (not implemented in Rust handlers)
    - datefmt: Date format string (not implemented in Rust handlers)
    - stream: Stream to write log output to (sys.stdout or sys.stderr supported)
    - filename: Log to a file instead of a stream
    - force: If True, remove any existing handlers and reconfigure (default: False)

    Note: LogXide uses Rust native handlers for performance. All handler
    configuration is done through this function. Direct handler registration
    via addHandler() is not supported.

    Like Python's standard logging, basicConfig() will do nothing if the root
    logger already has handlers configured, unless force=True is specified.
    """
    import sys

    # Import logxide at the top of the function
    from . import logxide as logxide_module

    global _basic_config_called

    # Check if already configured (unless force=True)
    force = kwargs.get("force", False)
    if _basic_config_called and not force:
        return

    # If force=True, clear existing handlers
    if force and _basic_config_called:
        with contextlib.suppress(ImportError, AttributeError):
            logxide_module.logging.clear_handlers()

    _basic_config_called = True

    # Store configuration for applying to new loggers
    _current_config["level"] = kwargs.get("level")
    _current_config["format"] = kwargs.get("format")
    _current_config["datefmt"] = kwargs.get("datefmt")

    # Get configuration parameters
    level = kwargs.get("level", 10)  # Default to DEBUG (10)
    stream = kwargs.get("stream")
    filename = kwargs.get("filename")

    # Register appropriate Rust native handler
    if filename:
        # File handler
        logxide_module.logging.register_file_handler(filename, level)
    else:
        # Stream handler (stdout, stderr, or Python object like StringIO)
        if stream is None:
            # Default to stderr
            logxide_module.logging.register_stream_handler("stderr", level)
        elif stream is sys.stdout:
            logxide_module.logging.register_stream_handler("stdout", level)
        elif stream is sys.stderr:
            logxide_module.logging.register_stream_handler("stderr", level)
        else:
            # Python file-like object (StringIO, file, etc.)
            # Pass the Python object directly to Rust
            logxide_module.logging.register_stream_handler(stream, level)

    # Set root logger level
    root_logger = getLogger()
    if hasattr(root_logger, "setLevel"):
        root_logger.setLevel(level)

    # Now handle existing Python loggers that were created before LogXide
    _migrate_existing_loggers()

    # Explicitly reconfigure uvicorn loggers to ensure they propagate to LogXide's root
    # This is a targeted fix for uvicorn's aggressive logging setup.
    for logger_name in ["uvicorn", "uvicorn.error", "uvicorn.access"]:
        uvicorn_logger = getLogger(logger_name)
        if uvicorn_logger:
            with contextlib.suppress(AttributeError):
                uvicorn_logger.handlers.clear()
                uvicorn_logger.propagate = True

captureWarnings(capture)

If capture is true, redirect all warnings to the logging package. If capture is False, ensure that warnings are not redirected to logging but to their original destinations.

This function maintains full compatibility with Python's logging.captureWarnings.

Source code in logxide/compat_functions.py
def captureWarnings(capture):
    """
    If capture is true, redirect all warnings to the logging package.
    If capture is False, ensure that warnings are not redirected to logging
    but to their original destinations.

    This function maintains full compatibility with Python's logging.captureWarnings.
    """
    global _warnings_showwarning

    if capture:
        if _warnings_showwarning is None:
            _warnings_showwarning = warnings.showwarning

        def showwarning(message, category, filename, lineno, file=None, line=None):
            """
            Implementation of showwarning which redirects to logging.
            """
            from . import logging

            if file is not None:
                if _warnings_showwarning is not None:
                    _warnings_showwarning(
                        message, category, filename, lineno, file, line
                    )
            else:
                s = warnings.formatwarning(message, category, filename, lineno, line)
                logger = logging.getLogger("py.warnings")
                logger.warning("%s", s)

        warnings.showwarning = showwarning
    else:
        if _warnings_showwarning is not None:
            warnings.showwarning = _warnings_showwarning
            _warnings_showwarning = None

disable(level)

Disable logging below the specified level - compatibility function

Source code in logxide/compat_functions.py
def disable(level):
    """Disable logging below the specified level - compatibility function"""
    # For compatibility - not fully implemented
    pass

getHandlerByName(name)

Get a handler by its name.

Parameters:

Name Type Description Default
name str

The name of the handler to retrieve

required

Returns:

Type Description
Any

The handler with the given name, or None if not found

Source code in logxide/compat_functions.py
def getHandlerByName(name):
    """
    Get a handler by its name.

    Args:
        name: The name of the handler to retrieve

    Returns:
        The handler with the given name, or None if not found
    """
    return _handlers.get(name)

getHandlerNames()

Return a list of all registered handler names.

Returns:

Name Type Description
list list

A list of handler names

Source code in logxide/compat_functions.py
def getHandlerNames():
    """
    Return a list of all registered handler names.

    Returns:
        list: A list of handler names
    """
    return list(_handlers.keys())

getLevelName(level)

Get level name - compatibility function

Source code in logxide/compat_functions.py
def getLevelName(level):
    """Get level name - compatibility function"""
    global _levelToName, _nameToLevel

    # If it's a string, return the corresponding level number
    if isinstance(level, str):
        return _nameToLevel.get(level.upper(), f"Level {level}")

    # If it's a number, return the corresponding level name
    return _levelToName.get(level, f"Level {level}")

getLevelNamesMapping()

Return a mapping of level names to level numbers.

This function returns a copy of the internal mapping used for level name to number conversions.

Returns:

Name Type Description
dict dict[str, int]

A dictionary mapping level names to level numbers

Source code in logxide/compat_functions.py
def getLevelNamesMapping():
    """
    Return a mapping of level names to level numbers.

    This function returns a copy of the internal mapping used for
    level name to number conversions.

    Returns:
        dict: A dictionary mapping level names to level numbers
    """
    global _nameToLevel
    return _nameToLevel.copy()

getLogRecordFactory()

Return the factory to be used for creating log records.

Returns:

Type Description
type[LogRecord]

The current log record factory function, or None if using default

Source code in logxide/compat_functions.py
def getLogRecordFactory():
    """
    Return the factory to be used for creating log records.

    Returns:
        The current log record factory function, or None if using default
    """
    return _logRecordFactory

getLogger(name=None)

Get a logger by name, ensuring existing loggers get LogXide functionality.

This wraps the Rust getLogger to handle cases where Python libraries created loggers before LogXide was configured.

Source code in logxide/logger_wrapper.py
def getLogger(name=None):
    """
    Get a logger by name, ensuring existing loggers get LogXide functionality.

    This wraps the Rust getLogger to handle cases where Python libraries
    created loggers before LogXide was configured.
    """
    # Get the LogXide logger
    logger = _rust_getLogger(name)

    # Ensure any retrieved logger propagates to the root and has no other handlers
    # logger.handlers.clear() # Handlers are managed by the Rust side now
    # logger.propagate = True # Propagate is handled by Rust side now

    # Apply the current configuration level if available
    if _current_config["level"] is not None:
        with contextlib.suppress(AttributeError):
            logger.setLevel(_current_config["level"])

    # Set parent for non-root loggers
    if name and "." in name:
        parent_name = name.rsplit(".", 1)[0]
        parent_logger = getLogger(parent_name)
        with contextlib.suppress(AttributeError):
            logger.parent = parent_logger
    elif name and name != "root":
        with contextlib.suppress(AttributeError):
            logger.parent = getLogger("root")

    return logger

getLoggerClass()

Get the logger class - compatibility function

Source code in logxide/compat_functions.py
def getLoggerClass():
    """Get the logger class - compatibility function"""
    # Import here to avoid circular imports
    try:
        from . import logxide

        return logxide.logging.PyLogger
    except ImportError:
        return object  # type: ignore[return-value]

makeLogRecord(dict_)

Make a LogRecord whose attributes are defined by the specified dictionary.

This function is useful for converting a logging event received over a socket connection (which is sent as a dictionary) into a LogRecord instance.

Parameters:

Name Type Description Default
dict_

Dictionary containing log record attributes

required

Returns:

Type Description
LogRecord

A LogRecord-like object (or dict for LogXide compatibility)

Source code in logxide/compat_functions.py
def makeLogRecord(dict_):
    """
    Make a LogRecord whose attributes are defined by the specified dictionary.

    This function is useful for converting a logging event received over
    a socket connection (which is sent as a dictionary) into a LogRecord
    instance.

    Args:
        dict_: Dictionary containing log record attributes

    Returns:
        A LogRecord-like object (or dict for LogXide compatibility)
    """

    # For LogXide, we can return the dictionary itself or create a simple object
    # that has the required attributes
    class LogRecordCompat:
        def __init__(self, d):
            self.__dict__.update(d)

    return LogRecordCompat(dict_)

setLogRecordFactory(factory)

Set the factory to be used for creating log records.

Parameters:

Name Type Description Default
factory type[LogRecord]

A callable that creates LogRecord instances

required
Source code in logxide/compat_functions.py
def setLogRecordFactory(factory):
    """
    Set the factory to be used for creating log records.

    Args:
        factory: A callable that creates LogRecord instances
    """
    global _logRecordFactory
    _logRecordFactory = factory

setLoggerClass(klass)

Set the logger class - compatibility function

Source code in logxide/compat_functions.py
def setLoggerClass(klass):
    """Set the logger class - compatibility function"""
    # For compatibility - not implemented
    pass

uninstall()

Restore the standard logging module.

This undoes the monkey-patching done by _install().

Source code in logxide/module_system.py
def uninstall():
    """
    Restore the standard logging module.

    This undoes the monkey-patching done by _install().
    """
    import logging as std_logging

    # Restore original getLogger if it exists
    if hasattr(std_logging, "_original_getLogger"):
        std_logging.getLogger = std_logging._original_getLogger  # type: ignore[attr-defined]
        delattr(std_logging, "_original_getLogger")

    # Restore original basicConfig if it exists
    if hasattr(std_logging, "_original_basicConfig"):
        std_logging.basicConfig = std_logging._original_basicConfig  # type: ignore[attr-defined]
        delattr(std_logging, "_original_basicConfig")