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 - compatible with logging.FileHandler

Source code in logxide/compat_handlers.py
class FileHandler(StreamHandler):
    """File handler class - compatible with logging.FileHandler"""

    def __init__(self, filename, mode="a", encoding=None, delay=False):
        # Implement basic file handling
        self.baseFilename = filename
        self.mode = mode
        self.encoding = encoding
        self.delay = delay
        # Open file and keep it open for the handler
        self._file = open(filename, mode, encoding=encoding)  # noqa: SIM115
        super().__init__(stream=self._file)

    def close(self):
        """Close the file."""
        if hasattr(self, "_file") and self._file:
            self._file.close()
            self._file = None
        if hasattr(super(), "close"):
            super().close()  # type: ignore[misc]

close()

Close the file.

Source code in logxide/compat_handlers.py
def close(self):
    """Close the file."""
    if hasattr(self, "_file") and self._file:
        self._file.close()
        self._file = None
    if hasattr(super(), "close"):
        super().close()  # type: ignore[misc]

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):
        """
        NO-OP formatter - ALL formatting is now handled in Rust.

        This method should never be called if you're using the pure Rust pipeline.
        For maximum performance, use logxide.register_console_handler() or
        logxide.register_file_handler() instead of Python handlers.

        If this method is called, it means you're using Python handlers for
        compatibility. The real formatting should have been done in Rust already.
        """
        # True NO-OP: Return empty string or just the basic message
        # All real formatting is done in Rust
        if isinstance(record, dict):
            return record.get("msg", "")
        else:
            return getattr(record, "msg", "")  # Just the raw message, no formatting

    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)

NO-OP formatter - ALL formatting is now handled in Rust.

This method should never be called if you're using the pure Rust pipeline. For maximum performance, use logxide.register_console_handler() or logxide.register_file_handler() instead of Python handlers.

If this method is called, it means you're using Python handlers for compatibility. The real formatting should have been done in Rust already.

