A Python port of the Invisible Internet Project (I2P)
at main 147 lines 4.2 kB view raw
1"""Log and LogManager — I2P logging system wrapping Python's logging module. 2 3Ported from net.i2p.util.Log and net.i2p.util.LogManager. 4""" 5 6import logging 7import threading 8import traceback 9from typing import Dict, List, Optional 10 11 12class Log: 13 """I2P logger with custom priority levels.""" 14 15 DEBUG = 10 16 INFO = 20 17 WARN = 30 18 ERROR = 40 19 CRIT = 50 20 21 STR_DEBUG = "DEBUG" 22 STR_INFO = "INFO" 23 STR_WARN = "WARN" 24 STR_ERROR = "ERROR" 25 STR_CRIT = "CRIT" 26 27 _LEVEL_MAP = { 28 "DEBUG": DEBUG, "INFO": INFO, "WARN": WARN, 29 "ERROR": ERROR, "CRIT": CRIT, "CRITICAL": CRIT, 30 } 31 _STR_MAP = { 32 DEBUG: STR_DEBUG, INFO: STR_INFO, WARN: STR_WARN, 33 ERROR: STR_ERROR, CRIT: STR_CRIT, 34 } 35 36 # Map I2P levels to Python logging levels 37 _PY_LEVEL_MAP = { 38 DEBUG: logging.DEBUG, 39 INFO: logging.INFO, 40 WARN: logging.WARNING, 41 ERROR: logging.ERROR, 42 CRIT: logging.CRITICAL, 43 } 44 45 def __init__(self, name: str, manager: "LogManager") -> None: 46 self._name = name 47 self._manager = manager 48 self._min_priority = Log.DEBUG 49 self._logger = logging.getLogger(f"i2p.{name}") 50 51 @staticmethod 52 def get_level(level_str: str) -> int: 53 return Log._LEVEL_MAP.get(level_str.upper(), Log.DEBUG) 54 55 @staticmethod 56 def to_level_string(level: int) -> str: 57 return Log._STR_MAP.get(level, "DEBUG") 58 59 @property 60 def name(self) -> str: 61 return self._name 62 63 def get_minimum_priority(self) -> int: 64 return self._min_priority 65 66 def set_minimum_priority(self, priority: int) -> None: 67 self._min_priority = priority 68 69 def should_log(self, priority: int) -> bool: 70 return priority >= self._min_priority 71 72 def should_debug(self) -> bool: 73 return self.should_log(Log.DEBUG) 74 75 def should_info(self) -> bool: 76 return self.should_log(Log.INFO) 77 78 def should_warn(self) -> bool: 79 return self.should_log(Log.WARN) 80 81 def should_error(self) -> bool: 82 return self.should_log(Log.ERROR) 83 84 def log(self, priority: int, msg: str, exc: Optional[Exception] = None) -> None: 85 if not self.should_log(priority): 86 return 87 py_level = self._PY_LEVEL_MAP.get(priority, logging.DEBUG) 88 if exc: 89 self._logger.log(py_level, msg, exc_info=exc) 90 else: 91 self._logger.log(py_level, msg) 92 93 def log_always(self, priority: int, msg: str) -> None: 94 py_level = self._PY_LEVEL_MAP.get(priority, logging.DEBUG) 95 self._logger.log(py_level, msg) 96 97 def debug(self, msg: str, exc: Optional[Exception] = None) -> None: 98 self.log(Log.DEBUG, msg, exc) 99 100 def info(self, msg: str, exc: Optional[Exception] = None) -> None: 101 self.log(Log.INFO, msg, exc) 102 103 def warn(self, msg: str, exc: Optional[Exception] = None) -> None: 104 self.log(Log.WARN, msg, exc) 105 106 def error(self, msg: str, exc: Optional[Exception] = None) -> None: 107 self.log(Log.ERROR, msg, exc) 108 109 110class LogManager: 111 """Central log manager — creates and manages Log instances.""" 112 113 def __init__(self) -> None: 114 self._logs: Dict[str, Log] = {} 115 self._lock = threading.Lock() 116 self._default_limit = Log.WARN 117 118 def get_log(self, name_or_cls) -> Log: 119 """Get or create a Log for the given name or class.""" 120 if isinstance(name_or_cls, type): 121 name = name_or_cls.__qualname__ 122 else: 123 name = str(name_or_cls) 124 125 with self._lock: 126 if name not in self._logs: 127 log = Log(name, self) 128 log.set_minimum_priority(self._default_limit) 129 self._logs[name] = log 130 return self._logs[name] 131 132 def get_logs(self) -> List[Log]: 133 with self._lock: 134 return list(self._logs.values()) 135 136 def get_default_limit(self) -> int: 137 return self._default_limit 138 139 def set_default_limit(self, level: int) -> None: 140 self._default_limit = level 141 with self._lock: 142 for log in self._logs.values(): 143 log.set_minimum_priority(level) 144 145 def shutdown(self) -> None: 146 with self._lock: 147 self._logs.clear()