apitally 0.8.0__py3-none-any.whl → 0.9.0__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/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.8.0"
1
+ __version__ = "0.9.0"
apitally/client/base.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
3
4
  import contextlib
4
5
  import os
5
6
  import re
@@ -243,6 +244,7 @@ class ServerError:
243
244
  class ServerErrorCounter:
244
245
  def __init__(self) -> None:
245
246
  self.error_counts: Counter[ServerError] = Counter()
247
+ self.sentry_event_ids: Dict[ServerError, str] = {}
246
248
  self._lock = threading.Lock()
247
249
 
248
250
  def add_server_error(self, consumer: Optional[str], method: str, path: str, exception: BaseException) -> None:
@@ -259,6 +261,36 @@ class ServerErrorCounter:
259
261
  traceback=self._get_truncated_exception_traceback(exception),
260
262
  )
261
263
  self.error_counts[server_error] += 1
264
+ self.capture_sentry_event_id(server_error)
265
+
266
+ def capture_sentry_event_id(self, server_error: ServerError) -> None:
267
+ try:
268
+ from sentry_sdk.hub import Hub
269
+ from sentry_sdk.scope import Scope
270
+ except ImportError:
271
+ return # pragma: no cover
272
+ if not hasattr(Scope, "get_isolation_scope") or not hasattr(Scope, "last_event_id"):
273
+ # sentry-sdk < 2.2.0 is not supported
274
+ return # pragma: no cover
275
+ if Hub.current.client is None:
276
+ return # sentry-sdk not initialized
277
+
278
+ scope = Scope.get_isolation_scope()
279
+ if event_id := scope.last_event_id():
280
+ self.sentry_event_ids[server_error] = event_id
281
+ return
282
+
283
+ async def _wait_for_sentry_event_id(scope: Scope) -> None:
284
+ i = 0
285
+ while not (event_id := scope.last_event_id()) and i < 100:
286
+ i += 1
287
+ await asyncio.sleep(0.001)
288
+ if event_id:
289
+ self.sentry_event_ids[server_error] = event_id
290
+
291
+ with contextlib.suppress(RuntimeError): # ignore no running loop
292
+ loop = asyncio.get_running_loop()
293
+ loop.create_task(_wait_for_sentry_event_id(scope))
262
294
 
263
295
  def get_and_reset_server_errors(self) -> List[Dict[str, Any]]:
264
296
  data: List[Dict[str, Any]] = []
@@ -272,10 +304,12 @@ class ServerErrorCounter:
272
304
  "type": server_error.type,
273
305
  "msg": server_error.msg,
274
306
  "traceback": server_error.traceback,
307
+ "sentry_event_id": self.sentry_event_ids.get(server_error),
275
308
  "error_count": count,
276
309
  }
277
310
  )
278
311
  self.error_counts.clear()
312
+ self.sentry_event_ids.clear()
279
313
  return data
280
314
 
281
315
  @staticmethod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apitally
3
- Version: 0.8.0
3
+ Version: 0.9.0
4
4
  Summary: API monitoring for REST APIs built with FastAPI, Flask, Django, Starlette and Litestar.
5
5
  Home-page: https://apitally.io
6
6
  License: MIT
@@ -26,6 +26,7 @@ Provides-Extra: django-rest-framework
26
26
  Provides-Extra: fastapi
27
27
  Provides-Extra: flask
28
28
  Provides-Extra: litestar
29
+ Provides-Extra: sentry
29
30
  Provides-Extra: starlette
30
31
  Requires-Dist: backoff (>=2.0.0)
31
32
  Requires-Dist: django (>=2.2) ; extra == "django-ninja" or extra == "django-rest-framework"
@@ -37,6 +38,7 @@ Requires-Dist: httpx (>=0.22.0) ; extra == "fastapi" or extra == "litestar" or e
37
38
  Requires-Dist: inflection (>=0.5.1) ; extra == "django-rest-framework"
38
39
  Requires-Dist: litestar (>=2.0.0) ; extra == "litestar"
39
40
  Requires-Dist: requests (>=2.26.0) ; extra == "django-ninja" or extra == "django-rest-framework" or extra == "flask"
41
+ Requires-Dist: sentry-sdk (>=2.2.0) ; extra == "sentry"
40
42
  Requires-Dist: starlette (>=0.21.0,<1.0.0) ; extra == "fastapi" or extra == "starlette"
41
43
  Requires-Dist: uritemplate (>=3.0.0) ; extra == "django-rest-framework"
42
44
  Project-URL: Documentation, https://docs.apitally.io
@@ -1,7 +1,7 @@
1
- apitally/__init__.py,sha256=iPlYCcIzuzW7T2HKDkmYlMkRI51dBLfNRxPPiWrfw9U,22
1
+ apitally/__init__.py,sha256=H9NWRZb7NbeRRPLP_V1fARmLNXranorVM-OOY-8_2ug,22
2
2
  apitally/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  apitally/client/asyncio.py,sha256=uR5JlH37G6gZvAJ7A1gYOGkjn3zjC-4I6avA1fncXHs,4433
4
- apitally/client/base.py,sha256=-2Bw_zFq6de-mSQSYsTa0NSRoMcHwiZqpSCQnjjIZ2M,11351
4
+ apitally/client/base.py,sha256=VWtHTkA6UT5aV37i_CXUvcfmYrabjHGBhzbDU6EeH1A,12785
5
5
  apitally/client/logging.py,sha256=QMsKIIAFo92PNBUleeTgsrsQa7SEal-oJa1oOHUr1wI,507
6
6
  apitally/client/threading.py,sha256=ihQzUStrSQFynpqXgFpseAXrHuc5Et1QvG-YHlzqDr8,4831
7
7
  apitally/common.py,sha256=GbVmnXxhRvV30d7CfCQ9r0AeXj14Mv9Jm_Yd1bRWP28,1088
@@ -13,7 +13,7 @@ apitally/flask.py,sha256=Utn92aXXl_1f4bKvuf4iZDB4v1vVLpeW5p1tF57Kf-8,5552
13
13
  apitally/litestar.py,sha256=1-skfFDKjYa7y6mOdNvjR4YGtsQbNA0iGQP1jyre41Q,7978
14
14
  apitally/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  apitally/starlette.py,sha256=kyXgzw0L90GUOZCgjuwM3q0uOpPX-hD0TQb2Wgqbqb8,7767
16
- apitally-0.8.0.dist-info/LICENSE,sha256=vbLzC-4TddtXX-_AFEBKMYWRlxC_MN0g66QhPxo8PgY,1065
17
- apitally-0.8.0.dist-info/METADATA,sha256=jl6_hdLWMu-CStH70iJ-4-Lt94328tX_X33avx-PIqI,6736
18
- apitally-0.8.0.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
19
- apitally-0.8.0.dist-info/RECORD,,
16
+ apitally-0.9.0.dist-info/LICENSE,sha256=vbLzC-4TddtXX-_AFEBKMYWRlxC_MN0g66QhPxo8PgY,1065
17
+ apitally-0.9.0.dist-info/METADATA,sha256=SsnJeg47jd8Q8MP9RXC9mbmweeE8ywLCxOLtQks3VKQ,6815
18
+ apitally-0.9.0.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
19
+ apitally-0.9.0.dist-info/RECORD,,