"""Log and LogManager — I2P logging system wrapping Python's logging module. Ported from net.i2p.util.Log and net.i2p.util.LogManager. """ import logging import threading import traceback from typing import Dict, List, Optional class Log: """I2P logger with custom priority levels.""" DEBUG = 10 INFO = 20 WARN = 30 ERROR = 40 CRIT = 50 STR_DEBUG = "DEBUG" STR_INFO = "INFO" STR_WARN = "WARN" STR_ERROR = "ERROR" STR_CRIT = "CRIT" _LEVEL_MAP = { "DEBUG": DEBUG, "INFO": INFO, "WARN": WARN, "ERROR": ERROR, "CRIT": CRIT, "CRITICAL": CRIT, } _STR_MAP = { DEBUG: STR_DEBUG, INFO: STR_INFO, WARN: STR_WARN, ERROR: STR_ERROR, CRIT: STR_CRIT, } # Map I2P levels to Python logging levels _PY_LEVEL_MAP = { DEBUG: logging.DEBUG, INFO: logging.INFO, WARN: logging.WARNING, ERROR: logging.ERROR, CRIT: logging.CRITICAL, } def __init__(self, name: str, manager: "LogManager") -> None: self._name = name self._manager = manager self._min_priority = Log.DEBUG self._logger = logging.getLogger(f"i2p.{name}") @staticmethod def get_level(level_str: str) -> int: return Log._LEVEL_MAP.get(level_str.upper(), Log.DEBUG) @staticmethod def to_level_string(level: int) -> str: return Log._STR_MAP.get(level, "DEBUG") @property def name(self) -> str: return self._name def get_minimum_priority(self) -> int: return self._min_priority def set_minimum_priority(self, priority: int) -> None: self._min_priority = priority def should_log(self, priority: int) -> bool: return priority >= self._min_priority def should_debug(self) -> bool: return self.should_log(Log.DEBUG) def should_info(self) -> bool: return self.should_log(Log.INFO) def should_warn(self) -> bool: return self.should_log(Log.WARN) def should_error(self) -> bool: return self.should_log(Log.ERROR) def log(self, priority: int, msg: str, exc: Optional[Exception] = None) -> None: if not self.should_log(priority): return py_level = self._PY_LEVEL_MAP.get(priority, logging.DEBUG) if exc: self._logger.log(py_level, msg, exc_info=exc) else: self._logger.log(py_level, msg) def log_always(self, priority: int, msg: str) -> None: py_level = self._PY_LEVEL_MAP.get(priority, logging.DEBUG) self._logger.log(py_level, msg) def debug(self, msg: str, exc: Optional[Exception] = None) -> None: self.log(Log.DEBUG, msg, exc) def info(self, msg: str, exc: Optional[Exception] = None) -> None: self.log(Log.INFO, msg, exc) def warn(self, msg: str, exc: Optional[Exception] = None) -> None: self.log(Log.WARN, msg, exc) def error(self, msg: str, exc: Optional[Exception] = None) -> None: self.log(Log.ERROR, msg, exc) class LogManager: """Central log manager — creates and manages Log instances.""" def __init__(self) -> None: self._logs: Dict[str, Log] = {} self._lock = threading.Lock() self._default_limit = Log.WARN def get_log(self, name_or_cls) -> Log: """Get or create a Log for the given name or class.""" if isinstance(name_or_cls, type): name = name_or_cls.__qualname__ else: name = str(name_or_cls) with self._lock: if name not in self._logs: log = Log(name, self) log.set_minimum_priority(self._default_limit) self._logs[name] = log return self._logs[name] def get_logs(self) -> List[Log]: with self._lock: return list(self._logs.values()) def get_default_limit(self) -> int: return self._default_limit def set_default_limit(self, level: int) -> None: self._default_limit = level with self._lock: for log in self._logs.values(): log.set_minimum_priority(level) def shutdown(self) -> None: with self._lock: self._logs.clear()