From f1def76e7f769d516b4042d4933be0633c9a98ca Mon Sep 17 00:00:00 2001 From: David Herberth Date: Thu, 13 Aug 2026 14:30:22 +0200 Subject: [PATCH 1/2] fix(filters): Improve inbound filters for EAP items --- CHANGELOG.md | 1 + relay-filter/src/interface.rs | 96 ++++++++++++++----------- tests/integration/test_ourlogs.py | 57 +++++++++++++++ tests/integration/test_spansv2.py | 38 ++++++++++ tests/integration/test_trace_metrics.py | 52 +++++++++++++- 5 files changed, 201 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a08f6ecb18b..e8da51cc5ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Reject spans, logs, trace metrics, replays when they are too old instead of shifting their timestamp. ([#6272](https://github.com/getsentry/relay/pull/6272)) - Extract nvgpu dumps and create GPU events. ([#6242](https://github.com/getsentry/relay/pull/6242)) +- Add support for more inbound filters for logs, trace metrics and spans. ([#6306](https://github.com/getsentry/relay/pull/6306)) - Preserve transaction `contexts`, `extra`, and `breadcrumbs` on the segment span as serialized attributes. ([#6286](https://github.com/getsentry/relay/pull/6286)) **Bug Fixes**: diff --git a/relay-filter/src/interface.rs b/relay-filter/src/interface.rs index b0636b6f287..7c4ff717232 100644 --- a/relay-filter/src/interface.rs +++ b/relay-filter/src/interface.rs @@ -1,9 +1,12 @@ //! This module contains the trait for items that can be filtered by Inbound Filters, plus //! the implementation for [`Event`]. +use std::sync::LazyLock; + use relay_conventions::attributes::{ BROWSER__NAME, BROWSER__VERSION, CLIENT__ADDRESS, SENTRY__RELEASE, SENTRY__SEGMENT__NAME, URL__FULL, USER_AGENT__ORIGINAL, }; +use relay_conventions::interpolate::http__request__header__key; use url::Url; use relay_event_schema::protocol::{ @@ -196,26 +199,6 @@ impl Filterable for Span { } } -impl Filterable for SpanV2 { - fn release(&self) -> Option<&str> { - self.attributes - .value()? - .get_value(SENTRY__RELEASE)? - .as_str() - } - - fn transaction(&self) -> Option<&str> { - self.attributes - .value()? - .get_value(SENTRY__SEGMENT__NAME)? - .as_str() - } - - fn user_agent(&self) -> UserAgent<'_> { - user_agent_from_attributes(&self.attributes) - } -} - impl Filterable for SessionUpdate { fn ip_addr(&self) -> Option<&str> { self.attributes @@ -256,32 +239,51 @@ impl Filterable for SessionAggregates { } } -impl Filterable for OurLog { - fn release(&self) -> Option<&str> { - self.attributes - .value()? - .get_value(SENTRY__RELEASE)? - .as_str() - } +macro_rules! impl_for_attributes { + ($ty:ty) => { + impl Filterable for $ty { + fn ip_addr(&self) -> Option<&str> { + self.attributes + .value()? + .get_value(CLIENT__ADDRESS)? + .as_str() + } - fn user_agent(&self) -> UserAgent<'_> { - user_agent_from_attributes(&self.attributes) - } -} + fn release(&self) -> Option<&str> { + self.attributes + .value()? + .get_value(SENTRY__RELEASE)? + .as_str() + } -impl Filterable for TraceMetric { - fn release(&self) -> Option<&str> { - self.attributes - .value()? - .get_value(SENTRY__RELEASE)? - .as_str() - } + fn transaction(&self) -> Option<&str> { + self.attributes + .value()? + .get_value(SENTRY__SEGMENT__NAME)? + .as_str() + } - fn user_agent(&self) -> UserAgent<'_> { - user_agent_from_attributes(&self.attributes) - } + fn url(&self) -> Option { + let url = self.attributes.value()?.get_value(URL__FULL)?.as_str()?; + Url::parse(url).ok() + } + + fn user_agent(&self) -> UserAgent<'_> { + user_agent_from_attributes(&self.attributes) + } + + fn header(&self, header_name: &str) -> Option<&str> { + let key = http__request__header__key(header_name); + self.attributes.value()?.get_value(&key)?.as_str() + } + } + }; } +impl_for_attributes!(SpanV2); +impl_for_attributes!(OurLog); +impl_for_attributes!(TraceMetric); + fn user_agent_from_attributes(attributes: &relay_protocol::Annotated) -> UserAgent<'_> { let parsed = (|| { let attributes = attributes.value()?; @@ -298,10 +300,20 @@ fn user_agent_from_attributes(attributes: &relay_protocol::Annotated }) })(); - let raw = attributes + static HTTP_USER_AGENT: LazyLock = + LazyLock::new(|| http__request__header__key("user-agent")); + + let ua_original = attributes .value() .and_then(|attr| attr.get_value(USER_AGENT__ORIGINAL)) .and_then(|ua| ua.as_str()); + let ua_header = attributes + .value() + .and_then(|attr| attr.get_value(&*HTTP_USER_AGENT)) + .and_then(|ua| ua.as_str()); + + let raw = ua_original.or(ua_header); + UserAgent { raw, parsed } } diff --git a/tests/integration/test_ourlogs.py b/tests/integration/test_ourlogs.py index 63e725f3d52..8b8be701126 100644 --- a/tests/integration/test_ourlogs.py +++ b/tests/integration/test_ourlogs.py @@ -937,6 +937,55 @@ def test_browser_name_version_extraction( {}, id="release", ), + pytest.param( + "filtered-transaction", + {"ignoreTransactions": {"isEnabled": True, "patterns": ["*health*"]}}, + { + "attributes": { + "sentry.segment.name": { + "value": "/foo/healthz", + "type": "string", + } + } + }, + id="transaction", + ), + pytest.param( + "localhost", + {"localhost": {"isEnabled": True}}, + { + "attributes": { + "client.address": {"value": "127.0.0.1", "type": "string"} + } + }, + id="localhost-ip", + ), + pytest.param( + "localhost", + {"localhost": {"isEnabled": True}}, + { + "attributes": { + "url.full": { + "value": "http://localhost:8000/foo", + "type": "string", + } + } + }, + id="localhost-url", + ), + pytest.param( + "localhost", + {"localhost": {"isEnabled": True}}, + { + "attributes": { + "http.request.header.Host": { + "value": "localhost:8000", + "type": "string", + } + } + }, + id="localhost-header", + ), pytest.param( "legacy-browsers", {"legacyBrowsers": {"isEnabled": True, "options": ["ie9"]}}, @@ -1021,6 +1070,14 @@ def test_filters_are_applied_to_logs( "attributes": { "some_integer": {"value": 123, "type": "integer"}, "sentry.release": {"value": "foobar@1.0", "type": "string"}, + **args.get("attributes", {}), + }, + }, + metadata={ + "version": 2, + "ingest_settings": { + "infer_ip": "never", + "infer_user_agent": "auto", }, }, ) diff --git a/tests/integration/test_spansv2.py b/tests/integration/test_spansv2.py index 656626903f7..3e62ec91f2c 100644 --- a/tests/integration/test_spansv2.py +++ b/tests/integration/test_spansv2.py @@ -1000,6 +1000,42 @@ def test_spansv2_ds_root_in_different_org( {}, id="transaction", ), + pytest.param( + "localhost", + {"localhost": {"isEnabled": True}}, + { + "attributes": { + "client.address": {"value": "127.0.0.1", "type": "string"} + } + }, + id="localhost-ip", + ), + pytest.param( + "localhost", + {"localhost": {"isEnabled": True}}, + { + "attributes": { + "url.full": { + "value": "http://localhost:8000/foo", + "type": "string", + } + } + }, + id="localhost-url", + ), + pytest.param( + "localhost", + {"localhost": {"isEnabled": True}}, + { + "attributes": { + "http.request.header.Host": { + "value": "localhost:8000", + "type": "string", + } + } + }, + id="localhost-header", + ), pytest.param( "legacy-browsers", {"legacyBrowsers": {"isEnabled": True, "options": ["ie9"]}}, @@ -1081,11 +1117,13 @@ def test_spanv2_inbound_filters( "some_integer": {"value": 123, "type": "integer"}, "sentry.release": {"value": "foobar@1.0", "type": "string"}, "sentry.segment.name": {"value": "/foo/healthz", "type": "string"}, + **args.get("attributes", {}), }, }, metadata={ "version": 2, "ingest_settings": { + "infer_ip": "never", "infer_user_agent": "auto", }, }, diff --git a/tests/integration/test_trace_metrics.py b/tests/integration/test_trace_metrics.py index ca2bec31bdc..8e80725c90a 100644 --- a/tests/integration/test_trace_metrics.py +++ b/tests/integration/test_trace_metrics.py @@ -1028,6 +1028,55 @@ def test_trace_metric_container_metadata( {}, id="release", ), + pytest.param( + "filtered-transaction", + {"ignoreTransactions": {"isEnabled": True, "patterns": ["*health*"]}}, + { + "attributes": { + "sentry.segment.name": { + "value": "/foo/healthz", + "type": "string", + } + } + }, + id="transaction", + ), + pytest.param( + "localhost", + {"localhost": {"isEnabled": True}}, + { + "attributes": { + "client.address": {"value": "127.0.0.1", "type": "string"} + } + }, + id="localhost-ip", + ), + pytest.param( + "localhost", + {"localhost": {"isEnabled": True}}, + { + "attributes": { + "url.full": { + "value": "http://localhost:8000/foo", + "type": "string", + } + } + }, + id="localhost-url", + ), + pytest.param( + "localhost", + {"localhost": {"isEnabled": True}}, + { + "attributes": { + "http.request.header.Host": { + "value": "localhost:8000", + "type": "string", + } + } + }, + id="localhost-header", + ), pytest.param( "legacy-browsers", {"legacyBrowsers": {"isEnabled": True, "options": ["ie9"]}}, @@ -1101,7 +1150,7 @@ def test_filters_are_applied_to_trace_metrics( metadata = { "version": 2, - "ingest_settings": {"infer_ip": "auto", "infer_user_agent": "auto"}, + "ingest_settings": {"infer_ip": "never", "infer_user_agent": "auto"}, } envelope = envelope_with_trace_metrics( @@ -1114,6 +1163,7 @@ def test_filters_are_applied_to_trace_metrics( "attributes": { "http.status_code": {"value": 500, "type": "integer"}, "sentry.release": {"value": "foobar@1.0", "type": "string"}, + **args.get("attributes", {}), }, }, metadata=metadata, From e6ee77dbfa70dda41957d660b71d14562a2a0c57 Mon Sep 17 00:00:00 2001 From: David Herberth Date: Tue, 25 Aug 2026 14:38:29 +0200 Subject: [PATCH 2/2] remove second useragent and headers --- CHANGELOG.md | 2 +- relay-filter/src/interface.rs | 20 +------------------- tests/integration/test_ourlogs.py | 13 ------------- tests/integration/test_spansv2.py | 13 ------------- tests/integration/test_trace_metrics.py | 13 ------------- 5 files changed, 2 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8da51cc5ef..1ffc9b66c13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ **Features**: - Raise the size limit for the flags context to 128 KiB. ([#6310](https://github.com/getsentry/relay/pull/6310)) +- Add support for more inbound filters for logs, trace metrics and spans. ([#6306](https://github.com/getsentry/relay/pull/6306)) ## 26.8.0 @@ -12,7 +13,6 @@ - Reject spans, logs, trace metrics, replays when they are too old instead of shifting their timestamp. ([#6272](https://github.com/getsentry/relay/pull/6272)) - Extract nvgpu dumps and create GPU events. ([#6242](https://github.com/getsentry/relay/pull/6242)) -- Add support for more inbound filters for logs, trace metrics and spans. ([#6306](https://github.com/getsentry/relay/pull/6306)) - Preserve transaction `contexts`, `extra`, and `breadcrumbs` on the segment span as serialized attributes. ([#6286](https://github.com/getsentry/relay/pull/6286)) **Bug Fixes**: diff --git a/relay-filter/src/interface.rs b/relay-filter/src/interface.rs index 7c4ff717232..2fe465c3579 100644 --- a/relay-filter/src/interface.rs +++ b/relay-filter/src/interface.rs @@ -1,12 +1,9 @@ //! This module contains the trait for items that can be filtered by Inbound Filters, plus //! the implementation for [`Event`]. -use std::sync::LazyLock; - use relay_conventions::attributes::{ BROWSER__NAME, BROWSER__VERSION, CLIENT__ADDRESS, SENTRY__RELEASE, SENTRY__SEGMENT__NAME, URL__FULL, USER_AGENT__ORIGINAL, }; -use relay_conventions::interpolate::http__request__header__key; use url::Url; use relay_event_schema::protocol::{ @@ -271,11 +268,6 @@ macro_rules! impl_for_attributes { fn user_agent(&self) -> UserAgent<'_> { user_agent_from_attributes(&self.attributes) } - - fn header(&self, header_name: &str) -> Option<&str> { - let key = http__request__header__key(header_name); - self.attributes.value()?.get_value(&key)?.as_str() - } } }; } @@ -300,20 +292,10 @@ fn user_agent_from_attributes(attributes: &relay_protocol::Annotated }) })(); - static HTTP_USER_AGENT: LazyLock = - LazyLock::new(|| http__request__header__key("user-agent")); - - let ua_original = attributes + let raw = attributes .value() .and_then(|attr| attr.get_value(USER_AGENT__ORIGINAL)) .and_then(|ua| ua.as_str()); - let ua_header = attributes - .value() - .and_then(|attr| attr.get_value(&*HTTP_USER_AGENT)) - .and_then(|ua| ua.as_str()); - - let raw = ua_original.or(ua_header); - UserAgent { raw, parsed } } diff --git a/tests/integration/test_ourlogs.py b/tests/integration/test_ourlogs.py index 8b8be701126..b18089877ab 100644 --- a/tests/integration/test_ourlogs.py +++ b/tests/integration/test_ourlogs.py @@ -973,19 +973,6 @@ def test_browser_name_version_extraction( }, id="localhost-url", ), - pytest.param( - "localhost", - {"localhost": {"isEnabled": True}}, - { - "attributes": { - "http.request.header.Host": { - "value": "localhost:8000", - "type": "string", - } - } - }, - id="localhost-header", - ), pytest.param( "legacy-browsers", {"legacyBrowsers": {"isEnabled": True, "options": ["ie9"]}}, diff --git a/tests/integration/test_spansv2.py b/tests/integration/test_spansv2.py index 3e62ec91f2c..32b7a3ed2f9 100644 --- a/tests/integration/test_spansv2.py +++ b/tests/integration/test_spansv2.py @@ -1023,19 +1023,6 @@ def test_spansv2_ds_root_in_different_org( }, id="localhost-url", ), - pytest.param( - "localhost", - {"localhost": {"isEnabled": True}}, - { - "attributes": { - "http.request.header.Host": { - "value": "localhost:8000", - "type": "string", - } - } - }, - id="localhost-header", - ), pytest.param( "legacy-browsers", {"legacyBrowsers": {"isEnabled": True, "options": ["ie9"]}}, diff --git a/tests/integration/test_trace_metrics.py b/tests/integration/test_trace_metrics.py index 8e80725c90a..f0ba2fe0925 100644 --- a/tests/integration/test_trace_metrics.py +++ b/tests/integration/test_trace_metrics.py @@ -1064,19 +1064,6 @@ def test_trace_metric_container_metadata( }, id="localhost-url", ), - pytest.param( - "localhost", - {"localhost": {"isEnabled": True}}, - { - "attributes": { - "http.request.header.Host": { - "value": "localhost:8000", - "type": "string", - } - } - }, - id="localhost-header", - ), pytest.param( "legacy-browsers", {"legacyBrowsers": {"isEnabled": True, "options": ["ie9"]}},