# src/utils/logger.py """ Общие утилиты логирования для всего проекта. Включает стандартную настройку вывода в консоль и специализированный трекер коллизий физического движка. """ import logging import sys from typing import List class CollisionTracker(logging.Handler): """ Перехватчик логов для отслеживания коллизий физического движка. Накапливает сообщения о неудачном размещении объектов. """ def __init__(self) -> None: """Инициализация трекера с пустым списком проваленных элементов.""" super().__init__() self.failed_items: List[str] = [] def emit(self, record: logging.LogRecord) -> None: """Перехватывает сообщения об ошибках размещения объектов.""" if "Couldn't place object" in record.getMessage(): self.failed_items.append(record.getMessage()) class FlushStreamHandler(logging.StreamHandler): """ Обработчик логов с принудительным сбросом буфера. Гарантирует мгновенный вывод сообщений в консоль. """ def emit(self, record: logging.LogRecord) -> None: """Записывает лог и сразу сбрасывает буфер вывода.""" super().emit(record) self.flush() def setup_logger() -> CollisionTracker: """ Настраивает корневой логгер для вывода в stdout и возвращает трекер коллизий. Отключает лишний спам от сторонних библиотек. Returns: CollisionTracker: Объект для отслеживания ошибок физики. """ root_logger: logging.Logger = logging.getLogger() if root_logger.hasHandlers(): root_logger.handlers.clear() root_logger.setLevel(logging.INFO) tracker: CollisionTracker = CollisionTracker() root_logger.addHandler(tracker) console_handler: FlushStreamHandler = FlushStreamHandler(sys.stdout) console_handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")) root_logger.addHandler(console_handler) # Отключаем спам от библиотеки trimesh logging.getLogger("trimesh").setLevel(logging.ERROR) return tracker