Source code for isek.util.logger

from loguru import logger as loguru_logger # Rename to avoid conflict
import sys
from threading import Lock
from typing import Optional, ClassVar, Any # Added Any

[docs] class LoggerManager: """ A singleton manager for configuring the global Loguru logger. This class ensures that logger configuration is applied consistently. Use the `LoggerManager.init()` class method to configure the logger. Access the configured logger via `LoggerManager.get_logger()` or the globally exported `logger` instance from this module. """ _instance: ClassVar[Optional['LoggerManager']] = None _lock: ClassVar[Lock] = Lock() _configured_level: ClassVar[str] = "INFO" # Store current config state _configured_format: ClassVar[Optional[str]] = "{message}" # __new__ only creates the instance if it doesn't exist. # Configuration is now handled by a separate method called from init. def __new__(cls, *args: Any, **kwargs: Any) -> 'LoggerManager': """ Ensures only one instance of LoggerManager is created (Singleton). Actual logger configuration should be done via the `init` class method. """ if cls._instance is None: with cls._lock: # Double-check locking if cls._instance is None: cls._instance = super().__new__(cls) # print("LoggerManager singleton instance created.") # Debug return cls._instance # __init__ is generally not needed for this type of singleton if # state is managed at the class level or configuration is via a dedicated method. # def __init__(self): # pass # No instance-specific initialization needed here @classmethod def _configure_logger(cls, level: str, log_format: Optional[str]) -> None: """ Internal class method to actually configure the Loguru logger. This is called by `init`. :param level: The logging level string (e.g., "INFO", "DEBUG"). :type level: str :param log_format: The Loguru format string. If None, Loguru's default rich format is used. :type log_format: typing.Optional[str] """ with cls._lock: # Protect access to global Loguru configuration try: loguru_logger.remove() # Remove all existing handlers except ValueError: # Raised if no handlers to remove (Loguru <0.6.0) pass sink_args = { "sink": sys.stdout, "level": level.upper(), "colorize": True, "enqueue": True, } if log_format: sink_args["format"] = log_format # For non-debug (INFO level), we want INFO and higher. # For DEBUG level, we want DEBUG and higher (which Loguru does by default). # The filter is only needed if you want to *strictly* log only one level, # which is not the case for standard INFO or DEBUG settings. # If level is INFO, messages of level INFO, WARNING, ERROR, CRITICAL will pass. # If level is DEBUG, messages of level DEBUG, INFO, ... will pass. loguru_logger.add(**sink_args) cls._configured_level = level.upper() cls._configured_format = log_format # print(f"Loguru logger configured. Level: {cls._configured_level}, Format: '{cls._configured_format if cls._configured_format else 'Loguru Default'}'") # Debug
[docs] @classmethod def init(cls, debug: bool = False) -> None: """ Initializes or reconfigures the global Loguru logger. Sets the logging level to "DEBUG" if `debug` is True, otherwise "INFO". Uses Loguru's default rich format if `debug` is True, otherwise uses a minimal "{message}" format. :param debug: If `True`, sets log level to "DEBUG" and uses Loguru's default rich format. If `False`, sets log level to "INFO" and uses "{message}" format. Defaults to `False`. :type debug: bool """ level = "DEBUG" if debug else "INFO" # If debug, use Loguru's default rich format (by passing None for format). # Otherwise, use minimal "{message}". log_format = None if debug else "{message}" # Ensure instance exists, then configure. cls() # This calls __new__ to ensure _instance is created. cls._configure_logger(level=level, log_format=log_format)
[docs] @staticmethod def get_logger(): # No type hint needed for Loguru's dynamic logger object """ Provides access to the globally configured Loguru logger instance. :return: The Loguru logger instance. """ return loguru_logger
# --- Global Logger Instance --- # Initialize logger on module import with default non-debug settings. LoggerManager.init(debug=False) # Provide a globally accessible logger instance from this module. # Users can `from isek.util.logger import logger` logger = LoggerManager.get_logger() # Example Usage (can be removed from the final module) if __name__ == "__main__": print("--- Initial (default) logger config ---") logger.debug("This is a debug message (should not appear).") logger.info("This is an info message (default format).") logger.warning("This is a warning message.") print("\n--- Reconfiguring to DEBUG mode ---") LoggerManager.init(debug=True) logger.debug("This is a debug message (should appear now, rich format).") logger.info("This is an info message (rich format).") print("\n--- Reconfiguring back to non-DEBUG mode ---") LoggerManager.init(debug=False) logger.debug("This is a debug message (should not appear again).") logger.info("This is an info message (minimal format again).") logger.error("This is an error message.")