Skip to content

Commit a46b0da

Browse files
committed
.
1 parent bb3648b commit a46b0da

File tree

2 files changed

+40
-28
lines changed

2 files changed

+40
-28
lines changed

sentry_sdk/_batcher.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def __init__(
3131
self._record_lost_func = record_lost_func
3232
self._running = True
3333
self._lock = threading.Lock()
34+
self._active: "threading.local" = threading.local()
3435

3536
self._flush_event: "threading.Event" = threading.Event()
3637

@@ -70,23 +71,40 @@ def _ensure_thread(self) -> bool:
7071
return True
7172

7273
def _flush_loop(self) -> None:
74+
# Mark the flush-loop thread as active for its entire lifetime so
75+
# that any re-entrant add() triggered by GC warnings during wait(),
76+
# flush(), or Event operations is silently dropped instead of
77+
# deadlocking on internal locks.
78+
self._active.flag = True
7379
while self._running:
7480
self._flush_event.wait(self.FLUSH_WAIT_TIME + random.random())
7581
self._flush_event.clear()
7682
self._flush()
7783

7884
def add(self, item: "T") -> None:
79-
if not self._ensure_thread() or self._flusher is None:
85+
# Bail out if the current thread is already executing batcher code.
86+
# This prevents deadlocks when code running inside the batcher (e.g.
87+
# _add_to_envelope during flush, or _flush_event.wait/set) triggers
88+
# a GC-emitted warning that routes back through the logging
89+
# integration into add().
90+
if getattr(self._active, "flag", False):
8091
return None
8192

82-
with self._lock:
83-
if len(self._buffer) >= self.MAX_BEFORE_DROP:
84-
self._record_lost(item)
93+
self._active.flag = True
94+
try:
95+
if not self._ensure_thread() or self._flusher is None:
8596
return None
8697

87-
self._buffer.append(item)
88-
if len(self._buffer) >= self.MAX_BEFORE_FLUSH:
89-
self._flush_event.set()
98+
with self._lock:
99+
if len(self._buffer) >= self.MAX_BEFORE_DROP:
100+
self._record_lost(item)
101+
return None
102+
103+
self._buffer.append(item)
104+
if len(self._buffer) >= self.MAX_BEFORE_FLUSH:
105+
self._flush_event.set()
106+
finally:
107+
self._active.flag = False
90108

91109
def kill(self) -> None:
92110
if self._flusher is None:
@@ -97,7 +115,11 @@ def kill(self) -> None:
97115
self._flusher = None
98116

99117
def flush(self) -> None:
100-
self._flush()
118+
self._active.flag = True
119+
try:
120+
self._flush()
121+
finally:
122+
self._active.flag = False
101123

102124
def _add_to_envelope(self, envelope: "Envelope") -> None:
103125
envelope.add_item(

sentry_sdk/integrations/logging.py

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import logging
22
import sys
3-
import threading
43
from datetime import datetime, timezone
54
from fnmatch import fnmatch
65

@@ -322,29 +321,20 @@ class SentryLogsHandler(_BaseHandler):
322321
Note that you do not have to use this class if the logging integration is enabled, which it is by default.
323322
"""
324323

325-
_emitting = threading.local()
326-
327324
def emit(self, record: "LogRecord") -> "Any":
328-
if getattr(self._emitting, "active", False):
329-
return
330-
331-
self._emitting.active = True
332-
try:
333-
with capture_internal_exceptions():
334-
self.format(record)
335-
if not self._can_record(record):
336-
return
325+
with capture_internal_exceptions():
326+
self.format(record)
327+
if not self._can_record(record):
328+
return
337329

338-
client = sentry_sdk.get_client()
339-
if not client.is_active():
340-
return
330+
client = sentry_sdk.get_client()
331+
if not client.is_active():
332+
return
341333

342-
if not has_logs_enabled(client.options):
343-
return
334+
if not has_logs_enabled(client.options):
335+
return
344336

345-
self._capture_log_from_record(client, record)
346-
finally:
347-
self._emitting.active = False
337+
self._capture_log_from_record(client, record)
348338

349339
def _capture_log_from_record(
350340
self, client: "BaseClient", record: "LogRecord"

0 commit comments

Comments
 (0)