fast-telemetry 0.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fast_telemetry-0.0.0/.gitignore +10 -0
- fast_telemetry-0.0.0/.gitlab-ci.yml +33 -0
- fast_telemetry-0.0.0/PKG-INFO +11 -0
- fast_telemetry-0.0.0/README.md +199 -0
- fast_telemetry-0.0.0/examples/.gitignore +1 -0
- fast_telemetry-0.0.0/examples/app_example/.gitignore +2 -0
- fast_telemetry-0.0.0/examples/app_example/_grafana-data/grafana.db +0 -0
- fast_telemetry-0.0.0/examples/app_example/apps/fastapi/.dockerignore +1 -0
- fast_telemetry-0.0.0/examples/app_example/apps/fastapi/Dockerfile +10 -0
- fast_telemetry-0.0.0/examples/app_example/apps/fastapi/fast_telemetry-0.1.0-py3-none-any.whl +0 -0
- fast_telemetry-0.0.0/examples/app_example/apps/fastapi/main.py +73 -0
- fast_telemetry-0.0.0/examples/app_example/apps/fastapi/requirements.txt +6 -0
- fast_telemetry-0.0.0/examples/app_example/apps/faststream/.dockerignore +1 -0
- fast_telemetry-0.0.0/examples/app_example/apps/faststream/Dockerfile +10 -0
- fast_telemetry-0.0.0/examples/app_example/apps/faststream/fast_telemetry-0.1.0-py3-none-any.whl +0 -0
- fast_telemetry-0.0.0/examples/app_example/apps/faststream/main.py +81 -0
- fast_telemetry-0.0.0/examples/app_example/apps/faststream/requirements.txt +5 -0
- fast_telemetry-0.0.0/examples/app_example/apps/post_init/.dockerignore +1 -0
- fast_telemetry-0.0.0/examples/app_example/apps/post_init/Dockerfile +11 -0
- fast_telemetry-0.0.0/examples/app_example/apps/post_init/main.py +31 -0
- fast_telemetry-0.0.0/examples/app_example/apps/post_init/requirements.txt +2 -0
- fast_telemetry-0.0.0/examples/app_example/apps/worker/.dockerignore +1 -0
- fast_telemetry-0.0.0/examples/app_example/apps/worker/Dockerfile +10 -0
- fast_telemetry-0.0.0/examples/app_example/apps/worker/fast_telemetry-0.1.0-py3-none-any.whl +0 -0
- fast_telemetry-0.0.0/examples/app_example/apps/worker/main.py +46 -0
- fast_telemetry-0.0.0/examples/app_example/apps/worker/requirements.txt +3 -0
- fast_telemetry-0.0.0/examples/app_example/docker-compose.yaml +95 -0
- fast_telemetry-0.0.0/examples/app_example/env.example +6 -0
- fast_telemetry-0.0.0/examples/app_example/prometheus/prometheus.yaml +10 -0
- fast_telemetry-0.0.0/examples/dishka_example.py +19 -0
- fast_telemetry-0.0.0/grafana/fastapi.json +1055 -0
- fast_telemetry-0.0.0/grafana/faststream.json +1821 -0
- fast_telemetry-0.0.0/grafana/fasttelemetry.json +527 -0
- fast_telemetry-0.0.0/pyproject.toml +36 -0
- fast_telemetry-0.0.0/requirements.txt +4 -0
- fast_telemetry-0.0.0/setup.cfg +4 -0
- fast_telemetry-0.0.0/src/__init__.py +0 -0
- fast_telemetry-0.0.0/src/fast_telemetry/__init__.py +10 -0
- fast_telemetry-0.0.0/src/fast_telemetry/core.py +232 -0
- fast_telemetry-0.0.0/src/fast_telemetry/integrations/__init__.py +0 -0
- fast_telemetry-0.0.0/src/fast_telemetry/integrations/fastapi.py +30 -0
- fast_telemetry-0.0.0/src/fast_telemetry/integrations/faststream.py +102 -0
- fast_telemetry-0.0.0/src/fast_telemetry/integrations/worker.py +124 -0
- fast_telemetry-0.0.0/src/fast_telemetry.egg-info/PKG-INFO +11 -0
- fast_telemetry-0.0.0/src/fast_telemetry.egg-info/SOURCES.txt +46 -0
- fast_telemetry-0.0.0/src/fast_telemetry.egg-info/dependency_links.txt +1 -0
- fast_telemetry-0.0.0/src/fast_telemetry.egg-info/requires.txt +8 -0
- fast_telemetry-0.0.0/src/fast_telemetry.egg-info/top_level.txt +2 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
stages:
|
|
2
|
+
- build
|
|
3
|
+
- deploy
|
|
4
|
+
|
|
5
|
+
variables:
|
|
6
|
+
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
|
7
|
+
|
|
8
|
+
cache:
|
|
9
|
+
paths:
|
|
10
|
+
- .cache/pip
|
|
11
|
+
|
|
12
|
+
build_package:
|
|
13
|
+
stage: build
|
|
14
|
+
image: python:3.11
|
|
15
|
+
script:
|
|
16
|
+
- pip install build
|
|
17
|
+
- python -m build
|
|
18
|
+
artifacts:
|
|
19
|
+
paths:
|
|
20
|
+
- dist/
|
|
21
|
+
expire_in: 3 hour
|
|
22
|
+
only:
|
|
23
|
+
- tags
|
|
24
|
+
|
|
25
|
+
upload_to_pypi:
|
|
26
|
+
stage: deploy
|
|
27
|
+
image: python:3.11
|
|
28
|
+
needs: ["build_package"]
|
|
29
|
+
script:
|
|
30
|
+
- pip install twine
|
|
31
|
+
- twine upload dist/*
|
|
32
|
+
only:
|
|
33
|
+
- tags
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fast-telemetry
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Internal standardized metrics library
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: prometheus-client~=0.21.0
|
|
7
|
+
Provides-Extra: web
|
|
8
|
+
Requires-Dist: fastapi~=0.115.0; extra == "web"
|
|
9
|
+
Requires-Dist: prometheus-fastapi-instrumentator~=7.1.0; extra == "web"
|
|
10
|
+
Provides-Extra: stream
|
|
11
|
+
Requires-Dist: faststream~=0.6.0; extra == "stream"
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# 📊 fast-telemetry
|
|
2
|
+
|
|
3
|
+
**Production-ready Prometheus integration for FastAPI, FastStream, and Python Workers.**
|
|
4
|
+
|
|
5
|
+
`fast-telemetry` — это "клей", который объединяет метрики вашего микросервиса в единый стандарт. Библиотека навязывает
|
|
6
|
+
правильные практики (единые метки `env`, `version`, `service`) и предоставляет удобные абстракции для HTTP-сервисов,
|
|
7
|
+
обработчиков очередей и периодических задач.
|
|
8
|
+
|
|
9
|
+
### 🌟 Особенности
|
|
10
|
+
|
|
11
|
+
* **Единый стандарт:** Автоматически добавляет метки `service`, `env`, `version` ко всем метрикам.
|
|
12
|
+
* **FastAPI:** Автоконфигурация через `Instrumentator`, исключение служебных ручек (`/metrics` и других переданных).
|
|
13
|
+
* **FastStream:** Поддержка RabbitMQ, Kafka, Redis, NATS "из коробки" (автоопределение драйвера).
|
|
14
|
+
* **Workers & Cron:**
|
|
15
|
+
* Поддержка Long-running процессов (daemon thread server).
|
|
16
|
+
* Поддержка Batch-jobs (Pushgateway) через **универсальный контекстный менеджер** (sync/async).
|
|
17
|
+
* **Декораторы:** Удобные `@measure_task` и `@track_exception`, которые сами понимают, синхронная функция или
|
|
18
|
+
асинхронная.
|
|
19
|
+
* **Безопасность типов:** Полная типизация (mypy strict).
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
### 📦 Установка
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install fast-telemetry
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
### 🚀 Quick Start
|
|
32
|
+
|
|
33
|
+
#### 1. Core Concepts
|
|
34
|
+
|
|
35
|
+
В основе всего лежит класс `PrometheusMetrics` (или его наследники). Вы инициализируете его один раз при старте
|
|
36
|
+
приложения.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from fast_telemetry import PrometheusMetrics
|
|
40
|
+
|
|
41
|
+
metrics = PrometheusMetrics(
|
|
42
|
+
service_name="payment_service",
|
|
43
|
+
version="1.0.4",
|
|
44
|
+
env="production"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Использование в коде
|
|
48
|
+
metrics.inc_error("validation_error")
|
|
49
|
+
|
|
50
|
+
with metrics.timer("db_query", long_task=False):
|
|
51
|
+
# ... logic ...
|
|
52
|
+
pass
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### 2. FastAPI Integration
|
|
56
|
+
|
|
57
|
+
Автоматически добавляет эндпоинт `/metrics` и собирает RED-метрики (Requests, Errors, Duration).
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from fastapi import FastAPI
|
|
61
|
+
from fast_telemetry import PrometheusMetrics, setup_fastapi_metrics
|
|
62
|
+
|
|
63
|
+
app = FastAPI()
|
|
64
|
+
metrics = PrometheusMetrics(service_name="api_core", env="dev", version="1.2.3")
|
|
65
|
+
|
|
66
|
+
# Настройка (исключает /docs, /openapi.json + readiness)
|
|
67
|
+
setup_fastapi_metrics(app, metrics, excluded_routes=["/readiness"])
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@app.get("/")
|
|
71
|
+
@metrics.measure_task("root_handler", long_task=False)
|
|
72
|
+
async def root():
|
|
73
|
+
return {"status": "ok"}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
#### 3. FastStream Integration
|
|
77
|
+
|
|
78
|
+
Поддерживает автоматическое определение брокера (Rabbit/Kafka/Redis/NATS) и инъекцию middleware.
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from faststream.asgi import AsgiFastStream, make_ping_asgi
|
|
82
|
+
from faststream.rabbit import RabbitBroker
|
|
83
|
+
from fast_telemetry import PrometheusMetrics, setup_faststream_metrics
|
|
84
|
+
|
|
85
|
+
broker = RabbitBroker("amqp://guest:guest@localhost:5672/")
|
|
86
|
+
app = AsgiFastStream(
|
|
87
|
+
broker,
|
|
88
|
+
asgi_routes=[
|
|
89
|
+
("/health", make_ping_asgi(broker))
|
|
90
|
+
],
|
|
91
|
+
lifespan=None,
|
|
92
|
+
asyncapi_path="/docs",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
metrics = PrometheusMetrics(service_name="worker_core", env="dev")
|
|
96
|
+
setup_faststream_metrics(app, metrics)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### 4. Workers & Cron Jobs
|
|
100
|
+
|
|
101
|
+
Для скриптов без веб-сервера. Поддерживает два режима:
|
|
102
|
+
|
|
103
|
+
1. **Daemon Server** (для бесконечных циклов).
|
|
104
|
+
2. **Push Gateway** (для скриптов, запускаемых по расписанию).
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
import asyncio
|
|
108
|
+
from fast_telemetry import WorkerMetrics
|
|
109
|
+
|
|
110
|
+
metrics = WorkerMetrics(service_name="nightly_job", env="prod")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# --- Вариант А: Long-running worker ---
|
|
114
|
+
def run_worker():
|
|
115
|
+
# Запускает HTTP сервер в фоновом потоке
|
|
116
|
+
metrics.start_server(port=8000)
|
|
117
|
+
while True:
|
|
118
|
+
process_data()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# --- Вариант Б: Batch Job (Cron) ---
|
|
122
|
+
# Универсальный трекер (работает и с with, и с async with)
|
|
123
|
+
async def run_batch_job():
|
|
124
|
+
# Автоматически отправит метрики в Pushgateway при выходе из блока
|
|
125
|
+
# Группирует по instance_id, чтобы воркеры не перезатирали друг друга
|
|
126
|
+
async with metrics.track_job(gateway_url="http://pushgateway:9091", timeout=5.0):
|
|
127
|
+
await do_heavy_calculation()
|
|
128
|
+
metrics.inc_error("calc_error")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if __name__ == "__main__":
|
|
132
|
+
asyncio.run(run_batch_job())
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
### 🛠 Декораторы
|
|
138
|
+
|
|
139
|
+
Библиотека предоставляет "умные" декораторы, которые работают с sync и async функциями прозрачно.
|
|
140
|
+
|
|
141
|
+
**`@track_exception()`**
|
|
142
|
+
Считает количество исключений. Использует имя класса исключения как метку.
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
@metrics.track_exception
|
|
146
|
+
async def dangerous_operation():
|
|
147
|
+
raise ValueError("Oops")
|
|
148
|
+
# Увеличит счетчик fasttelemetry_business_errors_total{error_type="ValueError", ...}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**`@measure_task("task_name")`**
|
|
152
|
+
Замеряет время выполнения функции (Histogram).
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
@metrics.measure_task("image_processing")
|
|
156
|
+
def process_image(img):
|
|
157
|
+
time.sleep(1)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### 📊 Grafana & Prometheus
|
|
163
|
+
|
|
164
|
+
Библиотека экспортирует следующие базовые метрики:
|
|
165
|
+
|
|
166
|
+
1. **`fasttelemetry_app_info`** (Gauge) — всегда `1`. Содержит метки `version`, `env`, `service`. Используйте для
|
|
167
|
+
аннотаций деплоев на графиках.
|
|
168
|
+
2. **`fasttelemetry_business_errors_total`** (Counter) — счетчик ошибок бизнес-логики.
|
|
169
|
+
3. **`fasttelemetry_task_processing_seconds`** (Histogram) — время выполнения внутренних задач.
|
|
170
|
+
4. **`http_requests_...`** — [fastapi.json](grafana/fastapi.json) (если используется FastAPI).
|
|
171
|
+
5. **`faststream_...`** — [faststream.json](grafana/faststream.json) (если используется FastStream).
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 📈 Base Metrics & Grafana Dashboard
|
|
176
|
+
|
|
177
|
+
Библиотека предоставляет "Ready-to-use" набор метрик
|
|
178
|
+
|
|
179
|
+
### Metric Schema
|
|
180
|
+
|
|
181
|
+
| Metric Name | Type | Labels | Description |
|
|
182
|
+
|----------------------------------------------|-----------|-----------------------------|----------------------------------------------------------------------------------|
|
|
183
|
+
| `fasttelemetry_app_info` | Gauge | `env`, `service`, `version` | Информация о запущенном инстансе. Всегда `1`. |
|
|
184
|
+
| `fasttelemetry_business_errors_total` | Counter | `error_type` | Количество ошибок бизнес-логики (пойманных через `track_exception` или вручную). |
|
|
185
|
+
| `fasttelemetry_task_processing_seconds` | Histogram | `task_type` | Время выполнения задач (обернутых в `@measure_task` или `timer`). |
|
|
186
|
+
| `fasttelemetry_long_task_processing_seconds` | Histogram | `task_type` | Время выполнения долгих задач (обернутых в `@measure_task` или `timer`). |
|
|
187
|
+
|
|
188
|
+
### ⚙️ Configuration
|
|
189
|
+
|
|
190
|
+
Вы можете передать параметры явно в `__init__` или использовать переменные окружения:
|
|
191
|
+
|
|
192
|
+
| Environment Variable | Description | Default |
|
|
193
|
+
|----------------------|----------------------------|-----------|
|
|
194
|
+
| `APP_ENV` | Окружение (prod/dev/stage) | `dev` |
|
|
195
|
+
| `APP_VERSION` | Версия приложения | `unknown` |
|
|
196
|
+
|
|
197
|
+
### 🖥️ Grafana Dashboard JSON
|
|
198
|
+
|
|
199
|
+
Дашборды можно импортировать из [папки](grafana)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Dockerfile
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
FROM python:3.11-alpine
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
RUN apk add --update helm && \
|
|
4
|
+
adduser -D -u 1000 appuser
|
|
5
|
+
|
|
6
|
+
COPY --chown=appuser:appuser . .
|
|
7
|
+
RUN pip install --upgrade pip \
|
|
8
|
+
&& pip install --root-user-action=ignore --no-cache-dir -r requirements.txt
|
|
9
|
+
USER appuser
|
|
10
|
+
CMD ["python", "-m", "main"]
|
|
Binary file
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import random
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
|
+
|
|
5
|
+
import uvicorn
|
|
6
|
+
from fastapi import FastAPI
|
|
7
|
+
from fast_telemetry.core import PrometheusMetrics
|
|
8
|
+
from fast_telemetry.integrations.fastapi import setup_fastapi_metrics
|
|
9
|
+
from starlette import status
|
|
10
|
+
|
|
11
|
+
service_name = "demo_service"
|
|
12
|
+
env = "production"
|
|
13
|
+
version = "1.0.0"
|
|
14
|
+
|
|
15
|
+
metrics = PrometheusMetrics(service_name=service_name, env=env, version=version)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@metrics.measure_task("heavy_calculation", long_task=True)
|
|
19
|
+
async def heavy_task():
|
|
20
|
+
await asyncio.sleep(random.uniform(1, 5))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@metrics.track_exception
|
|
24
|
+
async def risky_task():
|
|
25
|
+
if random.random() < 0.3:
|
|
26
|
+
raise PaymentError("Payment declined")
|
|
27
|
+
if random.random() < 0.1:
|
|
28
|
+
raise DatabaseError("Connection lost")
|
|
29
|
+
await asyncio.sleep(0.05)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def traffic_generator():
|
|
33
|
+
print("🚀 Traffic generator started...")
|
|
34
|
+
while True:
|
|
35
|
+
try:
|
|
36
|
+
await heavy_task()
|
|
37
|
+
await risky_task()
|
|
38
|
+
except Exception as ex:
|
|
39
|
+
print(f"Error: {ex}")
|
|
40
|
+
await asyncio.sleep(5)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@asynccontextmanager
|
|
44
|
+
async def lifespan(_: FastAPI):
|
|
45
|
+
asyncio.create_task(traffic_generator())
|
|
46
|
+
yield
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
app = FastAPI(title=service_name, lifespan=lifespan, version=version)
|
|
50
|
+
|
|
51
|
+
setup_fastapi_metrics(app, metrics)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class PaymentError(Exception):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class DatabaseError(Exception):
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@app.post("/foo")
|
|
63
|
+
async def foo(message: dict) -> None:
|
|
64
|
+
print(f"Incoming message: {message}")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@app.get("/health", status_code=status.HTTP_204_NO_CONTENT)
|
|
68
|
+
async def health() -> None:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if __name__ == "__main__":
|
|
73
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Dockerfile
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
FROM python:3.11-alpine
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
RUN apk add --update helm && \
|
|
4
|
+
adduser -D -u 1000 appuser
|
|
5
|
+
|
|
6
|
+
COPY --chown=appuser:appuser . .
|
|
7
|
+
RUN pip install --upgrade pip \
|
|
8
|
+
&& pip install --root-user-action=ignore --no-cache-dir -r requirements.txt
|
|
9
|
+
USER appuser
|
|
10
|
+
CMD ["python", "-m", "main"]
|
fast_telemetry-0.0.0/examples/app_example/apps/faststream/fast_telemetry-0.1.0-py3-none-any.whl
ADDED
|
Binary file
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import random
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
5
|
+
|
|
6
|
+
import uvicorn
|
|
7
|
+
from faststream.asgi import make_ping_asgi, AsgiFastStream
|
|
8
|
+
from faststream.rabbit import RabbitBroker
|
|
9
|
+
|
|
10
|
+
from fast_telemetry.core import PrometheusMetrics
|
|
11
|
+
from fast_telemetry.integrations.faststream import setup_faststream_metrics
|
|
12
|
+
|
|
13
|
+
service_name = "foo_service"
|
|
14
|
+
env = "dev"
|
|
15
|
+
version = "1.2.3"
|
|
16
|
+
queue = "foo"
|
|
17
|
+
metrics = PrometheusMetrics(service_name=service_name, version=version, env=env)
|
|
18
|
+
|
|
19
|
+
url = (
|
|
20
|
+
f"amqp://{os.getenv('RABBITMQ_DEFAULT_USER')}:{os.getenv('RABBITMQ_DEFAULT_PASS')}"
|
|
21
|
+
f"@{os.getenv('RABBITMQ_DEFAULT_HOST')}:{os.getenv('RABBITMQ_DEFAULT_PORT')}/"
|
|
22
|
+
)
|
|
23
|
+
broker = RabbitBroker(url)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@metrics.measure_task("heavy_calculation")
|
|
27
|
+
async def heavy_task():
|
|
28
|
+
await asyncio.sleep(random.uniform(0.1, 0.5))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@metrics.track_exception
|
|
32
|
+
async def risky_task():
|
|
33
|
+
if random.random() < 0.3:
|
|
34
|
+
raise PaymentError("Payment declined")
|
|
35
|
+
if random.random() < 0.1:
|
|
36
|
+
raise DatabaseError("Connection lost")
|
|
37
|
+
await asyncio.sleep(0.05)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def traffic_generator():
|
|
41
|
+
print("🚀 Traffic generator started...")
|
|
42
|
+
while True:
|
|
43
|
+
try:
|
|
44
|
+
await heavy_task()
|
|
45
|
+
await risky_task()
|
|
46
|
+
except Exception as ex:
|
|
47
|
+
print(f"Error: {ex}")
|
|
48
|
+
await asyncio.sleep(5)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@asynccontextmanager
|
|
52
|
+
async def lifespan():
|
|
53
|
+
asyncio.create_task(traffic_generator())
|
|
54
|
+
yield
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
app = AsgiFastStream(
|
|
58
|
+
broker, asgi_routes=[("/health", make_ping_asgi(broker))], asyncapi_path="/docs", lifespan=lifespan
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
setup_faststream_metrics(app, metrics)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class PaymentError(Exception):
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class DatabaseError(Exception):
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@broker.subscriber(queue)
|
|
74
|
+
async def foo(msg: dict) -> dict:
|
|
75
|
+
print("incoming message:", msg)
|
|
76
|
+
await broker.publish({"some_number": random.randint(1, 100)}, "delete_me")
|
|
77
|
+
return {"message": msg}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Dockerfile
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
FROM python:3.11-alpine
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
RUN apk add --update helm && \
|
|
4
|
+
adduser -D -u 1000 appuser
|
|
5
|
+
|
|
6
|
+
COPY --chown=appuser:appuser requirements.txt .
|
|
7
|
+
RUN pip install --upgrade pip \
|
|
8
|
+
&& pip install --root-user-action=ignore --no-cache-dir -r requirements.txt
|
|
9
|
+
COPY --chown=appuser:appuser . .
|
|
10
|
+
USER appuser
|
|
11
|
+
CMD ["python", "-m", "main"]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from faststream.rabbit import RabbitBroker
|
|
5
|
+
from httpx import AsyncClient
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def main():
|
|
9
|
+
queue = "foo"
|
|
10
|
+
broker_url = (
|
|
11
|
+
f"amqp://{os.getenv('RABBITMQ_DEFAULT_USER')}:{os.getenv('RABBITMQ_DEFAULT_PASS')}"
|
|
12
|
+
f"@{os.getenv('RABBITMQ_DEFAULT_HOST')}:{os.getenv('RABBITMQ_DEFAULT_PORT')}/"
|
|
13
|
+
)
|
|
14
|
+
broker = RabbitBroker(broker_url)
|
|
15
|
+
fastapi_url = os.getenv("FASTAPI_URL")
|
|
16
|
+
async with AsyncClient(base_url=fastapi_url) as client:
|
|
17
|
+
async with broker:
|
|
18
|
+
await broker.start()
|
|
19
|
+
i = 0
|
|
20
|
+
while True:
|
|
21
|
+
message = {"Hello": "World", "message_number": i}
|
|
22
|
+
print("Send message", message)
|
|
23
|
+
await client.post("/foo", json=message)
|
|
24
|
+
await broker.publish(message, queue)
|
|
25
|
+
i += 1
|
|
26
|
+
print("Sleep...")
|
|
27
|
+
await asyncio.sleep(1)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
asyncio.run(main())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Dockerfile
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
FROM python:3.11-alpine
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
RUN apk add --update helm && \
|
|
4
|
+
adduser -D -u 1000 appuser
|
|
5
|
+
|
|
6
|
+
COPY --chown=appuser:appuser . .
|
|
7
|
+
RUN pip install --upgrade pip \
|
|
8
|
+
&& pip install --root-user-action=ignore --no-cache-dir -r requirements.txt
|
|
9
|
+
USER appuser
|
|
10
|
+
CMD ["python", "-m", "main"]
|
|
Binary file
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import random
|
|
3
|
+
|
|
4
|
+
from fast_telemetry.integrations.worker import WorkerMetrics
|
|
5
|
+
|
|
6
|
+
metrics = WorkerMetrics(service_name="nightly_job", env="prod")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PaymentError(Exception):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DatabaseError(Exception):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@metrics.measure_task("heavy_calculation")
|
|
18
|
+
async def heavy_task():
|
|
19
|
+
await asyncio.sleep(random.uniform(0.1, 0.5))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@metrics.track_exception
|
|
23
|
+
async def risky_task():
|
|
24
|
+
if random.random() < 0.3:
|
|
25
|
+
raise PaymentError("Payment declined")
|
|
26
|
+
if random.random() < 0.1:
|
|
27
|
+
raise DatabaseError("Connection lost")
|
|
28
|
+
await asyncio.sleep(0.05)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def run_worker():
|
|
32
|
+
metrics.start_server(port=8000)
|
|
33
|
+
while True:
|
|
34
|
+
await risky_task()
|
|
35
|
+
await heavy_task()
|
|
36
|
+
await asyncio.sleep(5)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# async def run_batch_job():
|
|
40
|
+
# async with metrics.track_job(gateway_url="http://pushgateway:9091"):
|
|
41
|
+
# await do_heavy_calculation()
|
|
42
|
+
# metrics.inc_error("calc_error")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
asyncio.run(run_worker())
|