From 9c44cc7800321254c73862f99a4288d47627c333 Mon Sep 17 00:00:00 2001 From: collhoun <2904yr@mail.ru> Date: Fri, 1 May 2026 23:43:13 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20correlation=20id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/logging_dev.yaml | 7 ++++++- configs/logging_prod.yaml | 15 ++++++++++----- configs/logging_test.yaml | 11 ++++++++--- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/configs/logging_dev.yaml b/configs/logging_dev.yaml index d6d6516..3a755c4 100644 --- a/configs/logging_dev.yaml +++ b/configs/logging_dev.yaml @@ -3,7 +3,7 @@ disable_existing_loggers: false formatters: dev_formatter: - format: "%(asctime)s | %(levelname)-7s | %(name)s | %(message)s" + format: "%(asctime)s | %(correlation_id)s | %(levelname)-7s | %(name)s | %(message)s" datefmt: "%H:%M:%S" handlers: @@ -12,6 +12,11 @@ handlers: level: TRACE formatter: dev_formatter stream: ext://sys.stdout + filters: [correlation_filter] + +filters: + correlation_filter: + (): src.core.correlation.CorrelationFilter loggers: src: diff --git a/configs/logging_prod.yaml b/configs/logging_prod.yaml index 8c294ac..e142c9a 100644 --- a/configs/logging_prod.yaml +++ b/configs/logging_prod.yaml @@ -3,7 +3,7 @@ disable_existing_loggers: false formatters: prod_formatter: - format: "%(asctime)s | %(levelname)-7s | %(name)s | %(message)s" + format: "%(asctime)s | %(correlation_id)s | %(levelname)-7s | %(name)s | %(message)s" datefmt: "%Y-%m-%d %H:%M:%S" handlers: @@ -12,13 +12,18 @@ handlers: level: INFO formatter: prod_formatter stream: ext://sys.stdout + filters: [correlation_filter] + +filters: + correlation_filter: + (): src.core.correlation.CorrelationFilter loggers: src: - level: TRACE - handlers: [file] + level: INFO + handlers: [console] propagate: false root: - level: INFO - handlers: [file] \ No newline at end of file + level: WARNING + handlers: [console] \ No newline at end of file diff --git a/configs/logging_test.yaml b/configs/logging_test.yaml index a24a174..fb5fcb7 100644 --- a/configs/logging_test.yaml +++ b/configs/logging_test.yaml @@ -3,7 +3,7 @@ disable_existing_loggers: false formatters: test_formatter: - format: "%(asctime)s | %(levelname)-7s | %(name)s | %(message)s" + format: "%(asctime)s | %(correlation_id)s | %(levelname)-7s | %(name)s | %(message)s" datefmt: "%Y-%m-%d %H:%M:%S" handlers: @@ -12,13 +12,18 @@ handlers: level: DEBUG formatter: test_formatter stream: ext://sys.stdout + filters: [correlation_filter] + +filters: + correlation_filter: + (): src.core.correlation.CorrelationFilter loggers: src: level: DEBUG - handlers: [file] + handlers: [console] propagate: false root: level: WARNING - handlers: [file] \ No newline at end of file + handlers: [console] \ No newline at end of file From 7d93caa42f4d5249ecbd7dd60b8569959d44bc4c Mon Sep 17 00:00:00 2001 From: collhoun <2904yr@mail.ru> Date: Fri, 1 May 2026 23:44:29 +0300 Subject: [PATCH 2/3] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20id=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=BE=D0=B4=D0=BA?= =?UTF-8?q?=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=B8=20=D1=81?= =?UTF-8?q?=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/external.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/api/external.py b/src/api/external.py index c76e74f..6721bf6 100644 --- a/src/api/external.py +++ b/src/api/external.py @@ -13,6 +13,13 @@ from lambda_agent_api.client import ClientMessage, MsgUserMessage from src.agent import AgentChat from src.api.dependencies import get_chat_ws from src.core.logger import get_logger +from src.core.correlation import ( + generate_connection_id, + generate_message_id, + set_connection_id, + set_message_id, + clear_context, +) router = APIRouter() @@ -27,6 +34,10 @@ async def websocket_endpoint( # важно использовать именно _ws вариант, чтобы корректно обрабатывались исключения chat: Annotated[AgentChat, Depends(get_chat_ws)], ): + # Генерируем уникальный ID для этого подключения + connection_id = generate_connection_id() + set_connection_id(connection_id) + logger.info(f"WebSocket connection accepted for chat_id: {chat_id}") await ws.accept() await ws.send_text(MsgStatus().model_dump_json()) @@ -34,6 +45,11 @@ async def websocket_endpoint( try: while True: raw = await ws.receive_text() + + # Генерируем ID для каждого сообщения + message_id = generate_message_id() + set_message_id(message_id) + logger.trace(f"Received raw message: {len(raw)} characters for chat_id: {chat_id}") try: msg = ClientMessage.validate_json(raw) @@ -51,6 +67,8 @@ async def websocket_endpoint( await ws.send_text( MsgError(code="INTERNAL_ERROR", details=str(exc)).model_dump_json() ) + finally: + clear_context() async def process_message(ws: WebSocket, chat: AgentChat, msg): From d9207f3e0654c6aec8f2879a99b49fa4da2d8c96 Mon Sep 17 00:00:00 2001 From: collhoun <2904yr@mail.ru> Date: Fri, 1 May 2026 23:45:02 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20core-=D1=80=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20correlation=20id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/correlation.py | 104 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/core/correlation.py diff --git a/src/core/correlation.py b/src/core/correlation.py new file mode 100644 index 0000000..d10484c --- /dev/null +++ b/src/core/correlation.py @@ -0,0 +1,104 @@ +import logging +from contextvars import ContextVar +from typing import Optional + +# Глобальные счетчики +_connection_counter = 0 +_message_counter = 0 + +# Контекстная переменная для хранения correlation ID +_correlation_id_var: ContextVar[Optional[str]] = ContextVar( + "correlation_id", default=None +) + +# Контекстная переменная для хранения ID подключения +_connection_id_var: ContextVar[Optional[int]] = ContextVar( + "connection_id", default=None +) + +# Контекстная переменная для хранения ID сообщения +_message_id_var: ContextVar[Optional[int]] = ContextVar( + "message_id", default=None +) + + +def generate_connection_id() -> int: + """Генерирует новый ID подключения (глобальный счетчик).""" + global _connection_counter + _connection_counter += 1 + return _connection_counter + + +def generate_message_id() -> int: + """Генерирует новый ID сообщения (глобальный счетчик).""" + global _message_counter + _message_counter += 1 + return _message_counter + + +def set_connection_id(connection_id: int) -> None: + """Устанавливает ID подключения.""" + _connection_id_var.set(connection_id) + + +def set_message_id(message_id: int) -> None: + """Устанавливает ID сообщения.""" + _message_id_var.set(message_id) + # Обновляем полный correlation ID + _update_correlation_id() + + +def set_correlation_id(correlation_id: str) -> None: + """Устанавливает полный correlation ID вручную.""" + _correlation_id_var.set(correlation_id) + + +def _update_correlation_id() -> None: + """Обновляет полный correlation ID на основе connection_id и message_id.""" + connection_id = _connection_id_var.get() + message_id = _message_id_var.get() + + if connection_id and message_id: + correlation_id = f"CONN_{connection_id}+MSG_{message_id}" + elif connection_id: + correlation_id = f"CONN_{connection_id}" + elif message_id: + correlation_id = f"MSG_{message_id}" + else: + correlation_id = None + + if correlation_id: + _correlation_id_var.set(correlation_id) + + +def get_correlation_id() -> Optional[str]: + """Возвращает текущий correlation ID.""" + return _correlation_id_var.get() + + +def get_connection_id() -> Optional[int]: + """Возвращает текущий ID подключения.""" + return _connection_id_var.get() + + +def get_message_id() -> Optional[int]: + """Возвращает текущий ID сообщения.""" + return _message_id_var.get() + + +def clear_context() -> None: + """Очищает все контекстные переменные.""" + _correlation_id_var.set(None) + _connection_id_var.set(None) + _message_id_var.set(None) + + +class CorrelationFilter(logging.Filter): + """ + Фильтр логирования, который добавляет correlation_id в запись логирования. + """ + + def filter(self, record): + correlation_id = get_correlation_id() + record.correlation_id = correlation_id or "-" + return True