apitally 0.14.2__py3-none-any.whl → 0.14.4__py3-none-any.whl
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.
- apitally/client/client_asyncio.py +7 -8
- apitally/client/client_base.py +1 -0
- apitally/client/client_threading.py +7 -8
- apitally/starlette.py +12 -6
- {apitally-0.14.2.dist-info → apitally-0.14.4.dist-info}/METADATA +6 -5
- {apitally-0.14.2.dist-info → apitally-0.14.4.dist-info}/RECORD +8 -8
- {apitally-0.14.2.dist-info → apitally-0.14.4.dist-info}/WHEEL +1 -1
- {apitally-0.14.2.dist-info → apitally-0.14.4.dist-info}/licenses/LICENSE +0 -0
@@ -6,7 +6,7 @@ import random
|
|
6
6
|
import time
|
7
7
|
from contextlib import suppress
|
8
8
|
from functools import partial
|
9
|
-
from typing import Any, AsyncIterator, Dict, Optional,
|
9
|
+
from typing import Any, AsyncIterator, Dict, Optional, Union
|
10
10
|
from uuid import UUID
|
11
11
|
|
12
12
|
import backoff
|
@@ -40,7 +40,7 @@ class ApitallyClient(ApitallyClientBase):
|
|
40
40
|
self.proxy = proxy
|
41
41
|
self._stop_sync_loop = False
|
42
42
|
self._sync_loop_task: Optional[asyncio.Task] = None
|
43
|
-
self._sync_data_queue: asyncio.Queue[
|
43
|
+
self._sync_data_queue: asyncio.Queue[Dict[str, Any]] = asyncio.Queue()
|
44
44
|
self._set_startup_data_task: Optional[asyncio.Task] = None
|
45
45
|
|
46
46
|
def get_http_client(self) -> httpx.AsyncClient:
|
@@ -103,20 +103,19 @@ class ApitallyClient(ApitallyClientBase):
|
|
103
103
|
|
104
104
|
async def send_sync_data(self, client: httpx.AsyncClient) -> None:
|
105
105
|
data = self.get_sync_data()
|
106
|
-
self._sync_data_queue.put_nowait(
|
106
|
+
self._sync_data_queue.put_nowait(data)
|
107
107
|
|
108
108
|
i = 0
|
109
109
|
while not self._sync_data_queue.empty():
|
110
|
-
|
110
|
+
data = self._sync_data_queue.get_nowait()
|
111
111
|
try:
|
112
|
-
if
|
112
|
+
if time.time() - data["timestamp"] <= MAX_QUEUE_TIME:
|
113
113
|
if i > 0:
|
114
|
-
await asyncio.sleep(random.uniform(0.1, 0.
|
115
|
-
data["time_offset"] = time_offset
|
114
|
+
await asyncio.sleep(random.uniform(0.1, 0.5))
|
116
115
|
await self._send_sync_data(client, data)
|
117
116
|
i += 1
|
118
117
|
except httpx.HTTPError:
|
119
|
-
self._sync_data_queue.put_nowait(
|
118
|
+
self._sync_data_queue.put_nowait(data)
|
120
119
|
break
|
121
120
|
finally:
|
122
121
|
self._sync_data_queue.task_done()
|
apitally/client/client_base.py
CHANGED
@@ -89,6 +89,7 @@ class ApitallyClientBase(ABC):
|
|
89
89
|
|
90
90
|
def get_sync_data(self) -> Dict[str, Any]:
|
91
91
|
data = {
|
92
|
+
"timestamp": time.time(),
|
92
93
|
"requests": self.request_counter.get_and_reset_requests(),
|
93
94
|
"validation_errors": self.validation_error_counter.get_and_reset_validation_errors(),
|
94
95
|
"server_errors": self.server_error_counter.get_and_reset_server_errors(),
|
@@ -8,7 +8,7 @@ from functools import partial
|
|
8
8
|
from io import BufferedReader
|
9
9
|
from queue import Queue
|
10
10
|
from threading import Event, Thread
|
11
|
-
from typing import Any, Callable, Dict, Optional
|
11
|
+
from typing import Any, Callable, Dict, Optional
|
12
12
|
from uuid import UUID
|
13
13
|
|
14
14
|
import backoff
|
@@ -58,7 +58,7 @@ class ApitallyClient(ApitallyClientBase):
|
|
58
58
|
self.proxies = {"https": proxy} if proxy else None
|
59
59
|
self._thread: Optional[Thread] = None
|
60
60
|
self._stop_sync_loop = Event()
|
61
|
-
self._sync_data_queue: Queue[
|
61
|
+
self._sync_data_queue: Queue[Dict[str, Any]] = Queue()
|
62
62
|
|
63
63
|
def start_sync_loop(self) -> None:
|
64
64
|
self._stop_sync_loop.clear()
|
@@ -114,20 +114,19 @@ class ApitallyClient(ApitallyClientBase):
|
|
114
114
|
|
115
115
|
def send_sync_data(self, session: requests.Session) -> None:
|
116
116
|
data = self.get_sync_data()
|
117
|
-
self._sync_data_queue.put_nowait(
|
117
|
+
self._sync_data_queue.put_nowait(data)
|
118
118
|
|
119
119
|
i = 0
|
120
120
|
while not self._sync_data_queue.empty():
|
121
|
-
|
121
|
+
data = self._sync_data_queue.get_nowait()
|
122
122
|
try:
|
123
|
-
if
|
123
|
+
if time.time() - data["timestamp"] <= MAX_QUEUE_TIME:
|
124
124
|
if i > 0:
|
125
|
-
time.sleep(random.uniform(0.1, 0.
|
126
|
-
data["time_offset"] = time_offset
|
125
|
+
time.sleep(random.uniform(0.1, 0.5))
|
127
126
|
self._send_sync_data(session, data)
|
128
127
|
i += 1
|
129
128
|
except requests.RequestException:
|
130
|
-
self._sync_data_queue.put_nowait(
|
129
|
+
self._sync_data_queue.put_nowait(data)
|
131
130
|
break
|
132
131
|
finally:
|
133
132
|
self._sync_data_queue.task_done()
|
apitally/starlette.py
CHANGED
@@ -222,12 +222,18 @@ class ApitallyMiddleware:
|
|
222
222
|
},
|
223
223
|
)
|
224
224
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
if
|
230
|
-
|
225
|
+
def get_path(self, request: Request, routes: Optional[list[BaseRoute]] = None) -> Optional[str]:
|
226
|
+
if routes is None:
|
227
|
+
routes = request.app.routes
|
228
|
+
for route in routes:
|
229
|
+
if hasattr(route, "routes"):
|
230
|
+
path = self.get_path(request, routes=route.routes)
|
231
|
+
if path is not None:
|
232
|
+
return path
|
233
|
+
elif hasattr(route, "path"):
|
234
|
+
match, _ = route.matches(request.scope)
|
235
|
+
if match == Match.FULL:
|
236
|
+
return request.scope.get("root_path", "") + route.path
|
231
237
|
return None
|
232
238
|
|
233
239
|
def get_consumer(self, request: Request) -> Optional[ApitallyConsumer]:
|
@@ -1,12 +1,13 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: apitally
|
3
|
-
Version: 0.14.
|
3
|
+
Version: 0.14.4
|
4
4
|
Summary: Simple API monitoring & analytics for REST APIs built with FastAPI, Flask, Django, Starlette and Litestar.
|
5
5
|
Project-URL: Homepage, https://apitally.io
|
6
6
|
Project-URL: Documentation, https://docs.apitally.io
|
7
7
|
Project-URL: Repository, https://github.com/apitally/apitally-py
|
8
8
|
Author-email: Apitally <hello@apitally.io>
|
9
9
|
License: MIT License
|
10
|
+
License-File: LICENSE
|
10
11
|
Classifier: Development Status :: 5 - Production/Stable
|
11
12
|
Classifier: Environment :: Web Environment
|
12
13
|
Classifier: Framework :: Django
|
@@ -68,9 +69,9 @@ Description-Content-Type: text/markdown
|
|
68
69
|
</picture>
|
69
70
|
</p>
|
70
71
|
|
71
|
-
<p align="center"><b>
|
72
|
+
<p align="center"><b>Analytics, logging & monitoring for REST APIs.</b></p>
|
72
73
|
|
73
|
-
<p align="center"><i>Apitally
|
74
|
+
<p align="center"><i>Apitally helps you understand how your APIs are being used and alerts you when things go wrong.<br>It's super easy to use and designed to protect your data privacy.</i></p>
|
74
75
|
|
75
76
|
<p align="center">🔗 <b><a href="https://apitally.io" target="_blank">apitally.io</a></b></p>
|
76
77
|
|
@@ -100,7 +101,7 @@ the 📚 [documentation](https://docs.apitally.io).
|
|
100
101
|
## Key features
|
101
102
|
|
102
103
|
- Middleware for different frameworks to capture metadata about API endpoints,
|
103
|
-
requests and responses
|
104
|
+
requests and responses
|
104
105
|
- Non-blocking clients that aggregate and send captured data to Apitally in
|
105
106
|
regular intervals
|
106
107
|
|
@@ -7,18 +7,18 @@ apitally/fastapi.py,sha256=IfKfgsmIY8_AtnuMTW2sW4qnkya61CAE2vBoIpcc9tk,169
|
|
7
7
|
apitally/flask.py,sha256=Th5LsMsTKkWERPrKfSWPhzrp99tg0pDtKXgtlVLx3eo,9279
|
8
8
|
apitally/litestar.py,sha256=hAH2-OVVXBDVY8LopfIGv30yYwi-71tSEsKd6648CYc,13098
|
9
9
|
apitally/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
apitally/starlette.py,sha256=
|
10
|
+
apitally/starlette.py,sha256=VaT4-QVSYC0YX1U5kVI-dGROEd64IbjYU5lx5N16yf8,12852
|
11
11
|
apitally/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
-
apitally/client/client_asyncio.py,sha256=
|
13
|
-
apitally/client/client_base.py,sha256=
|
14
|
-
apitally/client/client_threading.py,sha256=
|
12
|
+
apitally/client/client_asyncio.py,sha256=2RibaadLAEdl2i0yAb4dSEDq_r46w6HPFpZVzvt59aQ,6941
|
13
|
+
apitally/client/client_base.py,sha256=dXsaB7scd0wCd_DcdiUvNlDLZ2pWbWsGDnJ4fLYvJGg,3771
|
14
|
+
apitally/client/client_threading.py,sha256=Y8LlA_8LFHAuXym-T4bY_XFqXxiIDw9qFqO6K3FbZy0,7356
|
15
15
|
apitally/client/consumers.py,sha256=w_AFQhVgdtJVt7pVySBvSZwQg-2JVqmD2JQtVBoMkus,2626
|
16
16
|
apitally/client/logging.py,sha256=QMsKIIAFo92PNBUleeTgsrsQa7SEal-oJa1oOHUr1wI,507
|
17
17
|
apitally/client/request_logging.py,sha256=5i7Gv4yP7iq4tBgw-ppkhlZd_OwMc719ZvWEm16TCvg,13047
|
18
18
|
apitally/client/requests.py,sha256=RdJyvIqQGVHvS-wjpAPUwcO7byOJ6jO8dYqNTU2Furg,3685
|
19
19
|
apitally/client/server_errors.py,sha256=axEhOxqV5SWjk0QCZTLVv2UMIaTfqPc81Typ4DXt66A,4646
|
20
20
|
apitally/client/validation_errors.py,sha256=6G8WYWFgJs9VH9swvkPXJGuOJgymj5ooWA9OwjUTbuM,1964
|
21
|
-
apitally-0.14.
|
22
|
-
apitally-0.14.
|
23
|
-
apitally-0.14.
|
24
|
-
apitally-0.14.
|
21
|
+
apitally-0.14.4.dist-info/METADATA,sha256=bE7HFJBsn6IamIlJvehQhTd46k3Lx67imr2Yi7eM_PA,7570
|
22
|
+
apitally-0.14.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
23
|
+
apitally-0.14.4.dist-info/licenses/LICENSE,sha256=vbLzC-4TddtXX-_AFEBKMYWRlxC_MN0g66QhPxo8PgY,1065
|
24
|
+
apitally-0.14.4.dist-info/RECORD,,
|
File without changes
|