From 4a0646bb14d708349e543e9f8d274c088d7e1cc8 Mon Sep 17 00:00:00 2001 From: zosimovaa Date: Wed, 4 Mar 2026 12:17:07 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8F=20=D1=82=D1=80=D0=B0=D0=BD=D1=81=D0=BF=D0=BE?= =?UTF-8?q?=D1=80=D1=82=D0=B0=20=D0=B4=D0=BB=D1=8F=20=D1=82=D1=80=D0=B5?= =?UTF-8?q?=D0=B9=D1=81=D0=B8=D0=BD=D0=B3=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 37 +++++++++++++ pyproject.toml | 1 + src/app_runtime/tracing/__init__.py | 3 +- src/app_runtime/tracing/transport.py | 82 ++++++++++++++++++++++++++++ src/plba/__init__.py | 3 +- src/plba/tracing.py | 4 +- 6 files changed, 126 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4a6a614..99d6c8d 100644 --- a/README.md +++ b/README.md @@ -368,6 +368,8 @@ Control plane и HTTP-канал управления. pip install "plba @ git+ssh://git@git.lesha.spb.ru/alex/plba.git" ``` +При такой установке `pip` ставит не только сам пакет `plba`, но и все его зависимости, объявленные в [pyproject.toml](/Users/alex/Dev_projects_v2/apps/plba/pyproject.toml), например `fastapi`, `uvicorn` и `PyYAML`. + Если нужна установка из конкретной ветки: ```bash @@ -395,6 +397,41 @@ pip install --upgrade pip pip install "plba @ git+ssh://git@git.lesha.spb.ru/alex/plba.git" ``` +### Подключение `plba` в бизнес-приложении + +Чтобы при установке бизнес-приложения автоматически подтягивались зависимости `plba`, нужно добавить `plba` в зависимости самого бизнес-приложения как Git dependency. + +Пример для `requirements.txt`: + +```txt +plba @ git+ssh://git@git.lesha.spb.ru/alex/plba.git +``` + +Пример для `pyproject.toml`: + +```toml +[project] +dependencies = [ + "plba @ git+ssh://git@git.lesha.spb.ru/alex/plba.git", +] +``` + +Если бизнес-приложение собирается в Docker, достаточно чтобы на этапе сборки выполнялся обычный `pip install`, например: + +```dockerfile +COPY pyproject.toml . +RUN pip install . +``` + +или при использовании `requirements.txt`: + +```dockerfile +COPY requirements.txt . +RUN pip install -r requirements.txt +``` + +В обоих случаях `pip` установит `plba` из Git и автоматически подтянет его транзитивные зависимости. + ### Локальная разработка Если пакет нужно не только использовать, но и разрабатывать: diff --git a/pyproject.toml b/pyproject.toml index 5af9178..f43a464 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,7 @@ readme = "README.md" requires-python = ">=3.12" dependencies = [ "fastapi>=0.129.0", + "PyMySQL>=1.1", "PyYAML>=6.0.3", "uvicorn>=0.41.0", ] diff --git a/src/app_runtime/tracing/__init__.py b/src/app_runtime/tracing/__init__.py index 4048ccc..18c32cc 100644 --- a/src/app_runtime/tracing/__init__.py +++ b/src/app_runtime/tracing/__init__.py @@ -1,10 +1,11 @@ from app_runtime.contracts.trace import TraceContext, TraceContextRecord, TraceLogMessage, TraceTransport from app_runtime.tracing.service import TraceManager, TraceService from app_runtime.tracing.store import ActiveTraceContext, TraceContextStore -from app_runtime.tracing.transport import NoOpTraceTransport +from app_runtime.tracing.transport import MySqlTraceTransport, NoOpTraceTransport __all__ = [ "ActiveTraceContext", + "MySqlTraceTransport", "NoOpTraceTransport", "TraceContext", "TraceContextRecord", diff --git a/src/app_runtime/tracing/transport.py b/src/app_runtime/tracing/transport.py index 07416e7..c3b6db7 100644 --- a/src/app_runtime/tracing/transport.py +++ b/src/app_runtime/tracing/transport.py @@ -1,5 +1,9 @@ from __future__ import annotations +import json +from datetime import date, datetime + + from app_runtime.contracts.trace import TraceContextRecord, TraceLogMessage, TraceTransport @@ -9,3 +13,81 @@ class NoOpTraceTransport(TraceTransport): def write_message(self, record: TraceLogMessage) -> None: del record + + +class MySqlTraceTransport(TraceTransport): + def __init__( + self, + *, + host: str, + port: int, + database: str, + user: str, + password: str, + ) -> None: + self._host = host + self._port = port + self._database = database + self._user = user + self._password = password + + def write_context(self, record: TraceContextRecord) -> None: + query = """ + INSERT INTO trace_contexts (trace_id, parent_id, alias, type, event_time, attrs_json) + VALUES (%s, %s, %s, %s, %s, %s) + ON DUPLICATE KEY UPDATE + parent_id = VALUES(parent_id), + alias = VALUES(alias), + type = VALUES(type), + event_time = VALUES(event_time), + attrs_json = VALUES(attrs_json) + """ + params = ( + record.trace_id, + record.parent_id, + record.alias, + record.type, + record.event_time.replace(tzinfo=None), + self._dumps(record.attrs), + ) + self._execute(query, params) + + def write_message(self, record: TraceLogMessage) -> None: + query = """ + INSERT INTO trace_messages (trace_id, event_time, step, status, level, message, attrs_json) + VALUES (%s, %s, %s, %s, %s, %s, %s) + """ + params = ( + record.trace_id, + record.event_time.replace(tzinfo=None), + record.step, + record.status, + record.level, + record.message, + self._dumps(record.attrs), + ) + self._execute(query, params) + + def _execute(self, query: str, params: tuple[object, ...]) -> None: + import pymysql + + with pymysql.connect( + host=self._host, + port=self._port, + user=self._user, + password=self._password, + database=self._database, + charset="utf8mb4", + autocommit=True, + cursorclass=pymysql.cursors.DictCursor, + ) as connection: + with connection.cursor() as cursor: + cursor.execute(query, params) + + def _dumps(self, payload: dict[str, object]) -> str: + return json.dumps(payload, ensure_ascii=False, default=self._json_default) + + def _json_default(self, value: object) -> str: + if isinstance(value, (datetime, date)): + return value.isoformat() + return str(value) diff --git a/src/plba/__init__.py b/src/plba/__init__.py index 46f44cb..f8031c7 100644 --- a/src/plba/__init__.py +++ b/src/plba/__init__.py @@ -20,7 +20,7 @@ from plba.core import ConfigurationManager, RuntimeManager, ServiceContainer from plba.health import HealthRegistry from plba.logging import LogManager from plba.queue import InMemoryTaskQueue -from plba.tracing import NoOpTraceTransport, TraceService +from plba.tracing import MySqlTraceTransport, NoOpTraceTransport, TraceService from plba.workers import QueueWorker, WorkerSupervisor __all__ = [ @@ -38,6 +38,7 @@ __all__ = [ "HttpControlChannel", "InMemoryTaskQueue", "LogManager", + "MySqlTraceTransport", "NoOpTraceTransport", "QueueWorker", "RuntimeManager", diff --git a/src/plba/tracing.py b/src/plba/tracing.py index 73ee774..5d6a39f 100644 --- a/src/plba/tracing.py +++ b/src/plba/tracing.py @@ -1,4 +1,4 @@ from app_runtime.tracing.service import TraceService -from app_runtime.tracing.transport import NoOpTraceTransport +from app_runtime.tracing.transport import MySqlTraceTransport, NoOpTraceTransport -__all__ = ["NoOpTraceTransport", "TraceService"] +__all__ = ["MySqlTraceTransport", "NoOpTraceTransport", "TraceService"]