From efed20d703e05776e834bc26b5f354fe8c1357ab Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 5 Nov 2025 09:49:57 +0100 Subject: [PATCH 1/5] fix: Add hard limit to log batcher --- sentry_sdk/_log_batcher.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sentry_sdk/_log_batcher.py b/sentry_sdk/_log_batcher.py index 87bebdb226..531d6d9696 100644 --- a/sentry_sdk/_log_batcher.py +++ b/sentry_sdk/_log_batcher.py @@ -13,6 +13,7 @@ class LogBatcher: MAX_LOGS_BEFORE_FLUSH = 100 + MAX_LOGS_BEFORE_DROP = 1_000 FLUSH_WAIT_TIME = 5.0 def __init__( @@ -79,6 +80,9 @@ def add( return None with self._lock: + if len(self._log_buffer) >= self.MAX_LOGS_BEFORE_DROP: + return None + self._log_buffer.append(log) if len(self._log_buffer) >= self.MAX_LOGS_BEFORE_FLUSH: self._flush_event.set() From a814d962be4e18dc2307a690c0af8742c0db32f7 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 5 Nov 2025 13:37:46 +0100 Subject: [PATCH 2/5] record dropping logs and test --- sentry_sdk/_log_batcher.py | 7 +++++++ sentry_sdk/client.py | 5 ++++- tests/test_logs.py | 24 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/_log_batcher.py b/sentry_sdk/_log_batcher.py index 531d6d9696..f7f6c80565 100644 --- a/sentry_sdk/_log_batcher.py +++ b/sentry_sdk/_log_batcher.py @@ -19,10 +19,12 @@ class LogBatcher: def __init__( self, capture_func, # type: Callable[[Envelope], None] + record_lost_func, # type: Callable[..., None] ): # type: (...) -> None self._log_buffer = [] # type: List[Log] self._capture_func = capture_func + self._record_lost_func = record_lost_func self._running = True self._lock = threading.Lock() @@ -81,6 +83,11 @@ def add( with self._lock: if len(self._log_buffer) >= self.MAX_LOGS_BEFORE_DROP: + self._record_lost_func( + reason="queue_overflow", + data_category="log_item", + quantity=1, + ) return None self._log_buffer.append(log) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index 219f69b404..8d4c00e54a 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -373,7 +373,10 @@ def _capture_envelope(envelope): if has_logs_enabled(self.options): from sentry_sdk._log_batcher import LogBatcher - self.log_batcher = LogBatcher(capture_func=_capture_envelope) + self.log_batcher = LogBatcher( + capture_func=_capture_envelope, + record_lost_func=self.transport.record_lost_event, + ) self.metrics_batcher = None if has_metrics_enabled(self.options): diff --git a/tests/test_logs.py b/tests/test_logs.py index da7d0d5f03..982122d2d3 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -446,3 +446,27 @@ def test_logs_with_literal_braces( assert logs[0]["attributes"]["sentry.message.template"] == message else: assert "sentry.message.template" not in logs[0]["attributes"] + + +def test_batcher_drops_logs(sentry_init, monkeypatch): + sentry_init(enable_logs=True) + client = sentry_sdk.get_client() + + def no_op_flush(): + pass + + monkeypatch.setattr(client.log_batcher, "_flush", no_op_flush) + + lost_event_calls = [] + + def record_lost_event(reason, data_category=None, item=None, *, quantity=1): + lost_event_calls.append((reason, data_category, item, quantity)) + + monkeypatch.setattr(client.log_batcher, "_record_lost_func", record_lost_event) + + for i in range(1_005): # 5 logs over the hard limit + sentry_sdk.logger.info("This is a 'info' log...") + + assert len(lost_event_calls) == 5 + for lost_event_call in lost_event_calls: + assert lost_event_call == ("queue_overflow", "log_item", None, 1) From 50910bcb8f395e6eb65742915c8fb3b155c7c37d Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 5 Nov 2025 14:07:49 +0100 Subject: [PATCH 3/5] check transport is not None --- sentry_sdk/client.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index 8d4c00e54a..05d536f845 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -357,6 +357,19 @@ def _capture_envelope(envelope): if self.transport is not None: self.transport.capture_envelope(envelope) + def _record_lost_event( + reason, # type: str + data_category, # type: EventDataCategory + quantity=1, # type: int + ): + # type: (...) -> None + if self.transport is not None: + self.transport.record_lost_event( + reason=reason, + data_category=data_category, + quantity=quantity, + ) + try: _client_init_debug.set(self.options["debug"]) self.transport = make_transport(self.options) @@ -375,7 +388,7 @@ def _capture_envelope(envelope): self.log_batcher = LogBatcher( capture_func=_capture_envelope, - record_lost_func=self.transport.record_lost_event, + record_lost_func=_record_lost_event, ) self.metrics_batcher = None From d6cfbf8787e5bb8bf47126cfc75c6570190cc3df Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 5 Nov 2025 14:11:35 +0100 Subject: [PATCH 4/5] import --- sentry_sdk/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index 05d536f845..f0d50f643c 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -62,7 +62,7 @@ from typing import Union from typing import TypeVar - from sentry_sdk._types import Event, Hint, SDKInfo, Log, Metric + from sentry_sdk._types import Event, Hint, SDKInfo, Log, Metric, EventDataCategory from sentry_sdk.integrations import Integration from sentry_sdk.scope import Scope from sentry_sdk.session import Session From 90520d30699926c4d95c4cfdca13c16c4ec0f262 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Wed, 5 Nov 2025 14:19:14 +0100 Subject: [PATCH 5/5] add min python to test --- tests/test_logs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_logs.py b/tests/test_logs.py index 982122d2d3..6c0a9b14f9 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -448,6 +448,7 @@ def test_logs_with_literal_braces( assert "sentry.message.template" not in logs[0]["attributes"] +@minimum_python_37 def test_batcher_drops_logs(sentry_init, monkeypatch): sentry_init(enable_logs=True) client = sentry_sdk.get_client()