Skip to content

Commit 5b982d8

Browse files
committed
Initital Monitor impl
1 parent 0ebb2f9 commit 5b982d8

File tree

3 files changed

+88
-0
lines changed

3 files changed

+88
-0
lines changed

sentry_sdk/client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from sentry_sdk.envelope import Envelope
3333
from sentry_sdk.profiler import has_profiling_enabled, setup_profiler
3434
from sentry_sdk.scrubber import EventScrubber
35+
from sentry_sdk.monitor import Monitor
3536

3637
from sentry_sdk._types import TYPE_CHECKING
3738

@@ -211,6 +212,7 @@ def _capture_envelope(envelope):
211212
self.transport = make_transport(self.options)
212213

213214
self.session_flusher = SessionFlusher(capture_func=_capture_envelope)
215+
self.monitor = Monitor()
214216

215217
request_bodies = ("always", "never", "small", "medium")
216218
if self.options["request_bodies"] not in request_bodies:
@@ -568,6 +570,7 @@ def close(
568570
if self.transport is not None:
569571
self.flush(timeout=timeout, callback=callback)
570572
self.session_flusher.kill()
573+
self.monitor.kill()
571574
self.transport.kill()
572575
self.transport = None
573576

sentry_sdk/monitor.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import os
2+
import time
3+
from threading import Thread, Lock
4+
5+
from sentry_sdk.hub import Hub
6+
from sentry_sdk._types import TYPE_CHECKING
7+
8+
if TYPE_CHECKING:
9+
from typing import Optional
10+
11+
12+
class Monitor(object):
13+
"""
14+
Performs health checks in a separate thread once every interval seconds
15+
and updates the internal state. Other parts of the SDK only read this state
16+
and act accordingly.
17+
"""
18+
19+
name = "sentry.monitor"
20+
21+
def __init__(self, interval=60):
22+
# type: (int) -> None
23+
self.interval = interval # type: int
24+
self._healthy = True
25+
self._thread = None # type: Optional[Thread]
26+
self._thread_lock = Lock()
27+
self._thread_for_pid = None # type: Optional[int]
28+
self._running = True
29+
30+
def _ensure_running(self):
31+
# type: () -> None
32+
if self._thread_for_pid == os.getpid() and self._thread is not None:
33+
return None
34+
35+
with self._thread_lock:
36+
if self._thread_for_pid == os.getpid() and self._thread is not None:
37+
return None
38+
39+
def _thread():
40+
# type: (...) -> None
41+
while self._running:
42+
time.sleep(self.interval)
43+
if self._running:
44+
self.check_health()
45+
46+
thread = Thread(name=self.name, target=_thread)
47+
thread.daemon = True
48+
thread.start()
49+
self._thread = thread
50+
self._thread_for_pid = os.getpid()
51+
52+
return None
53+
54+
def _is_transport_rate_limited(self):
55+
# type: () -> bool
56+
transport = Hub.current.client and Hub.current.client.transport
57+
if transport and hasattr(transport, "_is_rate_limited"):
58+
return transport._is_rate_limited()
59+
return False
60+
61+
def check_health(self):
62+
# type: () -> None
63+
"""
64+
Perform the actual health checks,
65+
currently only checks if the transport is rate-limited.
66+
TODO: augment in the future with more checks.
67+
"""
68+
if self._is_transport_rate_limited():
69+
self._healthy = False
70+
71+
def is_healthy(self):
72+
# type: () -> bool
73+
return self._healthy
74+
75+
def kill(self):
76+
# type: () -> None
77+
self._running = False
78+
79+
def __del__(self):
80+
# type: () -> None
81+
self.kill()

sentry_sdk/transport.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,10 @@ def _disabled(bucket):
311311

312312
return _disabled(category) or _disabled(None)
313313

314+
def _is_rate_limited(self):
315+
# type: () -> bool
316+
return any(ts > datetime.utcnow() for ts in self._disabled_until.values())
317+
314318
def _send_event(
315319
self, event # type: Event
316320
):

0 commit comments

Comments
 (0)