diff --git a/sentry_sdk/integrations/aiohttp.py b/sentry_sdk/integrations/aiohttp.py index d06117a19f..9c21952119 100644 --- a/sentry_sdk/integrations/aiohttp.py +++ b/sentry_sdk/integrations/aiohttp.py @@ -184,17 +184,23 @@ async def sentry_app_handle( ) if filtered_query_string: url_attributes["url.query"] = filtered_query_string + url_attributes["url.full"] += ( + "?" + filtered_query_string + ) + elif should_send_default_pii(): - url_attributes["url.full"] = "%s://%s%s" % ( + url_full = "%s://%s%s" % ( request.scheme, request.host, request.path, ) - url_attributes["url.path"] = request.path - if request.query_string: + url_full += "?" + request.query_string url_attributes["url.query"] = request.query_string + url_attributes["url.full"] = url_full + url_attributes["url.path"] = request.path + client_address_attributes = {} if should_send_default_pii() and request.remote: client_address_attributes["client.address"] = request.remote @@ -387,12 +393,9 @@ async def on_request_start( } if parsed_url is not None: if has_data_collection_enabled(client.options): - attributes["url.full"] = parsed_url.url + url_full = parsed_url.url attributes["url.path"] = params.url.path - if parsed_url.fragment: - attributes["url.fragment"] = parsed_url.fragment - if parsed_url.query: filtered_query = ( _apply_data_collection_filtering_to_query_string( @@ -404,15 +407,26 @@ async def on_request_start( ) if filtered_query: attributes["url.query"] = filtered_query + url_full += "?" + filtered_query + + if parsed_url.fragment: + attributes["url.fragment"] = parsed_url.fragment + url_full += "#" + parsed_url.fragment + + attributes["url.full"] = url_full elif should_send_default_pii(): - attributes["url.full"] = parsed_url.url + url_full = parsed_url.url attributes["url.path"] = params.url.path if parsed_url.query: + url_full += "?" + parsed_url.query attributes["url.query"] = parsed_url.query if parsed_url.fragment: + url_full += "#" + parsed_url.fragment attributes["url.fragment"] = parsed_url.fragment + attributes["url.full"] = url_full + span = sentry_sdk.traces.start_span( name=span_name, attributes=attributes ) diff --git a/sentry_sdk/integrations/httpx.py b/sentry_sdk/integrations/httpx.py index 779302a238..333754e209 100644 --- a/sentry_sdk/integrations/httpx.py +++ b/sentry_sdk/integrations/httpx.py @@ -76,7 +76,13 @@ def send(self: "Client", request: "Request", **kwargs: "Any") -> "Response": attributes: "Attributes" = {} if parsed_url is not None and should_send_default_pii(): - attributes["url.full"] = parsed_url.url + url_full = parsed_url.url + if parsed_url.query: + url_full += "?" + parsed_url.query + if parsed_url.fragment: + url_full += "#" + parsed_url.fragment + + attributes["url.full"] = url_full if parsed_url.query: attributes["url.query"] = parsed_url.query if parsed_url.fragment: @@ -162,7 +168,13 @@ async def send( attributes: "Attributes" = {} if parsed_url is not None and should_send_default_pii(): - attributes["url.full"] = parsed_url.url + url_full = parsed_url.url + if parsed_url.query: + url_full += "?" + parsed_url.query + if parsed_url.fragment: + url_full += "#" + parsed_url.fragment + + attributes["url.full"] = url_full if parsed_url.query: attributes["url.query"] = parsed_url.query if parsed_url.fragment: diff --git a/sentry_sdk/integrations/httpx2.py b/sentry_sdk/integrations/httpx2.py index 1ce3e93524..b658ce8ba4 100644 --- a/sentry_sdk/integrations/httpx2.py +++ b/sentry_sdk/integrations/httpx2.py @@ -77,7 +77,13 @@ def send(self: "Client", request: "Request", **kwargs: "Any") -> "Response": attributes: "Attributes" = {} if parsed_url is not None and should_send_default_pii(): - attributes["url.full"] = parsed_url.url + url_full = parsed_url.url + if parsed_url.query: + url_full += "?" + parsed_url.query + if parsed_url.fragment: + url_full += "#" + parsed_url.fragment + + attributes["url.full"] = url_full if parsed_url.query: attributes["url.query"] = parsed_url.query if parsed_url.fragment: @@ -164,7 +170,13 @@ async def send( attributes: "Attributes" = {} if parsed_url is not None and should_send_default_pii(): - attributes["url.full"] = parsed_url.url + url_full = parsed_url.url + if parsed_url.query: + url_full += "?" + parsed_url.query + if parsed_url.fragment: + url_full += "#" + parsed_url.fragment + + attributes["url.full"] = url_full if parsed_url.query: attributes["url.query"] = parsed_url.query if parsed_url.fragment: diff --git a/sentry_sdk/integrations/wsgi.py b/sentry_sdk/integrations/wsgi.py index a1e4481bc6..b0156a9829 100644 --- a/sentry_sdk/integrations/wsgi.py +++ b/sentry_sdk/integrations/wsgi.py @@ -444,6 +444,7 @@ def _get_request_attributes( if has_data_collection_enabled(client_options): query_string = environ.get("QUERY_STRING") + filtered_qs = None if query_string: filtered_qs = _apply_data_collection_filtering_to_query_string( query_string=query_string, @@ -458,6 +459,8 @@ def _get_request_attributes( attributes["url.path"] = path attributes["url.full"] = get_request_url(environ, use_x_forwarded_for) + if filtered_qs is not None: + attributes["url.full"] += f"?{filtered_qs}" if client_options["data_collection"]["user_info"]: client_ip = get_client_ip(environ) @@ -477,6 +480,9 @@ def _get_request_attributes( if path: attributes["url.path"] = path - attributes["url.full"] = get_request_url(environ, use_x_forwarded_for) + url_full = get_request_url(environ, use_x_forwarded_for) + if query_string: + url_full += "?" + query_string + attributes["url.full"] = url_full return attributes diff --git a/tests/integrations/aiohttp/test_aiohttp.py b/tests/integrations/aiohttp/test_aiohttp.py index 7444d0ae5a..34fcf6892c 100644 --- a/tests/integrations/aiohttp/test_aiohttp.py +++ b/tests/integrations/aiohttp/test_aiohttp.py @@ -1717,10 +1717,8 @@ async def hello(request): 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 "?foo=bar" in url_full assert inner_client_span["attributes"]["url.path"] == "/" diff --git a/tests/integrations/httpx/test_httpx.py b/tests/integrations/httpx/test_httpx.py index 1470059137..7b82c553bc 100644 --- a/tests/integrations/httpx/test_httpx.py +++ b/tests/integrations/httpx/test_httpx.py @@ -1246,7 +1246,7 @@ def test_http_url_attributes_span_streaming( assert http_span["attributes"]["http.response.status_code"] == 200 if send_default_pii: - assert http_span["attributes"]["url.full"] == "http://example.com/" + assert http_span["attributes"]["url.full"] == "http://example.com/?foo=bar#frag" assert http_span["attributes"]["url.query"] == "foo=bar" assert http_span["attributes"]["url.fragment"] == "frag" else: diff --git a/tests/integrations/httpx2/test_httpx2.py b/tests/integrations/httpx2/test_httpx2.py index e8e8da54a3..cad039e108 100644 --- a/tests/integrations/httpx2/test_httpx2.py +++ b/tests/integrations/httpx2/test_httpx2.py @@ -1234,7 +1234,7 @@ def test_http_url_attributes_span_streaming( http_span = _get_http_client_span(items) assert http_span["attributes"]["http.request.method"] == "GET" - assert http_span["attributes"]["url.full"] == "http://example.com/" + assert http_span["attributes"]["url.full"] == "http://example.com/?foo=bar#frag" assert http_span["attributes"]["url.query"] == "foo=bar" assert http_span["attributes"]["url.fragment"] == "frag" assert http_span["attributes"]["http.response.status_code"] == 200 diff --git a/tests/integrations/wsgi/test_wsgi.py b/tests/integrations/wsgi/test_wsgi.py index 2fe2ebfea5..084abe0745 100644 --- a/tests/integrations/wsgi/test_wsgi.py +++ b/tests/integrations/wsgi/test_wsgi.py @@ -254,7 +254,10 @@ def dogpark(environ, start_response): assert span["status"] == "ok" if send_pii: - assert span["attributes"]["url.full"] == "http://localhost/dogs/are/great" + assert ( + span["attributes"]["url.full"] + == "http://localhost/dogs/are/great?toy=tennisball" + ) assert span["attributes"]["url.path"] == "/dogs/are/great" assert span["attributes"]["http.query"] == "toy=tennisball" else: