From eca25333b2043bcc674f149b889b1d7cc33cd30b Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Thu, 13 Aug 2026 13:55:39 +0200 Subject: [PATCH 1/2] chore(aiomysql): Remove transaction-based-tracing --- sentry_sdk/integrations/aiomysql.py | 45 +- tests/integrations/aiomysql/test_aiomysql.py | 764 ++++++------------- 2 files changed, 229 insertions(+), 580 deletions(-) diff --git a/sentry_sdk/integrations/aiomysql.py b/sentry_sdk/integrations/aiomysql.py index c6e35a1426..5dccc4144d 100644 --- a/sentry_sdk/integrations/aiomysql.py +++ b/sentry_sdk/integrations/aiomysql.py @@ -9,7 +9,6 @@ from sentry_sdk.traces import StreamedSpan from sentry_sdk.tracing_utils import ( add_query_source, - has_span_streaming_enabled, record_sql_queries, ) from sentry_sdk.utils import ( @@ -187,42 +186,22 @@ async def _inner(self: "Connection") -> T: if client.get_integration(AioMySQLIntegration) is None: return await f(self) - if has_span_streaming_enabled(client.options): - breadcrumb_data = _get_connect_data(self, use_streaming_keys=True) + breadcrumb_data = _get_connect_data(self, use_streaming_keys=True) - with capture_internal_exceptions(): - sentry_sdk.add_breadcrumb( - message="connect", category="query", data=breadcrumb_data - ) - - if sentry_sdk.traces.get_current_span() is None: - return await f(self) - - span_attributes: dict[str, Any] = { - "sentry.op": OP.DB, - "sentry.origin": AioMySQLIntegration.origin, - } | breadcrumb_data - - with sentry_sdk.traces.start_span( - name="connect", attributes=span_attributes - ): - return await f(self) + with capture_internal_exceptions(): + sentry_sdk.add_breadcrumb( + message="connect", category="query", data=breadcrumb_data + ) - connect_data = _get_connect_data(self) + if sentry_sdk.traces.get_current_span() is None: + return await f(self) - with sentry_sdk.start_span( - op=OP.DB, - name="connect", - origin=AioMySQLIntegration.origin, - ) as span: - _set_db_data(span, self) + span_attributes: dict[str, Any] = { + "sentry.op": OP.DB, + "sentry.origin": AioMySQLIntegration.origin, + } | breadcrumb_data - with capture_internal_exceptions(): - sentry_sdk.add_breadcrumb( - message="connect", - category="query", - data=connect_data, - ) + with sentry_sdk.traces.start_span(name="connect", attributes=span_attributes): return await f(self) return _inner diff --git a/tests/integrations/aiomysql/test_aiomysql.py b/tests/integrations/aiomysql/test_aiomysql.py index 32f2a22538..977ca46d09 100644 --- a/tests/integrations/aiomysql/test_aiomysql.py +++ b/tests/integrations/aiomysql/test_aiomysql.py @@ -20,7 +20,7 @@ import pytest_asyncio import sentry_sdk -from sentry_sdk import capture_message, start_transaction +from sentry_sdk import capture_message from sentry_sdk.consts import SPANDATA from sentry_sdk.integrations.aiomysql import AioMySQLIntegration from sentry_sdk.tracing_utils import record_sql_queries @@ -689,624 +689,320 @@ async def test_connection_pool(sentry_init, capture_events) -> None: @pytest.mark.asyncio -@pytest.mark.parametrize("span_streaming", [True, False]) -async def test_query_source_disabled( - sentry_init, capture_events, capture_items, span_streaming -): - sentry_options = { - "integrations": [AioMySQLIntegration()], - "traces_sample_rate": 1.0, - "enable_db_query_source": False, - "db_query_source_threshold_ms": 0, - "trace_lifecycle": "stream" if span_streaming else "static", - } - - sentry_init(**sentry_options) - - if span_streaming: - items = capture_items("span") - - with sentry_sdk.traces.start_span(name="test_segment"): - conn = await aiomysql.connect(**_connect_args()) - - async with conn.cursor() as cur: - await cur.execute( - "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", - ) - - conn.close() - - sentry_sdk.flush() - - spans = [item.payload for item in items] +async def test_query_source_disabled(sentry_init, capture_items): + sentry_init( + integrations=[AioMySQLIntegration()], + traces_sample_rate=1.0, + enable_db_query_source=False, + db_query_source_threshold_ms=0, + trace_lifecycle="stream", + ) - assert len(spans) == 3 + items = capture_items("span") - connect_span = spans[0] - insert_span = spans[1] - segment = spans[2] + with sentry_sdk.traces.start_span(name="test_segment"): + conn = await aiomysql.connect(**_connect_args()) - assert segment["is_segment"] is True - assert connect_span["name"] == "connect" - assert insert_span["name"].startswith("INSERT INTO") + async with conn.cursor() as cur: + await cur.execute( + "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", + ) - data = insert_span.get("attributes", {}) + conn.close() - assert "code.line.number" not in data - assert "code.namespace" not in data - assert "code.file.path" not in data - assert "code.function" not in data - else: - events = capture_events() + sentry_sdk.flush() - with start_transaction(name="test_transaction", sampled=True): - conn = await aiomysql.connect(**_connect_args()) + spans = [item.payload for item in items] - async with conn.cursor() as cur: - await cur.execute( - "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", - ) + assert len(spans) == 3 - conn.close() + connect_span = spans[0] + insert_span = spans[1] + segment = spans[2] - (event,) = events + assert segment["is_segment"] is True + assert connect_span["name"] == "connect" + assert insert_span["name"].startswith("INSERT INTO") - span = event["spans"][-1] - assert span["description"].startswith("INSERT INTO") - data = span.get("data", {}) + data = insert_span.get("attributes", {}) - assert SPANDATA.CODE_LINENO not in data - assert SPANDATA.CODE_NAMESPACE not in data - assert SPANDATA.CODE_FILEPATH not in data - assert SPANDATA.CODE_FUNCTION not in data + assert "code.line.number" not in data + assert "code.namespace" not in data + assert "code.file.path" not in data + assert "code.function" not in data @pytest.mark.asyncio @pytest.mark.parametrize("enable_db_query_source", [None, True]) -@pytest.mark.parametrize("span_streaming", [True, False]) -async def test_query_source_enabled( - sentry_init, capture_events, capture_items, enable_db_query_source, span_streaming -): +async def test_query_source_enabled(sentry_init, capture_items, enable_db_query_source): sentry_options = { "integrations": [AioMySQLIntegration()], "traces_sample_rate": 1.0, "db_query_source_threshold_ms": 0, - "trace_lifecycle": "stream" if span_streaming else "static", + "trace_lifecycle": "stream", } if enable_db_query_source is not None: sentry_options["enable_db_query_source"] = enable_db_query_source sentry_init(**sentry_options) - if span_streaming: - items = capture_items("span") - - with sentry_sdk.traces.start_span(name="test_segment"): - conn = await aiomysql.connect(**_connect_args()) - - async with conn.cursor() as cur: - await cur.execute( - "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", - ) - - conn.close() - - sentry_sdk.flush() - - spans = [item.payload for item in items] - - assert len(spans) == 3 + items = capture_items("span") - connect_span = spans[0] - insert_span = spans[1] - segment = spans[2] + with sentry_sdk.traces.start_span(name="test_segment"): + conn = await aiomysql.connect(**_connect_args()) - assert segment["is_segment"] is True - assert connect_span["name"] == "connect" - assert insert_span["name"].startswith("INSERT INTO") + async with conn.cursor() as cur: + await cur.execute( + "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", + ) - data = insert_span.get("attributes", {}) + conn.close() - assert "code.line.number" in data - assert "code.namespace" in data - assert "code.file.path" in data - assert "code.function" in data - else: - events = capture_events() + sentry_sdk.flush() - with start_transaction(name="test_transaction", sampled=True): - conn = await aiomysql.connect(**_connect_args()) + spans = [item.payload for item in items] - async with conn.cursor() as cur: - await cur.execute( - "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", - ) + assert len(spans) == 3 - conn.close() + connect_span = spans[0] + insert_span = spans[1] + segment = spans[2] - (event,) = events + assert segment["is_segment"] is True + assert connect_span["name"] == "connect" + assert insert_span["name"].startswith("INSERT INTO") - span = event["spans"][-1] - assert span["description"].startswith("INSERT INTO") - data = span.get("data", {}) + data = insert_span.get("attributes", {}) - assert SPANDATA.CODE_LINENO in data - assert SPANDATA.CODE_NAMESPACE in data - assert SPANDATA.CODE_FILEPATH in data - assert SPANDATA.CODE_FUNCTION in data + assert "code.line.number" in data + assert "code.namespace" in data + assert "code.file.path" in data + assert "code.function" in data @pytest.mark.asyncio -@pytest.mark.parametrize("span_streaming", [True, False]) -async def test_query_source(sentry_init, capture_events, capture_items, span_streaming): +async def test_query_source(sentry_init, capture_items): sentry_init( integrations=[AioMySQLIntegration()], traces_sample_rate=1.0, enable_db_query_source=True, db_query_source_threshold_ms=0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) - if span_streaming: - items = capture_items("span") - - with sentry_sdk.traces.start_span(name="test_segment"): - conn = await aiomysql.connect(**_connect_args()) - - async with conn.cursor() as cur: - await cur.execute( - "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", - ) - - conn.close() - - sentry_sdk.flush() - - spans = [item.payload for item in items] - - assert len(spans) == 3 - - connect_span = spans[0] - insert_span = spans[1] - segment = spans[2] - - assert segment["is_segment"] is True - assert connect_span["name"] == "connect" - assert insert_span["name"].startswith("INSERT INTO") + items = capture_items("span") - data = insert_span.get("attributes", {}) - - assert "code.line.number" in data - assert "code.namespace" in data - assert "code.file.path" in data - assert "code.function" in data + with sentry_sdk.traces.start_span(name="test_segment"): + conn = await aiomysql.connect(**_connect_args()) - assert type(data.get("code.line.number")) == int - assert data.get("code.line.number") > 0 - assert data.get("code.namespace") == "tests.integrations.aiomysql.test_aiomysql" - assert data.get("code.file.path").endswith( - "tests/integrations/aiomysql/test_aiomysql.py" - ) + async with conn.cursor() as cur: + await cur.execute( + "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", + ) - is_relative_path = data.get("code.file.path")[0] != os.sep - assert is_relative_path + conn.close() - assert data.get("code.function") == "test_query_source" - else: - events = capture_events() + sentry_sdk.flush() - with start_transaction(name="test_transaction", sampled=True): - conn = await aiomysql.connect(**_connect_args()) + spans = [item.payload for item in items] - async with conn.cursor() as cur: - await cur.execute( - "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", - ) + assert len(spans) == 3 - conn.close() + connect_span = spans[0] + insert_span = spans[1] + segment = spans[2] - (event,) = events + assert segment["is_segment"] is True + assert connect_span["name"] == "connect" + assert insert_span["name"].startswith("INSERT INTO") - span = event["spans"][-1] - assert span["description"].startswith("INSERT INTO") + data = insert_span.get("attributes", {}) - data = span.get("data", {}) + assert "code.line.number" in data + assert "code.namespace" in data + assert "code.file.path" in data + assert "code.function" in data - assert SPANDATA.CODE_LINENO in data - assert SPANDATA.CODE_NAMESPACE in data - assert SPANDATA.CODE_FILEPATH in data - assert SPANDATA.CODE_FUNCTION in data - - assert type(data.get(SPANDATA.CODE_LINENO)) == int - assert data.get(SPANDATA.CODE_LINENO) > 0 - assert ( - data.get(SPANDATA.CODE_NAMESPACE) - == "tests.integrations.aiomysql.test_aiomysql" - ) - assert data.get(SPANDATA.CODE_FILEPATH).endswith( - "tests/integrations/aiomysql/test_aiomysql.py" - ) + assert type(data.get("code.line.number")) == int + assert data.get("code.line.number") > 0 + assert data.get("code.namespace") == "tests.integrations.aiomysql.test_aiomysql" + assert data.get("code.file.path").endswith( + "tests/integrations/aiomysql/test_aiomysql.py" + ) - is_relative_path = data.get(SPANDATA.CODE_FILEPATH)[0] != os.sep - assert is_relative_path + is_relative_path = data.get("code.file.path")[0] != os.sep + assert is_relative_path - assert data.get(SPANDATA.CODE_FUNCTION) == "test_query_source" + assert data.get("code.function") == "test_query_source" @pytest.mark.asyncio -@pytest.mark.parametrize("span_streaming", [True, False]) -async def test_no_query_source_if_duration_too_short( - sentry_init, capture_events, capture_items, span_streaming -): +async def test_no_query_source_if_duration_too_short(sentry_init, capture_items): sentry_init( integrations=[AioMySQLIntegration()], traces_sample_rate=1.0, enable_db_query_source=True, db_query_source_threshold_ms=100, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) - if span_streaming: - items = capture_items("span") - - @contextmanager - def fake_record_sql_queries_streaming(*args, **kwargs): - with record_sql_queries(*args, **kwargs) as span: - pass - span._start_timestamp = datetime.datetime(2024, 1, 1, microsecond=0) - span._end_timestamp = datetime.datetime(2024, 1, 1, microsecond=99999) - yield span - - with sentry_sdk.traces.start_span(name="test_segment"): - conn = await aiomysql.connect(**_connect_args()) - - async with conn.cursor() as cur: - with mock.patch( - "sentry_sdk.integrations.aiomysql.record_sql_queries", - fake_record_sql_queries_streaming, - ): - await cur.execute( - "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", - ) - - conn.close() + items = capture_items("span") - sentry_sdk.flush() + @contextmanager + def fake_record_sql_queries_streaming(*args, **kwargs): + with record_sql_queries(*args, **kwargs) as span: + pass + span._start_timestamp = datetime.datetime(2024, 1, 1, microsecond=0) + span._end_timestamp = datetime.datetime(2024, 1, 1, microsecond=99999) + yield span - spans = [item.payload for item in items] - - assert len(spans) == 3 - - connect_span = spans[0] - insert_span = spans[1] - segment = spans[2] - - assert segment["name"] == "test_segment" - assert insert_span["name"].startswith("INSERT INTO") - assert connect_span["name"] == "connect" - data = insert_span.get("attributes", {}) - - assert "code.line.number" not in data - assert "code.namespace" not in data - assert "code.file.path" not in data - assert "code.function" not in data - else: - events = capture_events() + with sentry_sdk.traces.start_span(name="test_segment"): + conn = await aiomysql.connect(**_connect_args()) - with start_transaction(name="test_transaction", sampled=True): - conn = await aiomysql.connect(**_connect_args()) + async with conn.cursor() as cur: + with mock.patch( + "sentry_sdk.integrations.aiomysql.record_sql_queries", + fake_record_sql_queries_streaming, + ): + await cur.execute( + "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", + ) - @contextmanager - def fake_record_sql_queries(*args, **kwargs): - with record_sql_queries(*args, **kwargs) as span: - pass - span.start_timestamp = datetime.datetime(2024, 1, 1, microsecond=0) - span.timestamp = datetime.datetime(2024, 1, 1, microsecond=99999) - yield span + conn.close() - async with conn.cursor() as cur: - with mock.patch( - "sentry_sdk.integrations.aiomysql.record_sql_queries", - fake_record_sql_queries, - ): - await cur.execute( - "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", - ) + sentry_sdk.flush() - conn.close() + spans = [item.payload for item in items] - (event,) = events + assert len(spans) == 3 - span = event["spans"][-1] - assert span["description"].startswith("INSERT INTO") + connect_span = spans[0] + insert_span = spans[1] + segment = spans[2] - data = span.get("data", {}) + assert segment["name"] == "test_segment" + assert insert_span["name"].startswith("INSERT INTO") + assert connect_span["name"] == "connect" + data = insert_span.get("attributes", {}) - assert SPANDATA.CODE_LINENO not in data - assert SPANDATA.CODE_NAMESPACE not in data - assert SPANDATA.CODE_FILEPATH not in data - assert SPANDATA.CODE_FUNCTION not in data + assert "code.line.number" not in data + assert "code.namespace" not in data + assert "code.file.path" not in data + assert "code.function" not in data @pytest.mark.asyncio -@pytest.mark.parametrize("span_streaming", [True, False]) -async def test_query_source_if_duration_over_threshold( - sentry_init, capture_events, capture_items, span_streaming -): +async def test_query_source_if_duration_over_threshold(sentry_init, capture_items): sentry_init( integrations=[AioMySQLIntegration()], traces_sample_rate=1.0, enable_db_query_source=True, db_query_source_threshold_ms=100, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) - if span_streaming: - items = capture_items("span") + items = capture_items("span") - @contextmanager - def fake_record_sql_queries_streaming(*args, **kwargs): - with record_sql_queries(*args, **kwargs) as span: - span._start_timestamp = datetime.datetime( - 2024, 1, 1, microsecond=0, tzinfo=datetime.timezone.utc - ) - yield span - - with sentry_sdk.traces.start_span(name="test_segment"): - conn = await aiomysql.connect(**_connect_args()) - - async with conn.cursor() as cur: - with mock.patch( - "sentry_sdk.integrations.aiomysql.record_sql_queries", - fake_record_sql_queries_streaming, - ): - await cur.execute( - "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", - ) - - conn.close() - - sentry_sdk.flush() - - spans = [item.payload for item in items] - - assert len(spans) == 3 + @contextmanager + def fake_record_sql_queries_streaming(*args, **kwargs): + with record_sql_queries(*args, **kwargs) as span: + span._start_timestamp = datetime.datetime( + 2024, 1, 1, microsecond=0, tzinfo=datetime.timezone.utc + ) + yield span - connect_span = spans[0] - insert_span = spans[1] - segment = spans[2] + with sentry_sdk.traces.start_span(name="test_segment"): + conn = await aiomysql.connect(**_connect_args()) - assert segment["name"] == "test_segment" - assert insert_span["name"].startswith("INSERT INTO") - assert connect_span["name"] == "connect" + async with conn.cursor() as cur: + with mock.patch( + "sentry_sdk.integrations.aiomysql.record_sql_queries", + fake_record_sql_queries_streaming, + ): + await cur.execute( + "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", + ) - data = insert_span.get("attributes", {}) + conn.close() - assert "code.line.number" in data - assert "code.namespace" in data - assert "code.file.path" in data - assert "code.function" in data + sentry_sdk.flush() - assert type(data.get("code.line.number")) == int - assert data.get("code.line.number") > 0 - assert data.get("code.namespace") == "tests.integrations.aiomysql.test_aiomysql" - assert data.get("code.file.path").endswith( - "tests/integrations/aiomysql/test_aiomysql.py" - ) + spans = [item.payload for item in items] - is_relative_path = data.get("code.file.path")[0] != os.sep - assert is_relative_path + assert len(spans) == 3 - assert ( - data.get("code.function") == "test_query_source_if_duration_over_threshold" - ) - else: - events = capture_events() - - with start_transaction(name="test_transaction", sampled=True): - conn = await aiomysql.connect(**_connect_args()) - - @contextmanager - def fake_record_sql_queries(*args, **kwargs): - with record_sql_queries(*args, **kwargs) as span: - pass - span.start_timestamp = datetime.datetime(2024, 1, 1, microsecond=0) - span.timestamp = datetime.datetime(2024, 1, 1, microsecond=100001) - yield span - - async with conn.cursor() as cur: - with mock.patch( - "sentry_sdk.integrations.aiomysql.record_sql_queries", - fake_record_sql_queries, - ): - await cur.execute( - "INSERT INTO users(name, password, dob) VALUES ('Alice', 'secret', '1990-12-25')", - ) - - conn.close() - - (event,) = events - - span = event["spans"][-1] - assert span["description"].startswith("INSERT INTO") - - data = span.get("data", {}) - - assert SPANDATA.CODE_LINENO in data - assert SPANDATA.CODE_NAMESPACE in data - assert SPANDATA.CODE_FILEPATH in data - assert SPANDATA.CODE_FUNCTION in data - - assert type(data.get(SPANDATA.CODE_LINENO)) == int - assert data.get(SPANDATA.CODE_LINENO) > 0 - assert ( - data.get(SPANDATA.CODE_NAMESPACE) - == "tests.integrations.aiomysql.test_aiomysql" - ) - assert data.get(SPANDATA.CODE_FILEPATH).endswith( - "tests/integrations/aiomysql/test_aiomysql.py" - ) + connect_span = spans[0] + insert_span = spans[1] + segment = spans[2] - is_relative_path = data.get(SPANDATA.CODE_FILEPATH)[0] != os.sep - assert is_relative_path + assert segment["name"] == "test_segment" + assert insert_span["name"].startswith("INSERT INTO") + assert connect_span["name"] == "connect" - assert ( - data.get(SPANDATA.CODE_FUNCTION) - == "test_query_source_if_duration_over_threshold" - ) + data = insert_span.get("attributes", {}) + assert "code.line.number" in data + assert "code.namespace" in data + assert "code.file.path" in data + assert "code.function" in data -@pytest.mark.asyncio -@pytest.mark.parametrize("span_streaming", [True, False]) -async def test_span_origin(sentry_init, capture_events, capture_items, span_streaming): - sentry_init( - integrations=[AioMySQLIntegration()], - traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + assert type(data.get("code.line.number")) == int + assert data.get("code.line.number") > 0 + assert data.get("code.namespace") == "tests.integrations.aiomysql.test_aiomysql" + assert data.get("code.file.path").endswith( + "tests/integrations/aiomysql/test_aiomysql.py" ) - if span_streaming: - items = capture_items("span") - - with sentry_sdk.traces.start_span(name="test_segment"): - conn = await aiomysql.connect(**_connect_args()) - - async with conn.cursor() as cur: - await cur.execute("SELECT 1") - await cur.execute("SELECT 2") - - conn.close() - - sentry_sdk.flush() + is_relative_path = data.get("code.file.path")[0] != os.sep + assert is_relative_path - spans = [item.payload for item in items] - - non_segment_spans = [s for s in spans if not s.get("is_segment")] - segment_spans = [s for s in spans if s.get("is_segment")] - - assert len(segment_spans) == 1 - assert segment_spans[0]["attributes"]["sentry.origin"] == "manual" - - for span in non_segment_spans: - assert span["attributes"]["sentry.origin"] == "auto.db.aiomysql" - else: - events = capture_events() - - with start_transaction(name="test_transaction"): - conn = await aiomysql.connect(**_connect_args()) - - async with conn.cursor() as cur: - await cur.execute("SELECT 1") - await cur.execute("SELECT 2") - - conn.close() - - (event,) = events - - assert event["contexts"]["trace"]["origin"] == "manual" - - for span in event["spans"]: - assert span["origin"] == "auto.db.aiomysql" + assert data.get("code.function") == "test_query_source_if_duration_over_threshold" @pytest.mark.asyncio -@pytest.mark.parametrize("span_streaming", [True, False]) -async def test_multiline_query_description_normalized( - sentry_init, capture_events, capture_items, span_streaming -): +async def test_span_origin(sentry_init, capture_items): sentry_init( integrations=[AioMySQLIntegration()], traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) - if span_streaming: - items = capture_items("span") - - with sentry_sdk.traces.start_span(name="test_segment"): - conn = await aiomysql.connect(**_connect_args()) + items = capture_items("span") - async with conn.cursor() as cur: - await cur.execute( - """ - SELECT - id, - name - FROM - users - WHERE - name = 'Alice' - """ - ) - - conn.close() - - sentry_sdk.flush() + with sentry_sdk.traces.start_span(name="test_segment"): + conn = await aiomysql.connect(**_connect_args()) - spans = [item.payload for item in items] + async with conn.cursor() as cur: + await cur.execute("SELECT 1") + await cur.execute("SELECT 2") - select_spans = [ - s - for s in spans - if not s.get("is_segment") and "SELECT" in s.get("name", "") - ] - assert len(select_spans) == 1 - assert ( - select_spans[0]["name"] == "SELECT id, name FROM users WHERE name = 'Alice'" - ) - else: - events = capture_events() + conn.close() - with start_transaction(name="test_transaction"): - conn = await aiomysql.connect(**_connect_args()) + sentry_sdk.flush() - async with conn.cursor() as cur: - await cur.execute( - """ - SELECT - id, - name - FROM - users - WHERE - name = 'Alice' - """ - ) + spans = [item.payload for item in items] - conn.close() + non_segment_spans = [s for s in spans if not s.get("is_segment")] + segment_spans = [s for s in spans if s.get("is_segment")] - (event,) = events + assert len(segment_spans) == 1 + assert segment_spans[0]["attributes"]["sentry.origin"] == "manual" - spans = [ - s - for s in event["spans"] - if s["op"] == "db" and "SELECT" in s.get("description", "") - ] - assert len(spans) == 1 - assert ( - spans[0]["description"] == "SELECT id, name FROM users WHERE name = 'Alice'" - ) + for span in non_segment_spans: + assert span["attributes"]["sentry.origin"] == "auto.db.aiomysql" @pytest.mark.asyncio -async def test_before_send_transaction_sees_normalized_description( - sentry_init, capture_events -): - def before_send_transaction(event, hint): - for span in event.get("spans", []): - desc = span.get("description", "") - if "SELECT id, name FROM users" in desc: - span["description"] = "filtered" - return event - +async def test_multiline_query_description_normalized(sentry_init, capture_items): sentry_init( integrations=[AioMySQLIntegration()], traces_sample_rate=1.0, - before_send_transaction=before_send_transaction, + trace_lifecycle="stream", ) - events = capture_events() - with start_transaction(name="test_transaction"): + items = capture_items("span") + + with sentry_sdk.traces.start_span(name="test_segment"): conn = await aiomysql.connect(**_connect_args()) async with conn.cursor() as cur: @@ -1317,79 +1013,53 @@ def before_send_transaction(event, hint): name FROM users + WHERE + name = 'Alice' """ ) conn.close() - (event,) = events - spans = [ - s - for s in event["spans"] - if s["op"] == "db" and "filtered" in s.get("description", "") - ] + sentry_sdk.flush() - assert len(spans) == 1 - assert spans[0]["description"] == "filtered" + spans = [item.payload for item in items] + + select_spans = [ + s for s in spans if not s.get("is_segment") and "SELECT" in s.get("name", "") + ] + assert len(select_spans) == 1 + assert select_spans[0]["name"] == "SELECT id, name FROM users WHERE name = 'Alice'" @pytest.mark.asyncio -@pytest.mark.parametrize("span_streaming", [True, False]) -async def test_db_data_on_spans( - sentry_init, capture_events, capture_items, span_streaming -): +async def test_db_data_on_spans(sentry_init, capture_items): """Test that database connection data is properly set on spans.""" sentry_init( integrations=[AioMySQLIntegration()], traces_sample_rate=1.0, - trace_lifecycle="stream" if span_streaming else "static", + trace_lifecycle="stream", ) - if span_streaming: - items = capture_items("span") + items = capture_items("span") - with sentry_sdk.traces.start_span(name="test_segment"): - conn = await aiomysql.connect(**_connect_args()) - - async with conn.cursor() as cur: - await cur.execute("SELECT 1") - - conn.close() - - sentry_sdk.flush() - - spans = [item.payload for item in items] - - query_span = [ - s - for s in spans - if not s.get("is_segment") and "SELECT" in s.get("name", "") - ][0] - data = query_span.get("attributes", {}) - assert data.get(SPANDATA.DB_SYSTEM_NAME) == "mysql" - assert data.get(SPANDATA.SERVER_ADDRESS) == MYSQL_HOST - assert data.get(SPANDATA.SERVER_PORT) == MYSQL_PORT - assert data.get(SPANDATA.DB_NAMESPACE) == MYSQL_DB - assert data.get(SPANDATA.DB_USER) == MYSQL_USER - else: - events = capture_events() - - with start_transaction(name="test_transaction"): - conn = await aiomysql.connect(**_connect_args()) + with sentry_sdk.traces.start_span(name="test_segment"): + conn = await aiomysql.connect(**_connect_args()) - async with conn.cursor() as cur: - await cur.execute("SELECT 1") + async with conn.cursor() as cur: + await cur.execute("SELECT 1") - conn.close() + conn.close() - (event,) = events + sentry_sdk.flush() - db_spans = [s for s in event["spans"] if s["op"] == "db"] - assert len(db_spans) > 0 + spans = [item.payload for item in items] - query_span = [s for s in db_spans if "SELECT" in s.get("description", "")][0] - assert query_span["data"].get(SPANDATA.DB_SYSTEM) == "mysql" - assert query_span["data"].get(SPANDATA.SERVER_ADDRESS) == MYSQL_HOST - assert query_span["data"].get(SPANDATA.SERVER_PORT) == MYSQL_PORT - assert query_span["data"].get(SPANDATA.DB_NAME) == MYSQL_DB - assert query_span["data"].get(SPANDATA.DB_USER) == MYSQL_USER + query_span = [ + s for s in spans if not s.get("is_segment") and "SELECT" in s.get("name", "") + ][0] + data = query_span.get("attributes", {}) + assert data.get(SPANDATA.DB_SYSTEM_NAME) == "mysql" + assert data.get(SPANDATA.SERVER_ADDRESS) == MYSQL_HOST + assert data.get(SPANDATA.SERVER_PORT) == MYSQL_PORT + assert data.get(SPANDATA.DB_NAMESPACE) == MYSQL_DB + assert data.get(SPANDATA.DB_USER) == MYSQL_USER From a1f556848733d738dbde07868122cd8a2f3ae41b Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Thu, 13 Aug 2026 14:50:25 +0200 Subject: [PATCH 2/2] . --- tests/integrations/aiomysql/test_aiomysql.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integrations/aiomysql/test_aiomysql.py b/tests/integrations/aiomysql/test_aiomysql.py index 977ca46d09..21d1d9e6f2 100644 --- a/tests/integrations/aiomysql/test_aiomysql.py +++ b/tests/integrations/aiomysql/test_aiomysql.py @@ -44,8 +44,8 @@ def _get_db_name(): "category": "query", "data": ApproxDict( { - "db.name": MYSQL_DB, - "db.system": "mysql", + "db.namespace": MYSQL_DB, + "db.system.name": "mysql", "db.user": MYSQL_USER, "server.address": MYSQL_HOST, "server.port": MYSQL_PORT, @@ -684,7 +684,7 @@ async def test_connection_pool(sentry_init, capture_events) -> None: connect_crumbs = [c for c in crumbs if c.get("message") == "connect"] assert len(connect_crumbs) >= pool_size # One connect span per pooled connection for crumb in connect_crumbs: - assert crumb["data"]["db.system"] == "mysql" + assert crumb["data"]["db.system.name"] == "mysql" assert crumb["data"]["server.address"] == MYSQL_HOST