Skip to content

Commit 9667284

Browse files
sentrivanaclaude
andauthored
fix: Include query string and fragment in url.full span attribute (#6861)
The `url.full` span attribute was only being set to the base URL (scheme + host + path), omitting query string and fragment. Closes #6860 --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a249634 commit 9667284

8 files changed

Lines changed: 64 additions & 19 deletions

File tree

sentry_sdk/integrations/aiohttp.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -184,17 +184,23 @@ async def sentry_app_handle(
184184
)
185185
if filtered_query_string:
186186
url_attributes["url.query"] = filtered_query_string
187+
url_attributes["url.full"] += (
188+
"?" + filtered_query_string
189+
)
190+
187191
elif should_send_default_pii():
188-
url_attributes["url.full"] = "%s://%s%s" % (
192+
url_full = "%s://%s%s" % (
189193
request.scheme,
190194
request.host,
191195
request.path,
192196
)
193-
url_attributes["url.path"] = request.path
194-
195197
if request.query_string:
198+
url_full += "?" + request.query_string
196199
url_attributes["url.query"] = request.query_string
197200

201+
url_attributes["url.full"] = url_full
202+
url_attributes["url.path"] = request.path
203+
198204
client_address_attributes = {}
199205
if should_send_default_pii() and request.remote:
200206
client_address_attributes["client.address"] = request.remote
@@ -387,12 +393,9 @@ async def on_request_start(
387393
}
388394
if parsed_url is not None:
389395
if has_data_collection_enabled(client.options):
390-
attributes["url.full"] = parsed_url.url
396+
url_full = parsed_url.url
391397
attributes["url.path"] = params.url.path
392398

393-
if parsed_url.fragment:
394-
attributes["url.fragment"] = parsed_url.fragment
395-
396399
if parsed_url.query:
397400
filtered_query = (
398401
_apply_data_collection_filtering_to_query_string(
@@ -404,15 +407,26 @@ async def on_request_start(
404407
)
405408
if filtered_query:
406409
attributes["url.query"] = filtered_query
410+
url_full += "?" + filtered_query
411+
412+
if parsed_url.fragment:
413+
attributes["url.fragment"] = parsed_url.fragment
414+
url_full += "#" + parsed_url.fragment
415+
416+
attributes["url.full"] = url_full
407417
elif should_send_default_pii():
408-
attributes["url.full"] = parsed_url.url
418+
url_full = parsed_url.url
409419
attributes["url.path"] = params.url.path
410420

411421
if parsed_url.query:
422+
url_full += "?" + parsed_url.query
412423
attributes["url.query"] = parsed_url.query
413424
if parsed_url.fragment:
425+
url_full += "#" + parsed_url.fragment
414426
attributes["url.fragment"] = parsed_url.fragment
415427

428+
attributes["url.full"] = url_full
429+
416430
span = sentry_sdk.traces.start_span(
417431
name=span_name, attributes=attributes
418432
)

sentry_sdk/integrations/httpx.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,13 @@ def send(self: "Client", request: "Request", **kwargs: "Any") -> "Response":
7676
attributes: "Attributes" = {}
7777

7878
if parsed_url is not None and should_send_default_pii():
79-
attributes["url.full"] = parsed_url.url
79+
url_full = parsed_url.url
80+
if parsed_url.query:
81+
url_full += "?" + parsed_url.query
82+
if parsed_url.fragment:
83+
url_full += "#" + parsed_url.fragment
84+
85+
attributes["url.full"] = url_full
8086
if parsed_url.query:
8187
attributes["url.query"] = parsed_url.query
8288
if parsed_url.fragment:
@@ -162,7 +168,13 @@ async def send(
162168
attributes: "Attributes" = {}
163169

164170
if parsed_url is not None and should_send_default_pii():
165-
attributes["url.full"] = parsed_url.url
171+
url_full = parsed_url.url
172+
if parsed_url.query:
173+
url_full += "?" + parsed_url.query
174+
if parsed_url.fragment:
175+
url_full += "#" + parsed_url.fragment
176+
177+
attributes["url.full"] = url_full
166178
if parsed_url.query:
167179
attributes["url.query"] = parsed_url.query
168180
if parsed_url.fragment:

sentry_sdk/integrations/httpx2.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,13 @@ def send(self: "Client", request: "Request", **kwargs: "Any") -> "Response":
7777
attributes: "Attributes" = {}
7878

7979
if parsed_url is not None and should_send_default_pii():
80-
attributes["url.full"] = parsed_url.url
80+
url_full = parsed_url.url
81+
if parsed_url.query:
82+
url_full += "?" + parsed_url.query
83+
if parsed_url.fragment:
84+
url_full += "#" + parsed_url.fragment
85+
86+
attributes["url.full"] = url_full
8187
if parsed_url.query:
8288
attributes["url.query"] = parsed_url.query
8389
if parsed_url.fragment:
@@ -164,7 +170,13 @@ async def send(
164170
attributes: "Attributes" = {}
165171

166172
if parsed_url is not None and should_send_default_pii():
167-
attributes["url.full"] = parsed_url.url
173+
url_full = parsed_url.url
174+
if parsed_url.query:
175+
url_full += "?" + parsed_url.query
176+
if parsed_url.fragment:
177+
url_full += "#" + parsed_url.fragment
178+
179+
attributes["url.full"] = url_full
168180
if parsed_url.query:
169181
attributes["url.query"] = parsed_url.query
170182
if parsed_url.fragment:

sentry_sdk/integrations/wsgi.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ def _get_request_attributes(
444444

445445
if has_data_collection_enabled(client_options):
446446
query_string = environ.get("QUERY_STRING")
447+
filtered_qs = None
447448
if query_string:
448449
filtered_qs = _apply_data_collection_filtering_to_query_string(
449450
query_string=query_string,
@@ -458,6 +459,8 @@ def _get_request_attributes(
458459
attributes["url.path"] = path
459460

460461
attributes["url.full"] = get_request_url(environ, use_x_forwarded_for)
462+
if filtered_qs is not None:
463+
attributes["url.full"] += f"?{filtered_qs}"
461464

462465
if client_options["data_collection"]["user_info"]:
463466
client_ip = get_client_ip(environ)
@@ -477,6 +480,9 @@ def _get_request_attributes(
477480
if path:
478481
attributes["url.path"] = path
479482

480-
attributes["url.full"] = get_request_url(environ, use_x_forwarded_for)
483+
url_full = get_request_url(environ, use_x_forwarded_for)
484+
if query_string:
485+
url_full += "?" + query_string
486+
attributes["url.full"] = url_full
481487

482488
return attributes

tests/integrations/aiohttp/test_aiohttp.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1717,10 +1717,8 @@ async def hello(request):
17171717

17181718
url_full = inner_client_span["attributes"]["url.full"]
17191719

1720-
# parse_url() splits the URL — url.full is the base URL only, with the
1721-
# query string captured separately on url.query.
17221720
assert url_full.startswith("http://127.0.0.1:")
1723-
assert url_full.endswith("/")
1721+
assert "?foo=bar" in url_full
17241722

17251723
assert inner_client_span["attributes"]["url.path"] == "/"
17261724

tests/integrations/httpx/test_httpx.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1246,7 +1246,7 @@ def test_http_url_attributes_span_streaming(
12461246
assert http_span["attributes"]["http.response.status_code"] == 200
12471247

12481248
if send_default_pii:
1249-
assert http_span["attributes"]["url.full"] == "http://example.com/"
1249+
assert http_span["attributes"]["url.full"] == "http://example.com/?foo=bar#frag"
12501250
assert http_span["attributes"]["url.query"] == "foo=bar"
12511251
assert http_span["attributes"]["url.fragment"] == "frag"
12521252
else:

tests/integrations/httpx2/test_httpx2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1234,7 +1234,7 @@ def test_http_url_attributes_span_streaming(
12341234
http_span = _get_http_client_span(items)
12351235

12361236
assert http_span["attributes"]["http.request.method"] == "GET"
1237-
assert http_span["attributes"]["url.full"] == "http://example.com/"
1237+
assert http_span["attributes"]["url.full"] == "http://example.com/?foo=bar#frag"
12381238
assert http_span["attributes"]["url.query"] == "foo=bar"
12391239
assert http_span["attributes"]["url.fragment"] == "frag"
12401240
assert http_span["attributes"]["http.response.status_code"] == 200

tests/integrations/wsgi/test_wsgi.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,10 @@ def dogpark(environ, start_response):
254254
assert span["status"] == "ok"
255255

256256
if send_pii:
257-
assert span["attributes"]["url.full"] == "http://localhost/dogs/are/great"
257+
assert (
258+
span["attributes"]["url.full"]
259+
== "http://localhost/dogs/are/great?toy=tennisball"
260+
)
258261
assert span["attributes"]["url.path"] == "/dogs/are/great"
259262
assert span["attributes"]["http.query"] == "toy=tennisball"
260263
else:

0 commit comments

Comments
 (0)