Source code in logxide/compat_handlers.py
def format(self, record):
    """
    NO-OP formatter - ALL formatting is now handled in Rust.

    This method should never be called if you're using the pure Rust pipeline.
    For maximum performance, use logxide.register_console_handler() or
    logxide.register_file_handler() instead of Python handlers.

    If this method is called, it means you're using Python handlers for
    compatibility. The real formatting should have been done in Rust already.
    """
    # True NO-OP: Return empty string or just the basic message
    # All real formatting is done in Rust
    if isinstance(record, dict):
        return record.get("msg", "")
    else:
        return getattr(record, "msg", "")  # Just the raw message, no formatting

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

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

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

    def handle(self, record):
        self.emit(record)

    def emit(self, record):
        # This method should be overridden by subclasses
        pass

    def handleError(self, record):
        # Default error handling - print to stderr
        import traceback

        if sys.stderr:
            sys.stderr.write("--- Logging error ---\n")
            traceback.print_exc(file=sys.stderr)
            sys.stderr.write("Call stack:\n")
            traceback.print_stack(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"""
        self.formatter = formatter

    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:
            # Default formatting
            if isinstance(record, dict):
                return record.get("msg", str(record))
            else:
                return getattr(record, "msg", str(record))

    def close(self):
        """
        Tidy up any resources used by the handler.

        This version does nothing - it's up to subclasses to implement
        any cleanup operations.
        """
        pass

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

__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"""
    self.handle(record)

close()

Tidy up any resources used by the handler.

This version does nothing - it's up to subclasses to implement any cleanup operations.

Source code in logxide/compat_handlers.py
def close(self):
    """
    Tidy up any resources used by the handler.

    This version does nothing - it's up to subclasses to implement
    any cleanup operations.
    """
    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:
        # Default formatting
        if isinstance(record, dict):
            return record.get("msg", str(record))
        else:
            return getattr(record, "msg", str(record))

setFormatter(formatter)

Set the formatter for this handler

Source code in logxide/compat_handlers.py
def setFormatter(self, formatter):
    """Set the formatter for this handler"""
    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

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 - compatible with logging.NullHandler

Source code in logxide/compat_handlers.py
class NullHandler:
    """A handler that does nothing - compatible with logging.NullHandler"""

    def __init__(self):
        pass

    def handle(self, record):
        pass

    def emit(self, record):
        pass

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

__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"""
    self.handle(record)

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) -> str:
        """Map Python logging levels to Sentry levels."""
        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) -> str:
        """Map Python logging level names to Sentry breadcrumb levels."""
        level_mapping = {
            "CRITICAL": "fatal",
            "ERROR": "error",
            "WARNING": "warning",
            "INFO": "info",
            "DEBUG": "debug",
        }
        return level_mapping.get(level_name.upper(), "info")

    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 - compatible with logging.StreamHandler

Source code in logxide/compat_handlers.py
class StreamHandler(Handler):
    """Stream handler class - compatible with logging.StreamHandler"""

    _shutdown = False
    _lock = threading.RLock()

    def __init__(self, stream=None):
        super().__init__()
        if stream is None:
            stream = sys.stderr
        self._stream = stream

    def _get_stream(self):
        """Get the stream, ensuring it's not closed."""
        if hasattr(self, "_stream"):
            stream = self._stream
            if hasattr(stream, "closed") and not stream.closed:
                return stream
        return None

    @property
    def stream(self):
        return self._get_stream() or sys.stderr

    @stream.setter
    def stream(self, value):
        self._stream = value

    def emit(self, record):
        # Check if we're shutting down
        if self._shutdown:
            return

        try:
            # Handle different record types from LogXide
            if isinstance(record, dict):
                msg = record.get("msg", str(record))
            elif hasattr(record, "msg"):
                msg = str(record.msg)
            elif hasattr(record, "message"):
                msg = str(record.message)
            else:
                msg = str(record)

            # Apply formatter if available
            if self.formatter:
                import contextlib

                with contextlib.suppress(AttributeError, KeyError, TypeError):
                    msg = self.formatter.format(record)

            stream = self.stream
            # Check if stream is closed before writing
            if hasattr(stream, "closed") and stream.closed:
                return

            # Thread-safe write
            with self._lock:
                if not self._shutdown and stream and hasattr(stream, "write"):
                    try:
                        stream.write(msg + self.terminator)
                        self.flush()
                    except (ValueError, OSError):
                        # Stream was closed during operation
                        pass
        except RecursionError:
            raise
        except Exception:
            self.handleError(record)

    def flush(self):
        if self._shutdown:
            return

        with self._lock:
            if self.stream and hasattr(self.stream, "flush"):
                # Check if stream is closed before flushing
                if hasattr(self.stream, "closed") and self.stream.closed:
                    return
                with contextlib.suppress(ValueError, OSError):
                    self.stream.flush()

    def close(self):
        """
        Close the stream.
        """
        with self._lock:
            self._shutdown = True
            self.flush()
            if hasattr(self.stream, "close"):
                with contextlib.suppress(ValueError, OSError):
                    self.stream.close()
        Handler.close(self)

    @classmethod
    def _at_exit_shutdown(cls):
        """Shutdown all handlers at exit."""
        with cls._lock:
            cls._shutdown = True

close()

Close the stream.

Source code in logxide/compat_handlers.py
def close(self):
    """
    Close the stream.
    """
    with self._lock:
        self._shutdown = True
        self.flush()
        if hasattr(self.stream, "close"):
            with contextlib.suppress(ValueError, OSError):
                self.stream.close()
    Handler.close(self)

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 - datefmt: Date format string

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
    - datefmt: Date format string
    """

    # 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")

    # Apply configuration to LogXide's Rust backend
    format_str = kwargs.get("format")
    datefmt = kwargs.get("datefmt")

    # Build kwargs for Rust basicConfig
    rust_kwargs = {}
    if "level" in kwargs:
        rust_kwargs["level"] = kwargs["level"]
    if format_str is not None:
        rust_kwargs["format"] = format_str
    if datefmt is not None:
        rust_kwargs["datefmt"] = datefmt

    # Call Rust basicConfig with processed parameters
    _rust_basicConfig(**rust_kwargs)

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

    # Ensure the root logger has a handler with the specified format
    root_logger = getLogger()  # Get the root logger

    # Import formatter classes
    from .compat_handlers import Formatter, StreamHandler

    if not root_logger.handlers:
        # No handlers exist, create a new one
        handler = StreamHandler()
        root_logger.addHandler(handler)
    else:
        # Use existing handler
        handler = root_logger.handlers[0]

    # Always set/update the formatter if format_str is provided
    if format_str:
        formatter = Formatter(format_str, datefmt)
        handler.setFormatter(formatter)
    elif not getattr(handler, "formatter", None):
        # Set default formatter if none exists
        formatter = Formatter()
        handler.setFormatter(formatter)

    # 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)  # Get the LogXide PyLogger instance
        if uvicorn_logger:
            uvicorn_logger.handlers.clear()  # Clear handlers uvicorn added
            uvicorn_logger.propagate = True  # Ensure messages go to root

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

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}")

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]

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")