From 77b6abdcacb749462b28d3cb7287a7eac861e733 Mon Sep 17 00:00:00 2001 From: Marc-Andrieu Date: Tue, 14 Oct 2025 15:15:23 +0200 Subject: [PATCH 1/3] Send logs to Matrix asynchronously --- app/core/utils/log.py | 3 +++ app/utils/communication/matrix.py | 18 ++++++++++++------ app/utils/loggers_tools/matrix_handler.py | 8 +++++++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/app/core/utils/log.py b/app/core/utils/log.py index 07a74335e2..482d653202 100644 --- a/app/core/utils/log.py +++ b/app/core/utils/log.py @@ -7,6 +7,7 @@ from typing import Any import uvicorn +from fastapi import BackgroundTasks from app.core.utils.config import Settings @@ -121,6 +122,7 @@ def _get_config_dict(self, settings: Settings): # Send error to a Matrix server. If credentials are not set in settings, the handler will be disabled "formatter": "matrix", "class": "app.utils.loggers_tools.matrix_handler.MatrixHandler", + "background_tasks": BackgroundTasks, "room_id": settings.MATRIX_LOG_ERROR_ROOM_ID, "token": settings.MATRIX_TOKEN, "server_base_url": settings.MATRIX_SERVER_BASE_URL, @@ -133,6 +135,7 @@ def _get_config_dict(self, settings: Settings): # Send error to a Matrix server. If credentials are not set in settings, the handler will be disabled "formatter": "matrix", "class": "app.utils.loggers_tools.matrix_handler.MatrixHandler", + "background_tasks": BackgroundTasks, "room_id": settings.MATRIX_LOG_AMAP_ROOM_ID, "token": settings.MATRIX_TOKEN, "server_base_url": settings.MATRIX_SERVER_BASE_URL, diff --git a/app/utils/communication/matrix.py b/app/utils/communication/matrix.py index 566689fea8..c93232a34b 100644 --- a/app/utils/communication/matrix.py +++ b/app/utils/communication/matrix.py @@ -1,6 +1,6 @@ from typing import Any -import requests +import httpx from app.types.exceptions import MatrixRequestError, MatrixSendMessageError @@ -24,7 +24,7 @@ def __init__( self.access_token = token - def post( + async def post( self, url: str, json: dict[str, Any], @@ -43,15 +43,21 @@ def post( if "Authorization" not in headers: headers["Authorization"] = "Bearer " + self.access_token - response = requests.post(url, json=json, headers=headers, timeout=10) try: + async with httpx.AsyncClient() as client: + response = await client.post( + url, + json=json, + headers=headers, + timeout=10, + ) response.raise_for_status() - except requests.exceptions.HTTPError as err: + except httpx.RequestError as err: raise MatrixRequestError() from err return response.json() - def send_message(self, room_id: str, formatted_body: str) -> None: + async def send_message(self, room_id: str, formatted_body: str) -> None: """ Send a message to the room `room_id`. `formatted_body` can contain html formatted text @@ -71,6 +77,6 @@ def send_message(self, room_id: str, formatted_body: str) -> None: } try: - self.post(url, json=data, headers=None) + await self.post(url, json=data, headers=None) except MatrixRequestError as error: raise MatrixSendMessageError(room_id=room_id) from error diff --git a/app/utils/loggers_tools/matrix_handler.py b/app/utils/loggers_tools/matrix_handler.py index d95a2b6ae1..19e398af73 100644 --- a/app/utils/loggers_tools/matrix_handler.py +++ b/app/utils/loggers_tools/matrix_handler.py @@ -20,6 +20,7 @@ class MatrixHandler(StreamHandler): def __init__( self, + background_tasks: BackgroundTasks, room_id: str, token: str, server_base_url: str | None, @@ -29,6 +30,7 @@ def __init__( super().__init__() self.setLevel(level) + self.background_tasks = background_tasks self.room_id = room_id self.enabled = enabled if self.enabled: @@ -42,7 +44,11 @@ def emit(self, record): if self.enabled: msg = self.format(record) try: - self.matrix.send_message(self.room_id, msg) + self.background_tasks.add_task( + self.matrix.send_message, + room_id=self.room_id, + formatted_body=msg, + ) # We should catch and log any error, as Python may discarded them in production except Exception as err: # We use warning level so that the message is not sent to matrix again From 0b13517fa14ce5e275c5ca6fbfabd06d06dfc3d7 Mon Sep 17 00:00:00 2001 From: Marc-Andrieu Date: Tue, 14 Oct 2025 15:18:49 +0200 Subject: [PATCH 2/3] Remove `requests` from direct dependencies, promote `httpx` to common dependencies --- requirements-dev.txt | 1 - requirements.txt | 1 - 2 files changed, 2 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 083d0c399c..ecb2858d84 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -14,4 +14,3 @@ types-Authlib==1.5.0.20250516 types-fpdf2==2.8.3.20250516 types-psutil==7.0.0.20250601 types-redis==4.6.0.20241004 -types-requests==2.32.0.20250515 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index f09c236b9d..327938ff0d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,7 +28,6 @@ PyMuPDF==1.26.6 # PDF processing, imported as `fitz` pypdf==6.4.0 python-multipart==0.0.18 # a form data parser, as oauth flow requires form-data parameters redis==5.0.8 -requests==2.32.4 sqlalchemy-utils == 0.41.2 SQLAlchemy[asyncio]==2.0.44 # [asyncio] allows greenlet to be installed on Apple M1 devices. unidecode==1.3.8 From 5e16854a68cb14a616006c02ba26e2b40dea3fb1 Mon Sep 17 00:00:00 2001 From: Marc-Andrieu Date: Sat, 28 Mar 2026 16:32:26 +0100 Subject: [PATCH 3/3] Use httpx synchronously: no propagation of async/await pattern --- app/core/utils/log.py | 3 --- app/utils/communication/matrix.py | 19 +++++++++---------- app/utils/loggers_tools/matrix_handler.py | 8 +------- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/app/core/utils/log.py b/app/core/utils/log.py index 482d653202..07a74335e2 100644 --- a/app/core/utils/log.py +++ b/app/core/utils/log.py @@ -7,7 +7,6 @@ from typing import Any import uvicorn -from fastapi import BackgroundTasks from app.core.utils.config import Settings @@ -122,7 +121,6 @@ def _get_config_dict(self, settings: Settings): # Send error to a Matrix server. If credentials are not set in settings, the handler will be disabled "formatter": "matrix", "class": "app.utils.loggers_tools.matrix_handler.MatrixHandler", - "background_tasks": BackgroundTasks, "room_id": settings.MATRIX_LOG_ERROR_ROOM_ID, "token": settings.MATRIX_TOKEN, "server_base_url": settings.MATRIX_SERVER_BASE_URL, @@ -135,7 +133,6 @@ def _get_config_dict(self, settings: Settings): # Send error to a Matrix server. If credentials are not set in settings, the handler will be disabled "formatter": "matrix", "class": "app.utils.loggers_tools.matrix_handler.MatrixHandler", - "background_tasks": BackgroundTasks, "room_id": settings.MATRIX_LOG_AMAP_ROOM_ID, "token": settings.MATRIX_TOKEN, "server_base_url": settings.MATRIX_SERVER_BASE_URL, diff --git a/app/utils/communication/matrix.py b/app/utils/communication/matrix.py index c93232a34b..ca7711ebc6 100644 --- a/app/utils/communication/matrix.py +++ b/app/utils/communication/matrix.py @@ -24,7 +24,7 @@ def __init__( self.access_token = token - async def post( + def post( self, url: str, json: dict[str, Any], @@ -44,20 +44,19 @@ async def post( headers["Authorization"] = "Bearer " + self.access_token try: - async with httpx.AsyncClient() as client: - response = await client.post( - url, - json=json, - headers=headers, - timeout=10, - ) + response = httpx.post( + url, + json=json, + headers=headers, + timeout=10, + ) response.raise_for_status() except httpx.RequestError as err: raise MatrixRequestError() from err return response.json() - async def send_message(self, room_id: str, formatted_body: str) -> None: + def send_message(self, room_id: str, formatted_body: str) -> None: """ Send a message to the room `room_id`. `formatted_body` can contain html formatted text @@ -77,6 +76,6 @@ async def send_message(self, room_id: str, formatted_body: str) -> None: } try: - await self.post(url, json=data, headers=None) + self.post(url, json=data, headers=None) except MatrixRequestError as error: raise MatrixSendMessageError(room_id=room_id) from error diff --git a/app/utils/loggers_tools/matrix_handler.py b/app/utils/loggers_tools/matrix_handler.py index 19e398af73..d95a2b6ae1 100644 --- a/app/utils/loggers_tools/matrix_handler.py +++ b/app/utils/loggers_tools/matrix_handler.py @@ -20,7 +20,6 @@ class MatrixHandler(StreamHandler): def __init__( self, - background_tasks: BackgroundTasks, room_id: str, token: str, server_base_url: str | None, @@ -30,7 +29,6 @@ def __init__( super().__init__() self.setLevel(level) - self.background_tasks = background_tasks self.room_id = room_id self.enabled = enabled if self.enabled: @@ -44,11 +42,7 @@ def emit(self, record): if self.enabled: msg = self.format(record) try: - self.background_tasks.add_task( - self.matrix.send_message, - room_id=self.room_id, - formatted_body=msg, - ) + self.matrix.send_message(self.room_id, msg) # We should catch and log any error, as Python may discarded them in production except Exception as err: # We use warning level so that the message is not sent to matrix again