backup worked
This commit is contained in:
@@ -1,88 +1,205 @@
|
||||
# [MODULE] Superset Tool Logger Utility
|
||||
# PURPOSE: Предоставляет стандартизированный класс-обертку `SupersetLogger` для настройки и использования логирования в проекте.
|
||||
# COHERENCE: Модуль согласован со стандартной библиотекой `logging`, расширяя ее для нужд проекта.
|
||||
# [MODULE_PATH] superset_tool.utils.logger
|
||||
# [FILE] logger.py
|
||||
# [SEMANTICS] logging, utils, ai‑friendly, infrastructure
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# [IMPORTS]
|
||||
# --------------------------------------------------------------
|
||||
import logging
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Optional, Any, Mapping
|
||||
# [END_IMPORTS]
|
||||
|
||||
# CONTRACT:
|
||||
# PURPOSE: Обеспечивает унифицированную настройку логгера с выводом в консоль и/или файл.
|
||||
# PRECONDITIONS:
|
||||
# - `name` должен быть строкой.
|
||||
# - `level` должен быть валидным уровнем логирования (например, `logging.INFO`).
|
||||
# POSTCONDITIONS:
|
||||
# - Создает и настраивает логгер с указанным именем и уровнем.
|
||||
# - Добавляет обработчики для вывода в файл (если указан `log_dir`) и в консоль (если `console=True`).
|
||||
# - Очищает все предыдущие обработчики для данного логгера, чтобы избежать дублирования.
|
||||
# PARAMETERS:
|
||||
# - name: str - Имя логгера.
|
||||
# - log_dir: Optional[Path] - Директория для сохранения лог-файлов.
|
||||
# - level: int - Уровень логирования.
|
||||
# - console: bool - Флаг для включения вывода в консоль.
|
||||
# --------------------------------------------------------------
|
||||
# [ENTITY: Service('SupersetLogger')]
|
||||
# --------------------------------------------------------------
|
||||
"""
|
||||
:purpose: Универсальная обёртка над ``logging.Logger``. Позволяет:
|
||||
• задавать уровень и вывод в консоль/файл,
|
||||
• передавать произвольные ``extra``‑поля,
|
||||
• использовать привычный API (info, debug, warning, error,
|
||||
critical, exception) без «падения» при неверных аргументах.
|
||||
:preconditions:
|
||||
- ``name`` – строка‑идентификатор логгера,
|
||||
- ``level`` – валидный уровень из ``logging``,
|
||||
- ``log_dir`` – при указании директория, куда будет писаться файл‑лог.
|
||||
:postconditions:
|
||||
- Создан полностью сконфигурированный ``logging.Logger`` без
|
||||
дублирующих обработчиков.
|
||||
"""
|
||||
class SupersetLogger:
|
||||
"""
|
||||
:ivar logging.Logger logger: Внутренний стандартный логгер.
|
||||
:ivar bool propagate: Отключаем наследование записей, чтобы
|
||||
сообщения не «проваливались» выше.
|
||||
"""
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# [ENTITY: Method('__init__')]
|
||||
# --------------------------------------------------------------
|
||||
"""
|
||||
:purpose: Конфигурировать базовый логгер, добавить обработчики
|
||||
консоли и/или файла, очистить прежние обработчики.
|
||||
:preconditions: Параметры валидны.
|
||||
:postconditions: ``self.logger`` готов к использованию.
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "superset_tool",
|
||||
log_dir: Optional[Path] = None,
|
||||
level: int = logging.INFO,
|
||||
console: bool = True
|
||||
):
|
||||
console: bool = True,
|
||||
) -> None:
|
||||
self.logger = logging.getLogger(name)
|
||||
self.logger.setLevel(level)
|
||||
self.logger.propagate = False # ← не «прокидываем» записи выше
|
||||
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
|
||||
|
||||
# [ANCHOR] HANDLER_RESET
|
||||
# Очищаем существующие обработчики, чтобы избежать дублирования вывода при повторной инициализации.
|
||||
# ---- Очистка предыдущих обработчиков (важно при повторных инициализациях) ----
|
||||
if self.logger.hasHandlers():
|
||||
self.logger.handlers.clear()
|
||||
|
||||
# [ANCHOR] FILE_HANDLER
|
||||
# ---- Файловый обработчик (если указана директория) ----
|
||||
if log_dir:
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
timestamp = datetime.now().strftime("%Y%m%d")
|
||||
file_handler = logging.FileHandler(
|
||||
log_dir / f"{name}_{timestamp}.log", encoding='utf-8'
|
||||
log_dir / f"{name}_{timestamp}.log", encoding="utf-8"
|
||||
)
|
||||
file_handler.setFormatter(formatter)
|
||||
self.logger.addHandler(file_handler)
|
||||
|
||||
# [ANCHOR] CONSOLE_HANDLER
|
||||
# ---- Консольный обработчик ----
|
||||
if console:
|
||||
console_handler = logging.StreamHandler(sys.stdout)
|
||||
console_handler.setFormatter(formatter)
|
||||
self.logger.addHandler(console_handler)
|
||||
|
||||
# CONTRACT:
|
||||
# PURPOSE: (HELPER) Генерирует строку с текущей датой для имени лог-файла.
|
||||
# RETURN: str - Отформатированная дата (YYYYMMDD).
|
||||
def _get_timestamp(self) -> str:
|
||||
return datetime.now().strftime("%Y%m%d")
|
||||
# END_FUNCTION__get_timestamp
|
||||
# [END_ENTITY]
|
||||
|
||||
# [INTERFACE] Методы логирования
|
||||
def info(self, message: str, extra: Optional[dict] = None, exc_info: bool = False):
|
||||
self.logger.info(message, extra=extra, exc_info=exc_info)
|
||||
# --------------------------------------------------------------
|
||||
# [ENTITY: Method('_log')]
|
||||
# --------------------------------------------------------------
|
||||
"""
|
||||
:purpose: Универсальная вспомогательная обёртка над
|
||||
``logging.Logger.<level>``. Принимает любые ``*args``
|
||||
(подстановочные параметры) и ``extra``‑словарь.
|
||||
:preconditions:
|
||||
- ``level_method`` – один из методов ``logger``,
|
||||
- ``msg`` – строка‑шаблон,
|
||||
- ``*args`` – значения для ``%``‑подстановок,
|
||||
- ``extra`` – пользовательские атрибуты (может быть ``None``).
|
||||
:postconditions: Запись в журнал выполнена.
|
||||
"""
|
||||
def _log(
|
||||
self,
|
||||
level_method: Any,
|
||||
msg: str,
|
||||
*args: Any,
|
||||
extra: Optional[Mapping[str, Any]] = None,
|
||||
exc_info: bool = False,
|
||||
) -> None:
|
||||
if extra is not None:
|
||||
level_method(msg, *args, extra=extra, exc_info=exc_info)
|
||||
else:
|
||||
level_method(msg, *args, exc_info=exc_info)
|
||||
|
||||
def error(self, message: str, extra: Optional[dict] = None, exc_info: bool = False):
|
||||
self.logger.error(message, extra=extra, exc_info=exc_info)
|
||||
# [END_ENTITY]
|
||||
|
||||
def warning(self, message: str, extra: Optional[dict] = None, exc_info: bool = False):
|
||||
self.logger.warning(message, extra=extra, exc_info=exc_info)
|
||||
# --------------------------------------------------------------
|
||||
# [ENTITY: Method('info')]
|
||||
# --------------------------------------------------------------
|
||||
"""
|
||||
:purpose: Записать сообщение уровня INFO.
|
||||
"""
|
||||
def info(
|
||||
self,
|
||||
msg: str,
|
||||
*args: Any,
|
||||
extra: Optional[Mapping[str, Any]] = None,
|
||||
exc_info: bool = False,
|
||||
) -> None:
|
||||
self._log(self.logger.info, msg, *args, extra=extra, exc_info=exc_info)
|
||||
# [END_ENTITY]
|
||||
|
||||
def critical(self, message: str, extra: Optional[dict] = None, exc_info: bool = False):
|
||||
self.logger.critical(message, extra=extra, exc_info=exc_info)
|
||||
# --------------------------------------------------------------
|
||||
# [ENTITY: Method('debug')]
|
||||
# --------------------------------------------------------------
|
||||
"""
|
||||
:purpose: Записать сообщение уровня DEBUG.
|
||||
"""
|
||||
def debug(
|
||||
self,
|
||||
msg: str,
|
||||
*args: Any,
|
||||
extra: Optional[Mapping[str, Any]] = None,
|
||||
exc_info: bool = False,
|
||||
) -> None:
|
||||
self._log(self.logger.debug, msg, *args, extra=extra, exc_info=exc_info)
|
||||
# [END_ENTITY]
|
||||
|
||||
def debug(self, message: str, extra: Optional[dict] = None, exc_info: bool = False):
|
||||
self.logger.debug(message, extra=extra, exc_info=exc_info)
|
||||
# --------------------------------------------------------------
|
||||
# [ENTITY: Method('warning')]
|
||||
# --------------------------------------------------------------
|
||||
"""
|
||||
:purpose: Записать сообщение уровня WARNING.
|
||||
"""
|
||||
def warning(
|
||||
self,
|
||||
msg: str,
|
||||
*args: Any,
|
||||
extra: Optional[Mapping[str, Any]] = None,
|
||||
exc_info: bool = False,
|
||||
) -> None:
|
||||
self._log(self.logger.warning, msg, *args, extra=extra, exc_info=exc_info)
|
||||
# [END_ENTITY]
|
||||
|
||||
def exception(self, message: str, *args, **kwargs):
|
||||
self.logger.exception(message, *args, **kwargs)
|
||||
# END_CLASS_SupersetLogger
|
||||
# --------------------------------------------------------------
|
||||
# [ENTITY: Method('error')]
|
||||
# --------------------------------------------------------------
|
||||
"""
|
||||
:purpose: Записать сообщение уровня ERROR.
|
||||
"""
|
||||
def error(
|
||||
self,
|
||||
msg: str,
|
||||
*args: Any,
|
||||
extra: Optional[Mapping[str, Any]] = None,
|
||||
exc_info: bool = False,
|
||||
) -> None:
|
||||
self._log(self.logger.error, msg, *args, extra=extra, exc_info=exc_info)
|
||||
# [END_ENTITY]
|
||||
|
||||
# END_MODULE_logger
|
||||
# --------------------------------------------------------------
|
||||
# [ENTITY: Method('critical')]
|
||||
# --------------------------------------------------------------
|
||||
"""
|
||||
:purpose: Записать сообщение уровня CRITICAL.
|
||||
"""
|
||||
def critical(
|
||||
self,
|
||||
msg: str,
|
||||
*args: Any,
|
||||
extra: Optional[Mapping[str, Any]] = None,
|
||||
exc_info: bool = False,
|
||||
) -> None:
|
||||
self._log(self.logger.critical, msg, *args, extra=extra, exc_info=exc_info)
|
||||
# [END_ENTITY]
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# [ENTITY: Method('exception')]
|
||||
# --------------------------------------------------------------
|
||||
"""
|
||||
:purpose: Записать сообщение уровня ERROR вместе с трассировкой
|
||||
текущего исключения (аналог ``logger.exception``).
|
||||
"""
|
||||
def exception(self, msg: str, *args: Any, **kwargs: Any) -> None:
|
||||
self.logger.exception(msg, *args, **kwargs)
|
||||
# [END_ENTITY]
|
||||
|
||||
# --------------------------------------------------------------
|
||||
# [END_FILE logger.py]
|
||||
# --------------------------------------------------------------
|
||||
Reference in New Issue
Block a user