From 038823774e7d32379775cec1d80205a07759f239 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Wed, 30 Sep 2026 14:35:46 +0200 Subject: [PATCH 1/2] feat(wsgi): Add `user_agent.original` attr for inbound filters --- sentry_sdk/consts.py | 7 +++++++ sentry_sdk/integrations/wsgi.py | 4 ++++ tests/integrations/wsgi/test_wsgi.py | 30 +++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/consts.py b/sentry_sdk/consts.py index 20ae39c83a..9bad8aa7db 100644 --- a/sentry_sdk/consts.py +++ b/sentry_sdk/consts.py @@ -1202,6 +1202,13 @@ class SPANDATA: Example: ["AtexitIntegration", "StdlibIntegration"] """ + USER_AGENT_ORIGINAL = "user_agent.original" + """ + Value of the HTTP User-Agent header sent by the client. + + Used in inbound filters. + """ + class SPANSTATUS: """ diff --git a/sentry_sdk/integrations/wsgi.py b/sentry_sdk/integrations/wsgi.py index ab04308f6e..db1ee06a4e 100644 --- a/sentry_sdk/integrations/wsgi.py +++ b/sentry_sdk/integrations/wsgi.py @@ -138,6 +138,10 @@ def __call__( forwarded_host_header=environ.get("HTTP_X_FORWARDED_HOST"), ), ) + if environ.get("HTTP_USER_AGENT"): + scope.set_attribute( + SPANDATA.USER_AGENT_ORIGINAL, environ["HTTP_USER_AGENT"] + ) method = environ.get("REQUEST_METHOD", "").upper() diff --git a/tests/integrations/wsgi/test_wsgi.py b/tests/integrations/wsgi/test_wsgi.py index c0556aa37a..5a14a3c8a3 100644 --- a/tests/integrations/wsgi/test_wsgi.py +++ b/tests/integrations/wsgi/test_wsgi.py @@ -1361,7 +1361,7 @@ def test_error_event_no_user_ip_address_without_remote_addr( ), ], ) -def test_is_localhost_span_attribute( +def test_is_localhost_attribute( sentry_init, capture_items, client_kwargs, is_localhost ): def dogpark(environ, start_response): @@ -1384,3 +1384,31 @@ def dogpark(environ, start_response): assert server_span["attributes"]["sentry.is_localhost"] is is_localhost assert child_span["attributes"]["sentry.is_localhost"] is is_localhost + + +def test_user_agent_attribute(sentry_init, capture_items): + def dogpark(environ, start_response): + with sentry_sdk.traces.start_span(name="child-span"): + pass + start_response("200 OK", []) + return ["woof"] + + sentry_init( + traces_sample_rate=1.0, + trace_lifecycle="stream", + ) + + app = SentryWsgiMiddleware(dogpark) + client = Client(app) + + items = capture_items("span") + + user_agent = "Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Mobile/15E148 Safari/604.1" + client.get("/dogs/", headers={"User-Agent": user_agent}) + + sentry_sdk.flush() + + child_span, server_span = [item.payload for item in items] + + assert server_span["attributes"]["user_agent.original"] is user_agent + assert child_span["attributes"]["user_agent.original"] is user_agent From 982ec87dca4f38da6b7f91015f3093b1e003fcf2 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Wed, 30 Sep 2026 14:39:25 +0200 Subject: [PATCH 2/2] . --- tests/integrations/wsgi/test_wsgi.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/integrations/wsgi/test_wsgi.py b/tests/integrations/wsgi/test_wsgi.py index 5a14a3c8a3..2cda3a1b98 100644 --- a/tests/integrations/wsgi/test_wsgi.py +++ b/tests/integrations/wsgi/test_wsgi.py @@ -1403,12 +1403,11 @@ def dogpark(environ, start_response): items = capture_items("span") - user_agent = "Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Mobile/15E148 Safari/604.1" - client.get("/dogs/", headers={"User-Agent": user_agent}) + client.get("/dogs/", headers={"User-Agent": "TestBrowser/1.0"}) sentry_sdk.flush() child_span, server_span = [item.payload for item in items] - assert server_span["attributes"]["user_agent.original"] is user_agent - assert child_span["attributes"]["user_agent.original"] is user_agent + assert server_span["attributes"]["user_agent.original"] == "TestBrowser/1.0" + assert child_span["attributes"]["user_agent.original"] == "TestBrowser/1.0"