diff --git a/src/pyg2o/server.py b/src/pyg2o/server.py index 46b806a..ff76fdb 100644 --- a/src/pyg2o/server.py +++ b/src/pyg2o/server.py @@ -1,11 +1,13 @@ from __future__ import annotations + import json -from types import SimpleNamespace -import loguru import asyncio +from types import SimpleNamespace from weakref import WeakValueDictionary, WeakSet, finalize from collections import UserDict from uuid import uuid4 +from loguru import logger + from fastapi import WebSocket, FastAPI, WebSocketDisconnect, WebSocketException from .event import call_event @@ -19,7 +21,6 @@ class TopicWeakDict(UserDict): super().__setitem__(key, value) class Server: - _logger: loguru.Logger = loguru.logger _static_tokens: list[str] = [] _temp_tokens: list[str] = [] _server_connection: WebSocket | None = None @@ -60,6 +61,16 @@ class Server: data = json.dumps(data) # Меняем синтаксис под Squirrel data = data.replace("'", '\\"').replace('True', 'true').replace('False', 'false') + + logs_connection_list = [f"{item.client.host}:{item.client.port}" for item in connection_list] + logger.info( + 'Отправлено новое сообщение по каналу WebSocket', + log_type = 'PyG2O', + receivers = logs_connection_list, + message_uuid = data[uuid], + message_data = data, + ) + asyncio.create_task(cls._async_send(connection_list, data)) return request @@ -118,7 +129,11 @@ class Server: await connection.accept() await cls._subscribe(['all'], connection) - cls._logger.info('WebSocket клиент подключился') + logger.info( + 'WebSocket клиент подключился', + log_type = 'PyG2O', + connection = f"{connection.client.host}:{connection.client.port}", + ) try: while True: @@ -127,16 +142,37 @@ class Server: message_data = json.loads(data) asyncio.create_task(cls._process_message(connection, message_data)) except json.JSONDecodeError as e: - cls._logger.exception(f'Ошибка декодирования JSON: {e}') + logger.info( + 'Ошибка декодирования JSON сообщения', + log_type = 'PyG2O', + description = e, + message_data = data, + connection = f"{connection.client.host}:{connection.client.port}", + ) except WebSocketDisconnect: - cls._logger.info('WebSocket клиент отключился') if connection == cls._server_connection: cls._server_connection = None + logger.info( + 'WebSocket G2O сервер отключился', + log_type = 'PyG2O', + connection = f"{connection.client.host}:{connection.client.port}", + ) else: playerid = next((key for key, values in cls._registered_clients.items() if connection in values), None) if playerid is not None: cls._registered_clients[playerid].remove(connection) + logger.info( + 'WebSocket клиент отключился', + log_type = 'PyG2O', + connection = f"{connection.client.host}:{connection.client.port}", + playerid = playerid, + ) except WebSocketException as e: - cls._logger.exception(f'Ошибка WebSocket подключения: {e}') + logger.exception( + 'Ошибка при обработке WebSocket сообщения', + log_type = 'PyG2O', + connection = f"{connection.client.host}:{connection.client.port}", + description = e, + ) @classmethod async def _process_message(cls, connection: WebSocket, message: dict): @@ -162,26 +198,75 @@ class Server: cls._registered_clients[playerid].append(connection) except KeyError: cls._registered_clients[playerid] = [connection] + + logs_connection_list = [f"{item.client.host}:{item.client.port}" for item in cls._registered_clients[playerid]] + logger.info( + 'Зарегистрирован новый WebSocket клиент', + log_type = 'PyG2O', + connection = f"{connection.client.host}:{connection.client.port}", + playerid = playerid, + total_playerid_connections = logs_connection_list, + ) case {'event': 'register_server'}: if cls._server_connection is None: cls._server_connection = connection + logger.info( + 'Зарегистрирован новый WebSocket G2O сервер', + log_type = 'PyG2O', + connection = f"{connection.client.host}:{connection.client.port}", + ) case {'event': 'sq_response', 'uuid': uuid, 'data': data}: try: cls._requests[uuid].set_result(data) + logger.info( + 'Получен ответ от G2O сервера (sq_response)', + log_type = 'PyG2O', + connection = f"{connection.client.host}:{connection.client.port}", + message_uuid = uuid, + message_data = data, + ) except KeyError: - ... + logger.warning( + 'Получен неожиданный ответ от G2O сервера', + log_type = 'PyG2O', + connection = f"{connection.client.host}:{connection.client.port}", + message_uuid = uuid, + message_data = data, + ) case {'event': event, 'uuid': uuid, **kwargs}: try: cls._requests[uuid].set_result(SimpleNamespace(**kwargs)) + logger.info( + 'Получен ответ от клиента', + log_type = 'PyG2O', + connection = f"{connection.client.host}:{connection.client.port}", + message_event = event, + message_uuid = uuid, + message_data = kwargs, + ) except KeyError: kwargs['uuid'] = uuid kwargs['connection'] = connection playerid = next((key for key, values in cls._registered_clients.items() if connection in values), None) if playerid is not None: kwargs['playerid'] = playerid asyncio.create_task(call_event(event, **kwargs)) + logger.info( + 'Получено сообщение от сервера' if connection == cls._server_connection else 'Получено сообщение от клиента', + log_type = 'PyG2O', + connection = f"{connection.client.host}:{connection.client.port}", + message_event = event, + message_uuid = uuid, + message_data = kwargs, + ) case _: + logger.error( + 'Получено неподдерживаемое сообщение от сервера' if connection == cls._server_connection else 'Получено неподдерживаемое сообщение от клиента', + log_type = 'PyG2O', + connection = f"{connection.client.host}:{connection.client.port}", + message_data = message, + ) raise ValueError(f'Неподдерживаемый тип PyG2O сообщения: {message}')