Files
core/utils/logger.py

154 lines
5.4 KiB
Python
Raw Normal View History

2024-02-21 14:23:42 +03:00
import logging
2025-02-10 18:04:08 +03:00
from pathlib import Path
from typing import Any
2024-04-23 15:15:18 +03:00
2024-02-21 14:23:42 +03:00
import colorlog
2025-08-28 20:19:30 +03:00
from graphql import GraphQLError
# Импорт отложен для избежания циклических импортов
# from auth.exceptions import AuthorizationError
2024-02-21 14:23:42 +03:00
2025-02-10 18:04:08 +03:00
_lib_path = Path(__file__).parents[1]
_leng_path = len(_lib_path.as_posix())
2025-02-11 12:00:35 +03:00
def filter(record: logging.LogRecord) -> bool:
2025-02-10 18:04:08 +03:00
# Define `package` attribute with the relative path.
2025-02-11 12:00:35 +03:00
record.package = record.pathname[_leng_path + 1 :].replace(".py", "")
record.emoji = (
"🔍"
if record.levelno == logging.DEBUG
2025-03-09 21:01:52 +03:00
else ""
2025-02-11 12:00:35 +03:00
if record.levelno == logging.INFO
else "🚧"
if record.levelno == logging.WARNING
else ""
if record.levelno == logging.ERROR
else "🧨"
if record.levelno == logging.CRITICAL
2025-02-10 18:04:08 +03:00
else ""
2025-02-11 12:00:35 +03:00
)
return True
2025-02-10 18:04:08 +03:00
2025-02-11 12:00:35 +03:00
2024-02-21 14:23:42 +03:00
# Define the color scheme
color_scheme = {
2025-02-10 18:04:08 +03:00
"DEBUG": "light_black",
2024-04-17 18:32:23 +03:00
"INFO": "green",
"WARNING": "yellow",
"ERROR": "red",
"CRITICAL": "red,bg_white",
2024-02-21 14:23:42 +03:00
}
# Define secondary log colors
secondary_colors = {
2024-04-17 18:32:23 +03:00
"log_name": {"DEBUG": "blue"},
"asctime": {"DEBUG": "cyan"},
"process": {"DEBUG": "purple"},
2025-02-10 18:04:08 +03:00
"module": {"DEBUG": "light_black,bg_blue"},
"funcName": {"DEBUG": "light_white,bg_blue"}, # Add this line
2024-02-21 14:23:42 +03:00
}
# Define the log format string
2025-02-10 18:04:08 +03:00
fmt_string = "%(emoji)s%(log_color)s%(package)s.%(funcName)s%(reset)s %(white)s%(message)s"
2024-02-21 14:23:42 +03:00
# Define formatting configuration
fmt_config = {
2024-04-17 18:32:23 +03:00
"log_colors": color_scheme,
"secondary_log_colors": secondary_colors,
"style": "%",
"reset": True,
2024-02-21 14:23:42 +03:00
}
2024-03-03 16:59:15 +03:00
2024-02-21 14:23:42 +03:00
class MultilineColoredFormatter(colorlog.ColoredFormatter):
def __init__(self, *args: Any, **kwargs: Any) -> None:
2024-03-12 18:14:34 +03:00
super().__init__(*args, **kwargs)
2024-04-17 18:32:23 +03:00
self.log_colors = kwargs.pop("log_colors", {})
self.secondary_log_colors = kwargs.pop("secondary_log_colors", {})
2024-03-12 18:14:34 +03:00
def format(self, record: logging.LogRecord) -> str:
2025-02-10 18:04:08 +03:00
# Add default emoji if not present
2025-02-11 12:00:35 +03:00
if not hasattr(record, "emoji"):
record.emoji = "📝"
# Add default package if not present
if not hasattr(record, "package"):
record.package = getattr(record, "name", "unknown")
# Format the first line normally
formatted_first_line = super().format(record)
# Check if the message has multiple lines
lines = formatted_first_line.split("\n")
if len(lines) > 1:
# For multiple lines, only apply colors to the first line
# Keep subsequent lines without color formatting
2024-03-12 18:27:58 +03:00
formatted_lines = [formatted_first_line]
formatted_lines.extend(lines[1:])
2024-04-17 18:32:23 +03:00
return "\n".join(formatted_lines)
return super().format(record)
2024-02-21 14:23:42 +03:00
2024-03-12 18:17:28 +03:00
2024-02-21 14:23:42 +03:00
# Create a MultilineColoredFormatter object for colorized logging
formatter = MultilineColoredFormatter(fmt_string, **fmt_config)
# Create a stream handler for logging output
stream = logging.StreamHandler()
stream.setFormatter(formatter)
2025-02-10 18:04:08 +03:00
def get_colorful_logger(name: str = "main") -> logging.Logger:
2025-02-10 18:04:08 +03:00
# Create and configure the logger
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
logger.addHandler(stream)
logger.addFilter(filter)
return logger
2024-02-21 14:23:42 +03:00
# Set up the root logger with the same formatting
root_logger = logging.getLogger()
2025-02-10 18:04:08 +03:00
root_logger.setLevel(logging.DEBUG)
root_logger.addHandler(stream)
root_logger.addFilter(filter)
2025-08-30 20:43:13 +03:00
ignore_logs = ["_trace", "httpx", "_client", "atrace", "aiohttp", "_client", "ariadne"]
2025-02-10 18:04:08 +03:00
for lgr in ignore_logs:
loggr = logging.getLogger(lgr)
2025-08-30 20:43:13 +03:00
loggr.setLevel(logging.WARNING) # Повышаем до WARNING для подавления INFO и DEBUG
2025-08-28 20:19:30 +03:00
def custom_error_formatter(error: GraphQLError, debug: bool = False) -> dict[Any, Any]:
"""
Кастомный форматтер ошибок для подавления трейсбеков у ожидаемых ошибок авторизации.
🔍 Логирует AuthorizationError как обычные события, не как исключения
"""
# Преобразуем в словарь для работы с ним
formatted_error: dict[str, Any] = {
"message": error.message,
"locations": getattr(error.formatted, "locations", []),
"path": getattr(error.formatted, "path", []),
"extensions": getattr(error.formatted, "extensions", {}),
}
2025-08-30 20:43:13 +03:00
# Для ошибок авторизации не показываем трейсбек и НЕ логируем вообще
2025-08-28 20:19:30 +03:00
# Проверяем по имени класса для избежания циклических импортов
if (
error.original_error
and hasattr(error.original_error, "__class__")
and error.original_error.__class__.__name__ == "AuthorizationError"
):
# Убираем extensions.exception если есть
if "extensions" in formatted_error and "exception" in formatted_error["extensions"]:
del formatted_error["extensions"]["exception"]
2025-08-30 20:43:13 +03:00
# НЕ логируем ошибки авторизации - они ожидаемы и нормальны
2025-08-28 20:19:30 +03:00
# Для остальных ошибок используем стандартное логирование
elif debug and error.original_error:
root_logger.error(f"GraphQL error: {error.message}", exc_info=error.original_error)
else:
root_logger.warning(f"GraphQL error: {error.message}")
return formatted_error