Skip to content

Commit be77ded

Browse files
committed
Initital Monitor impl
1 parent 0ebb2f9 commit be77ded

File tree

3 files changed

+90
-0
lines changed

3 files changed

+90
-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: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import os
2+
import time
3+
from threading import Thread, Lock
4+
5+
import sentry_sdk
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 = (
57+
sentry_sdk.Hub.current.client and sentry_sdk.Hub.current.client.transport
58+
)
59+
if transport and hasattr(transport, "_is_rate_limited"):
60+
return transport._is_rate_limited()
61+
return False
62+
63+
def check_health(self):
64+
# type: () -> None
65+
"""
66+
Perform the actual health checks,
67+
currently only checks if the transport is rate-limited.
68+
TODO: augment in the future with more checks.
69+
"""
70+
if self._is_transport_rate_limited():
71+
self._healthy = False
72+
73+
def is_healthy(self):
74+
# type: () -> bool
75+
return self._healthy
76+
77+
def kill(self):
78+
# type: () -> None
79+
self._running = False
80+
81+
def __del__(self):
82+
# type: () -> None
83+
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)