src.core.err
Функционал обработки ошибок
1"""Функционал обработки ошибок""" 2 3import logging 4import os 5from collections import deque 6from functools import wraps 7 8from aiogram.exceptions import AiogramError, TelegramNetworkError 9from redis import exceptions as rexc 10from redis.asyncio.client import Pipeline 11 12from core.exceptions import DatabaseError 13 14ERRORS = deque([], maxlen=5) 15 16logger = logging.getLogger() 17rLogger = logging.getLogger("redis") 18 19 20def log_cash_error(error: Exception): 21 error_info = {"type": type(error).__name__, "message": str(error)} 22 23 if error_info not in ERRORS: 24 ERRORS.append(error_info) 25 26 return True 27 28 29def get_args_dict(fn, args, kwargs): 30 """Создает словарь аргументов для функции. 31 32 Args: 33 fn (function): Функция, для которой необходимо получить аргументы. 34 args (tuple): Позиционные аргументы функции. 35 kwargs (dict): Именованные аргументы функции. 36 37 Returns: 38 dict: Словарь аргументов, где ключи - имена параметров, а значения - переданные значения. 39 """ 40 args_names = fn.__code__.co_varnames[: fn.__code__.co_argcount] 41 return {**dict(zip(args_names, args)), **kwargs} 42 43 44def exception_logging( 45 ignore_raise=False, 46 custom_exception=Exception, 47 message="Something went wrong", 48): 49 """Декоратор для логирования исключений, возникающих в синхронных функциях. 50 51 Args: 52 ignore_raise (bool, optional): Прерывать ли выполнение программы при возникновении исключения. Defaults to False. 53 custom_exception (Exception, optional): Тип исключения, который нужно обрабатывать. Defaults to Exception. 54 message (str, optional): Сообщение для логирования при возникновении исключения. Defaults to "Something went wrong". 55 56 Returns: 57 function: Обернутая функция с логированием исключений. 58 """ 59 60 def __exception_logging(func): 61 old_factory = logging.getLogRecordFactory() 62 63 def record_factory(*args, **kwargs): 64 """Создает запись лога с информацией о функции.""" 65 record = old_factory(*args, **kwargs) 66 if hasattr(func, "__wrapped__"): 67 record.funcName = func.__wrapped__.__name__ 68 record.filename = os.path.basename( 69 func.__wrapped__.__code__.co_filename 70 ) 71 record.lineno = func.__wrapped__.__code__.co_firstlineno 72 else: 73 record.funcName = func.__name__ 74 record.filename = os.path.basename(func.__code__.co_filename) 75 record.lineno = func.__code__.co_firstlineno 76 return record 77 78 @wraps(func) 79 def wrapper(*args, **kwargs): 80 """Обертка для функции с обработкой исключений.""" 81 try: 82 result = func(*args, **kwargs) 83 except custom_exception as e: 84 logging.setLogRecordFactory(record_factory) 85 signature = get_args_dict(func, args, kwargs) 86 logger.debug(f"function {func.__name__} called with args {signature}") 87 logger.exception(f"{message}\nError: {e}") 88 logging.setLogRecordFactory(old_factory) 89 if not ignore_raise: 90 raise 91 else: 92 return result 93 94 return wrapper 95 96 return __exception_logging 97 98 99def redis_exceptor(func): 100 """Декоратор для обработки исключений при работе с Redis. 101 102 Args: 103 func (function): Функция, которая будет обернута для обработки исключений. 104 105 Returns: 106 function: Обернутая асинхронная функция с обработкой исключений Redis. 107 """ 108 109 @wraps(func) 110 async def wrapper(pipeline: Pipeline): 111 """Обертка для функции с обработкой исключений Redis.""" 112 try: 113 logQuery = str(getattr(pipeline, "command_stack", "")) 114 115 result = await func(pipeline) 116 return result 117 118 except rexc.AuthenticationError: 119 rLogger.exception( 120 f"Ошибка аутентификации при подключении к Redis :: {logQuery}" 121 ) 122 raise DatabaseError 123 except rexc.ConnectionError: 124 rLogger.exception(f"Ошибка подключения к Redis :: {logQuery}") 125 raise DatabaseError 126 except rexc.TimeoutError: 127 rLogger.exception( 128 f"Превышено время ожидания при работе с Redis :: {logQuery}" 129 ) 130 raise DatabaseError 131 except IndexError: 132 rLogger.exception("Command Stack is empty") 133 raise DatabaseError 134 except rexc.RedisError: 135 rLogger.exception(f"Произошла неизвестная ошибка c Redis :: {logQuery}") 136 raise DatabaseError 137 138 return wrapper 139 140 141def bot_except(func): 142 """Декоратор для логирования исключений, возникающих в асинхронных функциях.""" 143 144 @wraps(func) 145 async def wrapper(*args, **kwargs): 146 try: 147 result = await func(*args, **kwargs) 148 149 except TelegramNetworkError as e: 150 logger.warning(e.args[0]) 151 except AiogramError: 152 logger.exception("Ошибка Aiogram API") 153 raise 154 except Exception: 155 logger.exception("Неизвестная ошибка") 156 raise 157 else: 158 return result 159 160 return wrapper
ERRORS =
deque([], maxlen=5)
logger =
<RootLogger root (DEBUG)>
rLogger =
<Logger redis (INFO)>
def
log_cash_error(error: Exception):
def
get_args_dict(fn, args, kwargs):
30def get_args_dict(fn, args, kwargs): 31 """Создает словарь аргументов для функции. 32 33 Args: 34 fn (function): Функция, для которой необходимо получить аргументы. 35 args (tuple): Позиционные аргументы функции. 36 kwargs (dict): Именованные аргументы функции. 37 38 Returns: 39 dict: Словарь аргументов, где ключи - имена параметров, а значения - переданные значения. 40 """ 41 args_names = fn.__code__.co_varnames[: fn.__code__.co_argcount] 42 return {**dict(zip(args_names, args)), **kwargs}
Создает словарь аргументов для функции.
Arguments:
- fn (function): Функция, для которой необходимо получить аргументы.
- args (tuple): Позиционные аргументы функции.
- kwargs (dict): Именованные аргументы функции.
Returns:
dict: Словарь аргументов, где ключи - имена параметров, а значения - переданные значения.
def
exception_logging( ignore_raise=False, custom_exception=<class 'Exception'>, message='Something went wrong'):
45def exception_logging( 46 ignore_raise=False, 47 custom_exception=Exception, 48 message="Something went wrong", 49): 50 """Декоратор для логирования исключений, возникающих в синхронных функциях. 51 52 Args: 53 ignore_raise (bool, optional): Прерывать ли выполнение программы при возникновении исключения. Defaults to False. 54 custom_exception (Exception, optional): Тип исключения, который нужно обрабатывать. Defaults to Exception. 55 message (str, optional): Сообщение для логирования при возникновении исключения. Defaults to "Something went wrong". 56 57 Returns: 58 function: Обернутая функция с логированием исключений. 59 """ 60 61 def __exception_logging(func): 62 old_factory = logging.getLogRecordFactory() 63 64 def record_factory(*args, **kwargs): 65 """Создает запись лога с информацией о функции.""" 66 record = old_factory(*args, **kwargs) 67 if hasattr(func, "__wrapped__"): 68 record.funcName = func.__wrapped__.__name__ 69 record.filename = os.path.basename( 70 func.__wrapped__.__code__.co_filename 71 ) 72 record.lineno = func.__wrapped__.__code__.co_firstlineno 73 else: 74 record.funcName = func.__name__ 75 record.filename = os.path.basename(func.__code__.co_filename) 76 record.lineno = func.__code__.co_firstlineno 77 return record 78 79 @wraps(func) 80 def wrapper(*args, **kwargs): 81 """Обертка для функции с обработкой исключений.""" 82 try: 83 result = func(*args, **kwargs) 84 except custom_exception as e: 85 logging.setLogRecordFactory(record_factory) 86 signature = get_args_dict(func, args, kwargs) 87 logger.debug(f"function {func.__name__} called with args {signature}") 88 logger.exception(f"{message}\nError: {e}") 89 logging.setLogRecordFactory(old_factory) 90 if not ignore_raise: 91 raise 92 else: 93 return result 94 95 return wrapper 96 97 return __exception_logging
Декоратор для логирования исключений, возникающих в синхронных функциях.
Arguments:
- ignore_raise (bool, optional): Прерывать ли выполнение программы при возникновении исключения. Defaults to False.
- custom_exception (Exception, optional): Тип исключения, который нужно обрабатывать. Defaults to Exception.
- message (str, optional): Сообщение для логирования при возникновении исключения. Defaults to "Something went wrong".
Returns:
function: Обернутая функция с логированием исключений.
def
redis_exceptor(func):
100def redis_exceptor(func): 101 """Декоратор для обработки исключений при работе с Redis. 102 103 Args: 104 func (function): Функция, которая будет обернута для обработки исключений. 105 106 Returns: 107 function: Обернутая асинхронная функция с обработкой исключений Redis. 108 """ 109 110 @wraps(func) 111 async def wrapper(pipeline: Pipeline): 112 """Обертка для функции с обработкой исключений Redis.""" 113 try: 114 logQuery = str(getattr(pipeline, "command_stack", "")) 115 116 result = await func(pipeline) 117 return result 118 119 except rexc.AuthenticationError: 120 rLogger.exception( 121 f"Ошибка аутентификации при подключении к Redis :: {logQuery}" 122 ) 123 raise DatabaseError 124 except rexc.ConnectionError: 125 rLogger.exception(f"Ошибка подключения к Redis :: {logQuery}") 126 raise DatabaseError 127 except rexc.TimeoutError: 128 rLogger.exception( 129 f"Превышено время ожидания при работе с Redis :: {logQuery}" 130 ) 131 raise DatabaseError 132 except IndexError: 133 rLogger.exception("Command Stack is empty") 134 raise DatabaseError 135 except rexc.RedisError: 136 rLogger.exception(f"Произошла неизвестная ошибка c Redis :: {logQuery}") 137 raise DatabaseError 138 139 return wrapper
Декоратор для обработки исключений при работе с Redis.
Arguments:
- func (function): Функция, которая будет обернута для обработки исключений.
Returns:
function: Обернутая асинхронная функция с обработкой исключений Redis.
def
bot_except(func):
142def bot_except(func): 143 """Декоратор для логирования исключений, возникающих в асинхронных функциях.""" 144 145 @wraps(func) 146 async def wrapper(*args, **kwargs): 147 try: 148 result = await func(*args, **kwargs) 149 150 except TelegramNetworkError as e: 151 logger.warning(e.args[0]) 152 except AiogramError: 153 logger.exception("Ошибка Aiogram API") 154 raise 155 except Exception: 156 logger.exception("Неизвестная ошибка") 157 raise 158 else: 159 return result 160 161 return wrapper
Декоратор для логирования исключений, возникающих в асинхронных функциях.