Skip to content

Commit e804242

Browse files
ericapisaniclaude
andcommitted
fix(client): Normalize span description whitespace before before_send_transaction
Collapse newlines and extra whitespace in span descriptions to single spaces before passing events to before_send_transaction. This allows users to write simple string-matching callbacks without needing to account for multi-line SQL or other formatted descriptions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 62d2a98 commit e804242

File tree

2 files changed

+87
-1
lines changed

2 files changed

+87
-1
lines changed

sentry_sdk/client.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from importlib import import_module
88
from typing import TYPE_CHECKING, List, Dict, cast, overload
99
import warnings
10-
10+
import re
1111
from sentry_sdk._compat import check_uwsgi_thread_support
1212
from sentry_sdk._metrics_batcher import MetricsBatcher
1313
from sentry_sdk._span_batcher import SpanBatcher
@@ -688,6 +688,7 @@ def _prepare_event(
688688
):
689689
new_event = None
690690
spans_before = len(cast(List[Dict[str, object]], event.get("spans", [])))
691+
self._clean_span_descriptions(event)
691692
with capture_internal_exceptions():
692693
new_event = before_send_transaction(event, hint or {})
693694
if new_event is None:
@@ -712,6 +713,12 @@ def _prepare_event(
712713

713714
return event
714715

716+
def _clean_span_descriptions(self, event: "Event") -> None:
717+
for s in event.get("spans", []):
718+
if "description" in s:
719+
cleaned_description = re.sub(r"\s+", " ", s["description"]).strip()
720+
s["description"] = cleaned_description
721+
715722
def _is_ignored_error(self, event: "Event", hint: "Hint") -> bool:
716723
exc_info = hint.get("exc_info")
717724
if exc_info is None:

tests/test_basics.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,85 @@ def before_send_transaction(event, hint):
185185
assert event["extra"] == {"before_send_transaction_called": True}
186186

187187

188+
def test_before_send_transaction_span_description_contains_newlines(
189+
sentry_init, capture_events
190+
):
191+
def before_send_transaction(event, hint):
192+
for span in event.get("spans", []):
193+
if (
194+
span.get("description", "")
195+
== "SELECT u.id, u.name, u.email FROM users;"
196+
):
197+
span["description"] = "filtered"
198+
return event
199+
200+
sentry_init(
201+
before_send_transaction=before_send_transaction,
202+
traces_sample_rate=1.0,
203+
)
204+
events = capture_events()
205+
206+
description = "SELECT u.id,\n u.name,\n u.email\n FROM users;"
207+
208+
with start_transaction(name="test_transaction"):
209+
with sentry_sdk.start_span(op="db", description=description):
210+
pass
211+
212+
(event,) = events
213+
assert event["transaction"] == "test_transaction"
214+
215+
spans = event["spans"]
216+
assert len(spans) == 1
217+
assert spans[0]["description"] == "filtered"
218+
assert spans[0]["op"] == "db"
219+
220+
221+
def test_before_send_transaction_span_description_contains_multiple_lines(
222+
sentry_init, capture_events
223+
):
224+
def before_send_transaction(event, hint):
225+
for span in event.get("spans", []):
226+
if (
227+
span.get("description", "")
228+
== "SELECT u.id, u.name, u.email, p.title AS post_title, p.created_at AS post_date FROM users u JOIN posts p ON u.id = p.user_id WHERE u.active = true ORDER BY p.created_at DESC"
229+
):
230+
span["description"] = "no bueno"
231+
return event
232+
233+
sentry_init(
234+
before_send_transaction=before_send_transaction,
235+
traces_sample_rate=1.0,
236+
)
237+
events = capture_events()
238+
239+
description = """SELECT
240+
u.id,
241+
u.name,
242+
u.email,
243+
p.title AS post_title,
244+
p.created_at AS post_date
245+
FROM
246+
users u
247+
JOIN
248+
posts p ON u.id = p.user_id
249+
WHERE
250+
u.active = true
251+
ORDER BY
252+
p.created_at DESC"""
253+
254+
with start_transaction(name="test_transaction"):
255+
with sentry_sdk.start_span(op="db", description=description):
256+
pass
257+
258+
(event,) = events
259+
assert event["transaction"] == "test_transaction"
260+
261+
spans = event["spans"]
262+
assert len(spans) == 1
263+
assert spans[0]["description"] == "no bueno"
264+
assert spans[0]["op"] == "db"
265+
266+
188267
def test_option_before_send_transaction_discard(sentry_init, capture_events):
189268
def before_send_transaction_discard(event, hint):
190269
return None

0 commit comments

Comments
 (0)