Files
ss-tools/superset_tool/utils/logger.py
Volobuev Andrey 8f6b44c679 backup worked
2025-10-06 13:59:30 +03:00

205 lines
8.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# [MODULE_PATH] superset_tool.utils.logger
# [FILE] logger.py
# [SEMANTICS] logging, utils, aifriendly, infrastructure
# --------------------------------------------------------------
# [IMPORTS]
# --------------------------------------------------------------
import logging
import sys
from datetime import datetime
from pathlib import Path
from typing import Optional, Any, Mapping
# [END_IMPORTS]
# --------------------------------------------------------------
# [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,
) -> None:
self.logger = logging.getLogger(name)
self.logger.setLevel(level)
self.logger.propagate = False # ← не «прокидываем» записи выше
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
# ---- Очистка предыдущих обработчиков (важно при повторных инициализациях) ----
if self.logger.hasHandlers():
self.logger.handlers.clear()
# ---- Файловый обработчик (если указана директория) ----
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"
)
file_handler.setFormatter(formatter)
self.logger.addHandler(file_handler)
# ---- Консольный обработчик ----
if console:
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(formatter)
self.logger.addHandler(console_handler)
# [END_ENTITY]
# --------------------------------------------------------------
# [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)
# [END_ENTITY]
# --------------------------------------------------------------
# [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]
# --------------------------------------------------------------
# [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]
# --------------------------------------------------------------
# [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]
# --------------------------------------------------------------
# [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]
# --------------------------------------------------------------
# [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]
# --------------------------------------------------------------