diff --git a/sentry_sdk/integrations/aiohttp.py b/sentry_sdk/integrations/aiohttp.py index f905420d87..d22f3a745b 100644 --- a/sentry_sdk/integrations/aiohttp.py +++ b/sentry_sdk/integrations/aiohttp.py @@ -159,11 +159,17 @@ async def sentry_app_handle( header_value ) - url_query_attribute = ( - {"url.query": request.query_string} - if request.query_string - else {} - ) + url_attributes = {} + if should_send_default_pii(): + url_attributes["url.full"] = "%s://%s%s" % ( + request.scheme, + request.host, + request.path, + ) + url_attributes["url.path"] = request.path + + if request.query_string: + url_attributes["url.query"] = request.query_string client_address_attributes = {} if should_send_default_pii() and request.remote: @@ -180,10 +186,8 @@ async def sentry_app_handle( "sentry.op": OP.HTTP_SERVER, "sentry.origin": AioHttpIntegration.origin, "sentry.span.source": SegmentSource.ROUTE.value, - "url.full": "%s://%s%s" - % (request.scheme, request.host, request.path), "http.request.method": request.method, - **url_query_attribute, + **url_attributes, **client_address_attributes, **header_attributes, }, @@ -354,8 +358,10 @@ async def on_request_start( "sentry.origin": AioHttpIntegration.origin, "http.request.method": method, } - if parsed_url is not None: + if parsed_url is not None and should_send_default_pii(): attributes["url.full"] = parsed_url.url + attributes["url.path"] = params.url.path + if parsed_url.query: attributes["url.query"] = parsed_url.query if parsed_url.fragment: diff --git a/tests/integrations/aiohttp/test_aiohttp.py b/tests/integrations/aiohttp/test_aiohttp.py index 70f76204cc..583e7a50b6 100644 --- a/tests/integrations/aiohttp/test_aiohttp.py +++ b/tests/integrations/aiohttp/test_aiohttp.py @@ -1137,10 +1137,14 @@ async def handle(_): @pytest.mark.asyncio -async def test_tracing_span_streaming(sentry_init, aiohttp_client, capture_items): +@pytest.mark.parametrize("send_pii", [True, False]) +async def test_tracing_span_streaming( + sentry_init, aiohttp_client, capture_items, send_pii +): sentry_init( integrations=[AioHttpIntegration()], traces_sample_rate=1.0, + send_default_pii=send_pii, _experiments={"trace_lifecycle": "stream"}, ) @@ -1184,13 +1188,25 @@ async def hello(request): # Request attributes derived directly from the aiohttp request. assert server_span["attributes"]["http.request.method"] == "GET" - # client.address and user.ip_address is gated on send_default_pii (default False), so it must - # not be captured here. - assert "client.address" not in server_span["attributes"] - assert "user.ip_address" not in server_span["attributes"] - url_full = server_span["attributes"]["url.full"] - assert url_full.startswith("http://127.0.0.1:") - assert url_full.endswith("/") + + if send_pii: + assert "client.address" in server_span["attributes"] + assert "user.ip_address" in server_span["attributes"] + + url_full = server_span["attributes"]["url.full"] + assert url_full.startswith("http://127.0.0.1:") + assert url_full.endswith("/") + + url_path = server_span["attributes"]["url.path"] + assert url_path == "/" + else: + assert "url.full" not in server_span["attributes"] + assert "url.path" not in server_span["attributes"] + assert "url.query" not in server_span["attributes"] + + assert "client.address" not in server_span["attributes"] + assert "user.ip_address" not in server_span["attributes"] + # aiohttp's test client always sends a Host header; we assert it propagates # into the span attributes via _filter_headers. assert "http.request.header.host" in server_span["attributes"] @@ -1280,12 +1296,14 @@ async def hello(request): @pytest.mark.asyncio +@pytest.mark.parametrize("send_pii", [True, False]) async def test_url_query_attribute_span_streaming( - sentry_init, aiohttp_client, capture_items + sentry_init, aiohttp_client, capture_items, send_pii ): sentry_init( integrations=[AioHttpIntegration()], traces_sample_rate=1.0, + send_default_pii=send_pii, _experiments={"trace_lifecycle": "stream"}, ) @@ -1306,7 +1324,10 @@ async def hello(request): assert len(items) == 2 server_segment, client_segment = [item.payload for item in items] - assert server_segment["attributes"]["url.query"] == "foo=bar&baz=qux" + if send_pii: + assert server_segment["attributes"]["url.query"] == "foo=bar&baz=qux" + else: + assert "url.query" not in server_segment["attributes"] @pytest.mark.asyncio @@ -1486,12 +1507,14 @@ async def hello(request): @pytest.mark.asyncio +@pytest.mark.parametrize("send_pii", [True, False]) async def test_outgoing_client_span_span_streaming( - sentry_init, aiohttp_raw_server, aiohttp_client, capture_items + sentry_init, aiohttp_raw_server, aiohttp_client, capture_items, send_pii ): sentry_init( integrations=[AioHttpIntegration()], traces_sample_rate=1.0, + send_default_pii=send_pii, _experiments={"trace_lifecycle": "stream"}, ) @@ -1536,14 +1559,19 @@ async def hello(request): assert inner_client_span["attributes"]["sentry.origin"] == "auto.http.aiohttp" assert inner_client_span["attributes"]["http.request.method"] == "GET" assert inner_client_span["attributes"]["http.response.status_code"] == 200 - assert inner_client_span["attributes"]["url.query"] == "foo=bar" assert inner_client_span["status"] == "ok" - url_full = inner_client_span["attributes"]["url.full"] - # parse_url() splits the URL — url.full is the base URL only, with the - # query string captured separately on url.query. - assert url_full.startswith("http://127.0.0.1:") - assert url_full.endswith("/") + if send_pii: + assert inner_client_span["attributes"]["url.query"] == "foo=bar" + + url_full = inner_client_span["attributes"]["url.full"] + + # parse_url() splits the URL — url.full is the base URL only, with the + # query string captured separately on url.query. + assert url_full.startswith("http://127.0.0.1:") + assert url_full.endswith("/") + + assert inner_client_span["attributes"]["url.path"] == "/" @pytest.mark.asyncio