Skip to content

Commit aa31ebb

Browse files
committed
feat(tracing): Gate SQL query params behind data collection options
Record SQL params and paramstyle in `record_sql_queries` based on the new `data_collection.database_query_data` option instead of the `_experiments.record_sql_params` flag. When data collection is enabled, it takes precedence over the legacy flag; otherwise the legacy behavior is preserved until the experiment is removed. Refs PY-2587
1 parent 4eb0527 commit aa31ebb

2 files changed

Lines changed: 149 additions & 8 deletions

File tree

sentry_sdk/tracing_utils.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
_module_in_list,
2828
capture_internal_exceptions,
2929
filename_for_module,
30+
has_data_collection_enabled,
3031
is_sentry_url,
3132
is_valid_sample_rate,
3233
logger,
@@ -145,15 +146,27 @@ def record_sql_queries(
145146
) -> "Generator[Union[sentry_sdk.tracing.Span, sentry_sdk.traces.StreamedSpan], None, None]":
146147
# TODO: Bring back capturing of params by default
147148
client = sentry_sdk.get_client()
148-
if client.options["_experiments"].get("record_sql_params", False):
149-
if not params_list or params_list == [None]:
150-
params_list = None
149+
if has_data_collection_enabled(client.options):
150+
if client.options["data_collection"]["database_query_data"]:
151+
if not params_list or params_list == [None]:
152+
params_list = None
151153

152-
if paramstyle == "pyformat":
153-
paramstyle = "format"
154+
if paramstyle == "pyformat":
155+
paramstyle = "format"
156+
else:
157+
params_list = None
158+
paramstyle = None
154159
else:
155-
params_list = None
156-
paramstyle = None
160+
# TODO: remove this else block once data collection is released
161+
if client.options["_experiments"].get("record_sql_params", False):
162+
if not params_list or params_list == [None]:
163+
params_list = None
164+
165+
if paramstyle == "pyformat":
166+
paramstyle = "format"
167+
else:
168+
params_list = None
169+
paramstyle = None
157170

158171
query = _format_sql(cursor, query)
159172

tests/test_tracing_utils.py

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
from dataclasses import asdict, dataclass
2-
from typing import List, Optional
2+
from typing import Any, Dict, List, Optional
3+
from unittest import mock
34

45
import pytest
56

7+
import sentry_sdk
68
from sentry_sdk.tracing_utils import (
79
Baggage,
810
_should_be_included,
911
_should_continue_trace,
12+
record_sql_queries,
1013
)
1114
from tests.conftest import TestTransportWithOptions
1215

@@ -286,3 +289,128 @@ def test_baggage_from_incoming_header_value_with_equals_sign():
286289
header = "sentry-release=v1.0==1,sentry-trace_id=abc123"
287290
baggage = Baggage.from_incoming_header(header)
288291
assert baggage.sentry_items == {"release": "v1.0==1", "trace_id": "abc123"}
292+
293+
294+
def _get_query_breadcrumb_data(
295+
sentry_init,
296+
capture_events,
297+
sentry_options: "Dict[str, Any]",
298+
params_list: "Any" = [1, 2],
299+
paramstyle: "Optional[str]" = "pyformat",
300+
) -> "Dict[str, Any]":
301+
sentry_init(**sentry_options)
302+
events = capture_events()
303+
304+
with record_sql_queries(
305+
cursor=mock.MagicMock(),
306+
query="SELECT * FROM users WHERE id IN (%s, %s)",
307+
params_list=params_list,
308+
paramstyle=paramstyle,
309+
executemany=False,
310+
):
311+
pass
312+
313+
sentry_sdk.capture_message("hi")
314+
(event,) = events
315+
(crumb,) = event["breadcrumbs"]["values"]
316+
assert crumb["category"] == "query"
317+
return crumb["data"]
318+
319+
320+
@pytest.mark.parametrize(
321+
"sentry_options, expected_data",
322+
(
323+
pytest.param(
324+
{"_experiments": {"data_collection": {"database_query_data": True}}},
325+
{"db.params": [1, 2], "db.paramstyle": "format"},
326+
id="data_collection_on_records_params",
327+
),
328+
pytest.param(
329+
{"_experiments": {"data_collection": {"database_query_data": False}}},
330+
{},
331+
id="data_collection_off_strips_params",
332+
),
333+
pytest.param(
334+
{"_experiments": {"data_collection": {}}},
335+
{"db.params": [1, 2], "db.paramstyle": "format"},
336+
id="data_collection_default_records_params",
337+
),
338+
pytest.param(
339+
{"_experiments": {"record_sql_params": True}},
340+
{"db.params": [1, 2], "db.paramstyle": "format"},
341+
id="legacy_record_sql_params_on_records_params",
342+
),
343+
pytest.param(
344+
{"_experiments": {"record_sql_params": False}},
345+
{},
346+
id="legacy_record_sql_params_off_strips_params",
347+
),
348+
pytest.param(
349+
{},
350+
{},
351+
id="no_options_strips_params",
352+
),
353+
pytest.param(
354+
{
355+
"_experiments": {
356+
"record_sql_params": True,
357+
"data_collection": {"database_query_data": False},
358+
}
359+
},
360+
{},
361+
id="data_collection_off_takes_precedence_over_legacy_on",
362+
),
363+
pytest.param(
364+
{
365+
"_experiments": {
366+
"record_sql_params": False,
367+
"data_collection": {"database_query_data": True},
368+
}
369+
},
370+
{"db.params": [1, 2], "db.paramstyle": "format"},
371+
id="data_collection_on_takes_precedence_over_legacy_off",
372+
),
373+
),
374+
)
375+
def test_record_sql_queries_data_collection(
376+
sentry_init, capture_events, sentry_options, expected_data
377+
):
378+
assert (
379+
_get_query_breadcrumb_data(sentry_init, capture_events, sentry_options)
380+
== expected_data
381+
)
382+
383+
384+
@pytest.mark.parametrize("params_list", (None, [], [None]))
385+
def test_record_sql_queries_empty_params_not_recorded(
386+
sentry_init, capture_events, params_list
387+
):
388+
data = _get_query_breadcrumb_data(
389+
sentry_init,
390+
capture_events,
391+
{"_experiments": {"data_collection": {"database_query_data": True}}},
392+
params_list=params_list,
393+
)
394+
assert "db.params" not in data
395+
396+
397+
def test_record_sql_queries_paramstyle_passthrough(sentry_init, capture_events):
398+
data = _get_query_breadcrumb_data(
399+
sentry_init,
400+
capture_events,
401+
{"_experiments": {"data_collection": {"database_query_data": True}}},
402+
paramstyle="qmark",
403+
)
404+
assert data["db.paramstyle"] == "qmark"
405+
406+
407+
def test_record_sql_queries_paramstyle_dropped_when_collection_off(
408+
sentry_init, capture_events
409+
):
410+
data = _get_query_breadcrumb_data(
411+
sentry_init,
412+
capture_events,
413+
{"_experiments": {"data_collection": {"database_query_data": False}}},
414+
paramstyle="qmark",
415+
)
416+
assert "db.paramstyle" not in data

0 commit comments

Comments
 (0)