From c624d671a01ae9cbdc1c184954d354a873358302 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Fri, 8 May 2026 19:48:32 +0200 Subject: [PATCH 1/3] feat: Extend performance profiles to V2 spans This makes performance profiles able to also match V2 spans by turning the browser name conditions into disjunctions checking two different fields. This is required to enable performance score calculation for V2 spans. --- src/sentry/relay/config/__init__.py | 130 ++++++++-------------------- 1 file changed, 35 insertions(+), 95 deletions(-) diff --git a/src/sentry/relay/config/__init__.py b/src/sentry/relay/config/__init__.py index 4ece1fa99200..0f74e57a2d95 100644 --- a/src/sentry/relay/config/__init__.py +++ b/src/sentry/relay/config/__init__.py @@ -312,6 +312,25 @@ def _should_extract_abnormal_mechanism(project: Project) -> bool: ) +def _browser_name_one_of(*args): + """Returns a condition that matches if an event + or span's browser name is one of the options in `args`. + + For events and V1 spans, this checks the `event.contexts.browser.name` field. + For V2 spans, it checks the `span.attributes.browser.name.value` field. + """ + return ( + { + "op": "or", + "inner": [ + {"op": "eq", "name": field, "value": browser} + for field in ["event.contexts.browser.name", "span.attributes.browser.name.value"] + for browser in args + ], + }, + ) + + def _get_desktop_browser_performance_profiles( organization: Organization, ) -> list[dict[str, Any]]: @@ -348,11 +367,7 @@ def _get_desktop_browser_performance_profiles( "optional": True, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Chrome", - }, + "condition": _browser_name_one_of("Chrome"), }, { "name": "Firefox", @@ -386,11 +401,7 @@ def _get_desktop_browser_performance_profiles( "optional": True, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Firefox", - }, + "condition": _browser_name_one_of("Firefox"), }, { "name": "Safari", @@ -424,11 +435,7 @@ def _get_desktop_browser_performance_profiles( "optional": True, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Safari", - }, + "condition": _browser_name_one_of("Safari"), }, { "name": "Edge", @@ -462,11 +469,7 @@ def _get_desktop_browser_performance_profiles( "optional": True, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Edge", - }, + "condition": _browser_name_one_of("Edge"), }, { "name": "Opera", @@ -500,11 +503,7 @@ def _get_desktop_browser_performance_profiles( "optional": True, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Opera", - }, + "condition": _browser_name_one_of("Opera"), }, { "name": "Chrome INP", @@ -517,21 +516,7 @@ def _get_desktop_browser_performance_profiles( "optional": False, }, ], - "condition": { - "op": "or", - "inner": [ - { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Chrome", - }, - { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Google Chrome", - }, - ], - }, + "condition": _browser_name_one_of("Chrome", "Google Chrome"), }, { "name": "Edge INP", @@ -544,11 +529,7 @@ def _get_desktop_browser_performance_profiles( "optional": False, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Edge", - }, + "condition": _browser_name_one_of("Edge"), }, { "name": "Opera INP", @@ -561,11 +542,7 @@ def _get_desktop_browser_performance_profiles( "optional": False, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Opera", - }, + "condition": _browser_name_one_of("Opera"), }, ] @@ -606,11 +583,7 @@ def _get_mobile_browser_performance_profiles( "optional": True, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Chrome Mobile", - }, + "condition": _browser_name_one_of("Chrome Mobile"), }, { "name": "Firefox Mobile", @@ -644,11 +617,7 @@ def _get_mobile_browser_performance_profiles( "optional": True, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Firefox Mobile", - }, + "condition": _browser_name_one_of("Firefox Mobile"), }, { "name": "Safari Mobile", @@ -682,11 +651,7 @@ def _get_mobile_browser_performance_profiles( "optional": True, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Mobile Safari", - }, + "condition": _browser_name_one_of("Mobile Safari"), }, { "name": "Edge Mobile", @@ -720,11 +685,7 @@ def _get_mobile_browser_performance_profiles( "optional": True, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Edge Mobile", - }, + "condition": _browser_name_one_of("Edge Mobile"), }, { "name": "Opera Mobile", @@ -758,11 +719,7 @@ def _get_mobile_browser_performance_profiles( "optional": True, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Opera Mobile", - }, + "condition": _browser_name_one_of("Opera Mobile"), }, { "name": "Chrome Mobile INP", @@ -775,16 +732,7 @@ def _get_mobile_browser_performance_profiles( "optional": False, }, ], - "condition": { - "op": "or", - "inner": [ - { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Chrome Mobile", - }, - ], - }, + "condition": _browser_name_one_of("Chrome Mobile"), }, { "name": "Edge Mobile INP", @@ -797,11 +745,7 @@ def _get_mobile_browser_performance_profiles( "optional": False, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Edge Mobile", - }, + "condition": _browser_name_one_of("Edge Mobile"), }, { "name": "Opera Mobile INP", @@ -814,11 +758,7 @@ def _get_mobile_browser_performance_profiles( "optional": False, }, ], - "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Opera Mobile", - }, + "condition": _browser_name_one_of("Opera Mobile"), }, ] From 46c128ae44f2af1b551c8e284631162b1989a768 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Fri, 8 May 2026 19:55:42 +0200 Subject: [PATCH 2/3] Fix formatting --- src/sentry/relay/config/__init__.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/sentry/relay/config/__init__.py b/src/sentry/relay/config/__init__.py index 0f74e57a2d95..2d95c8e4f892 100644 --- a/src/sentry/relay/config/__init__.py +++ b/src/sentry/relay/config/__init__.py @@ -319,16 +319,14 @@ def _browser_name_one_of(*args): For events and V1 spans, this checks the `event.contexts.browser.name` field. For V2 spans, it checks the `span.attributes.browser.name.value` field. """ - return ( - { - "op": "or", - "inner": [ - {"op": "eq", "name": field, "value": browser} - for field in ["event.contexts.browser.name", "span.attributes.browser.name.value"] - for browser in args - ], - }, - ) + return { + "op": "or", + "inner": [ + {"op": "eq", "name": field, "value": browser} + for field in ["event.contexts.browser.name", "span.attributes.browser.name.value"] + for browser in args + ], + } def _get_desktop_browser_performance_profiles( From aeccac28a3b6ebbfc07ef80734e232520ad49455 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Fri, 8 May 2026 20:18:59 +0200 Subject: [PATCH 3/3] Fix tests --- .../test_get_project_config/REGION.pysnap | 163 +++++++++--- tests/sentry/relay/test_config.py | 240 ++++++++++++++---- 2 files changed, 318 insertions(+), 85 deletions(-) diff --git a/tests/sentry/relay/snapshots/test_config/test_get_project_config/REGION.pysnap b/tests/sentry/relay/snapshots/test_config/test_get_project_config/REGION.pysnap index 4c81c60c9a1e..3e7bd783e6fd 100644 --- a/tests/sentry/relay/snapshots/test_config/test_get_project_config/REGION.pysnap +++ b/tests/sentry/relay/snapshots/test_config/test_get_project_config/REGION.pysnap @@ -179,9 +179,14 @@ config: performanceScore: profiles: - condition: - name: event.contexts.browser.name - op: eq - value: Chrome + inner: + - name: event.contexts.browser.name + op: eq + value: Chrome + - name: span.attributes.browser.name.value + op: eq + value: Chrome + op: or name: Chrome scoreComponents: - measurement: fcp @@ -205,9 +210,14 @@ config: p50: 400.0 weight: 0.1 - condition: - name: event.contexts.browser.name - op: eq - value: Firefox + inner: + - name: event.contexts.browser.name + op: eq + value: Firefox + - name: span.attributes.browser.name.value + op: eq + value: Firefox + op: or name: Firefox scoreComponents: - measurement: fcp @@ -231,9 +241,14 @@ config: p50: 400.0 weight: 0.1 - condition: - name: event.contexts.browser.name - op: eq - value: Safari + inner: + - name: event.contexts.browser.name + op: eq + value: Safari + - name: span.attributes.browser.name.value + op: eq + value: Safari + op: or name: Safari scoreComponents: - measurement: fcp @@ -257,9 +272,14 @@ config: p50: 400.0 weight: 0.1 - condition: - name: event.contexts.browser.name - op: eq - value: Edge + inner: + - name: event.contexts.browser.name + op: eq + value: Edge + - name: span.attributes.browser.name.value + op: eq + value: Edge + op: or name: Edge scoreComponents: - measurement: fcp @@ -283,9 +303,14 @@ config: p50: 400.0 weight: 0.1 - condition: - name: event.contexts.browser.name - op: eq - value: Opera + inner: + - name: event.contexts.browser.name + op: eq + value: Opera + - name: span.attributes.browser.name.value + op: eq + value: Opera + op: or name: Opera scoreComponents: - measurement: fcp @@ -316,6 +341,12 @@ config: - name: event.contexts.browser.name op: eq value: Google Chrome + - name: span.attributes.browser.name.value + op: eq + value: Chrome + - name: span.attributes.browser.name.value + op: eq + value: Google Chrome op: or name: Chrome INP scoreComponents: @@ -325,9 +356,14 @@ config: p50: 500.0 weight: 1.0 - condition: - name: event.contexts.browser.name - op: eq - value: Edge + inner: + - name: event.contexts.browser.name + op: eq + value: Edge + - name: span.attributes.browser.name.value + op: eq + value: Edge + op: or name: Edge INP scoreComponents: - measurement: inp @@ -336,9 +372,14 @@ config: p50: 500.0 weight: 1.0 - condition: - name: event.contexts.browser.name - op: eq - value: Opera + inner: + - name: event.contexts.browser.name + op: eq + value: Opera + - name: span.attributes.browser.name.value + op: eq + value: Opera + op: or name: Opera INP scoreComponents: - measurement: inp @@ -347,9 +388,14 @@ config: p50: 500.0 weight: 1.0 - condition: - name: event.contexts.browser.name - op: eq - value: Chrome Mobile + inner: + - name: event.contexts.browser.name + op: eq + value: Chrome Mobile + - name: span.attributes.browser.name.value + op: eq + value: Chrome Mobile + op: or name: Chrome Mobile scoreComponents: - measurement: fcp @@ -373,9 +419,14 @@ config: p50: 1800.0 weight: 0.1 - condition: - name: event.contexts.browser.name - op: eq - value: Firefox Mobile + inner: + - name: event.contexts.browser.name + op: eq + value: Firefox Mobile + - name: span.attributes.browser.name.value + op: eq + value: Firefox Mobile + op: or name: Firefox Mobile scoreComponents: - measurement: fcp @@ -399,9 +450,14 @@ config: p50: 1800.0 weight: 0.1 - condition: - name: event.contexts.browser.name - op: eq - value: Mobile Safari + inner: + - name: event.contexts.browser.name + op: eq + value: Mobile Safari + - name: span.attributes.browser.name.value + op: eq + value: Mobile Safari + op: or name: Safari Mobile scoreComponents: - measurement: fcp @@ -425,9 +481,14 @@ config: p50: 1800.0 weight: 0.1 - condition: - name: event.contexts.browser.name - op: eq - value: Edge Mobile + inner: + - name: event.contexts.browser.name + op: eq + value: Edge Mobile + - name: span.attributes.browser.name.value + op: eq + value: Edge Mobile + op: or name: Edge Mobile scoreComponents: - measurement: fcp @@ -451,9 +512,14 @@ config: p50: 1800.0 weight: 0.1 - condition: - name: event.contexts.browser.name - op: eq - value: Opera Mobile + inner: + - name: event.contexts.browser.name + op: eq + value: Opera Mobile + - name: span.attributes.browser.name.value + op: eq + value: Opera Mobile + op: or name: Opera Mobile scoreComponents: - measurement: fcp @@ -481,6 +547,9 @@ config: - name: event.contexts.browser.name op: eq value: Chrome Mobile + - name: span.attributes.browser.name.value + op: eq + value: Chrome Mobile op: or name: Chrome Mobile INP scoreComponents: @@ -490,9 +559,14 @@ config: p50: 500.0 weight: 1.0 - condition: - name: event.contexts.browser.name - op: eq - value: Edge Mobile + inner: + - name: event.contexts.browser.name + op: eq + value: Edge Mobile + - name: span.attributes.browser.name.value + op: eq + value: Edge Mobile + op: or name: Edge Mobile INP scoreComponents: - measurement: inp @@ -501,9 +575,14 @@ config: p50: 500.0 weight: 1.0 - condition: - name: event.contexts.browser.name - op: eq - value: Opera Mobile + inner: + - name: event.contexts.browser.name + op: eq + value: Opera Mobile + - name: span.attributes.browser.name.value + op: eq + value: Opera Mobile + op: or name: Opera Mobile INP scoreComponents: - measurement: inp diff --git a/tests/sentry/relay/test_config.py b/tests/sentry/relay/test_config.py index 407a720dfc18..ea4f60ab0d17 100644 --- a/tests/sentry/relay/test_config.py +++ b/tests/sentry/relay/test_config.py @@ -26,7 +26,6 @@ from sentry.testutils.factories import Factories from sentry.testutils.helpers import Feature from sentry.testutils.helpers.datetime import freeze_time -from sentry.testutils.helpers.options import override_options from sentry.testutils.pytest.fixtures import InstaSnapshotter, django_db_all from sentry.testutils.silo import cell_silo_test from sentry.utils.safe import get_path @@ -786,9 +785,19 @@ def test_desktop_performance_calculate_score(default_project) -> None: }, ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Chrome", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Chrome", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Chrome", + }, + ], }, "version": "1", } @@ -825,9 +834,19 @@ def test_desktop_performance_calculate_score(default_project) -> None: }, ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Firefox", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Firefox", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Firefox", + }, + ], }, "version": "1", } @@ -864,9 +883,19 @@ def test_desktop_performance_calculate_score(default_project) -> None: }, ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Safari", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Safari", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Safari", + }, + ], }, "version": "1", } @@ -903,9 +932,19 @@ def test_desktop_performance_calculate_score(default_project) -> None: }, ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Edge", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Edge", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Edge", + }, + ], }, "version": "1", } @@ -942,9 +981,19 @@ def test_desktop_performance_calculate_score(default_project) -> None: }, ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Opera", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Opera", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Opera", + }, + ], }, "version": "1", } @@ -973,6 +1022,16 @@ def test_desktop_performance_calculate_score(default_project) -> None: "name": "event.contexts.browser.name", "value": "Google Chrome", }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Chrome", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Google Chrome", + }, ], }, "version": "1", @@ -990,9 +1049,19 @@ def test_desktop_performance_calculate_score(default_project) -> None: }, ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Edge", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Edge", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Edge", + }, + ], }, "version": "1", } @@ -1009,9 +1078,19 @@ def test_desktop_performance_calculate_score(default_project) -> None: }, ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Opera", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Opera", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Opera", + }, + ], }, "version": "1", } @@ -1061,9 +1140,19 @@ def test_mobile_performance_calculate_score(default_project) -> None: }, ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Chrome Mobile", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Chrome Mobile", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Chrome Mobile", + }, + ], }, "version": "1", } @@ -1100,9 +1189,19 @@ def test_mobile_performance_calculate_score(default_project) -> None: }, ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Firefox Mobile", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Firefox Mobile", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Firefox Mobile", + }, + ], }, "version": "1", } @@ -1139,9 +1238,19 @@ def test_mobile_performance_calculate_score(default_project) -> None: }, ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Mobile Safari", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Mobile Safari", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Mobile Safari", + }, + ], }, "version": "1", } @@ -1178,9 +1287,19 @@ def test_mobile_performance_calculate_score(default_project) -> None: }, ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Edge Mobile", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Edge Mobile", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Edge Mobile", + }, + ], }, "version": "1", } @@ -1218,9 +1337,19 @@ def test_mobile_performance_calculate_score(default_project) -> None: }, ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Opera Mobile", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Opera Mobile", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Opera Mobile", + }, + ], }, "version": "1", } @@ -1243,6 +1372,11 @@ def test_mobile_performance_calculate_score(default_project) -> None: "name": "event.contexts.browser.name", "value": "Chrome Mobile", }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Chrome Mobile", + }, ], }, "version": "1", @@ -1259,9 +1393,19 @@ def test_mobile_performance_calculate_score(default_project) -> None: }, ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Edge Mobile", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Edge Mobile", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Edge Mobile", + }, + ], }, "version": "1", } @@ -1277,9 +1421,19 @@ def test_mobile_performance_calculate_score(default_project) -> None: } ], "condition": { - "op": "eq", - "name": "event.contexts.browser.name", - "value": "Opera Mobile", + "op": "or", + "inner": [ + { + "op": "eq", + "name": "event.contexts.browser.name", + "value": "Opera Mobile", + }, + { + "op": "eq", + "name": "span.attributes.browser.name.value", + "value": "Opera Mobile", + }, + ], }, "version": "1", }