From 355c5b166ccefc12b7b8fe6d0fc55dc9cb707052 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Wed, 1 Jul 2026 16:20:42 +0200 Subject: [PATCH 1/4] Failing tests --- tests/integration/test_spans_standalone.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/integration/test_spans_standalone.py b/tests/integration/test_spans_standalone.py index 6b34c6f0856..93564ce2c3c 100644 --- a/tests/integration/test_spans_standalone.py +++ b/tests/integration/test_spans_standalone.py @@ -1,4 +1,5 @@ from datetime import datetime, timezone +from unittest import mock from sentry_sdk.envelope import Envelope, Item, PayloadRef @@ -165,7 +166,7 @@ def test_lcp_span( "release": "frontend@488531b11e6401fa530ac25554d44426e6ef0f0b", "environment": "prod", "replay_id": "3d76a6311de149b9b3f560827ea0ecf9", - "transaction": "/insights/projects/", + "transaction": "/insights/projects/b8686628-95f0-4be9-af6b-a98164504d8f", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Firefox/42.0", "client.address": "{{auto}}", "sentry.exclusive_time": 0, @@ -212,6 +213,7 @@ def test_lcp_span( } assert spans_consumer.get_span() == { + "_meta": mock.ANY, "attributes": { "client.address": {"type": "string", "value": "127.0.0.1"}, "browser.web_vital.lcp.value": {"type": "double", "value": 548.0}, @@ -247,8 +249,8 @@ def test_lcp_span( "value": "3d76a6311de149b9b3f560827ea0ecf9", }, "sentry.report_event": {"type": "string", "value": "navigation"}, - "sentry.segment.name": {"type": "string", "value": "/insights/projects/"}, - "sentry.transaction": {"type": "string", "value": "/insights/projects/"}, + "sentry.segment.name": {"type": "string", "value": "/insights/projects/*"}, + "sentry.transaction": {"type": "string", "value": "/insights/projects/*"}, "user_agent.original": { "type": "string", "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " @@ -375,7 +377,7 @@ def test_cls_span( "release": "frontend@488531b11e6401fa530ac25554d44426e6ef0f0b", "environment": "prod", "replay_id": "3d76a6311de149b9b3f560827ea0ecf9", - "transaction": "/insights/projects/", + "transaction": "/insights/projects/b8686628-95f0-4be9-af6b-a98164504d8f", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Firefox/42.0", "client.address": "{{auto}}", "sentry.exclusive_time": 0, @@ -423,6 +425,7 @@ def test_cls_span( } assert spans_consumer.get_span() == { + "_meta": mock.ANY, "attributes": { "client.address": {"type": "string", "value": "127.0.0.1"}, "browser.web_vital.cls.value": {"type": "double", "value": 0.1}, @@ -463,8 +466,8 @@ def test_cls_span( "value": "3d76a6311de149b9b3f560827ea0ecf9", }, "sentry.report_event": {"type": "string", "value": "navigation"}, - "sentry.segment.name": {"type": "string", "value": "/insights/projects/"}, - "sentry.transaction": {"type": "string", "value": "/insights/projects/"}, + "sentry.segment.name": {"type": "string", "value": "/insights/projects/*"}, + "sentry.transaction": {"type": "string", "value": "/insights/projects/*"}, "user_agent.original": { "type": "string", "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " @@ -590,7 +593,7 @@ def test_inp_span( "release": "frontend@488531b11e6401fa530ac25554d44426e6ef0f0b", "environment": "prod", "replay_id": "3d76a6311de149b9b3f560827ea0ecf9", - "transaction": "/insights/projects/", + "transaction": "/insights/projects/b8686628-95f0-4be9-af6b-a98164504d8f", "user_agent.original": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Firefox/42.0", "client.address": "{{auto}}", "sentry.exclusive_time": 104, @@ -624,6 +627,7 @@ def test_inp_span( } assert spans_consumer.get_span() == { + "_meta": mock.ANY, "attributes": { "client.address": {"type": "string", "value": "127.0.0.1"}, "browser.web_vital.inp.value": {"type": "double", "value": 104.0}, @@ -653,8 +657,8 @@ def test_inp_span( "type": "string", "value": "3d76a6311de149b9b3f560827ea0ecf9", }, - "sentry.segment.name": {"type": "string", "value": "/insights/projects/"}, - "sentry.transaction": {"type": "string", "value": "/insights/projects/"}, + "sentry.segment.name": {"type": "string", "value": "/insights/projects/*"}, + "sentry.transaction": {"type": "string", "value": "/insights/projects/*"}, "user_agent.original": { "type": "string", "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " From 2efc5b5a70bead4e5a916e2c81861c21cf904a1c Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Wed, 1 Jul 2026 17:29:33 +0200 Subject: [PATCH 2/4] Fix --- relay-event-normalization/src/eap/mod.rs | 67 +++++++++++++++++++- relay-server/src/processing/spans/mod.rs | 3 + relay-server/src/processing/spans/process.rs | 12 ++-- 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/relay-event-normalization/src/eap/mod.rs b/relay-event-normalization/src/eap/mod.rs index b52f7f7f671..53d60d6ad2c 100644 --- a/relay-event-normalization/src/eap/mod.rs +++ b/relay-event-normalization/src/eap/mod.rs @@ -10,7 +10,7 @@ use relay_common::time::UnixTimestamp; use relay_conventions::attributes::*; use relay_conventions::{AttributeInfo, ReplacementName, WriteBehavior}; use relay_event_schema::protocol::{ - Attribute, AttributeType, Attributes, BrowserContext, Geo, SpanV2, SpanV2Status, + Attribute, AttributeType, AttributeValue, Attributes, BrowserContext, Geo, SpanV2, SpanV2Status, }; use relay_protocol::{Annotated, Empty, Error, ErrorKind, Meta, Remark, RemarkType, Value}; use relay_sampling::DynamicSamplingContext; @@ -22,7 +22,10 @@ use crate::span::tag_extraction::{ domain_from_scrubbed_http, domain_from_server_address, span_op_to_category, sql_action_from_query, sql_tables_from_query, }; -use crate::{ClientHints, FromUserAgentInfo as _, RawUserAgentInfo}; +use crate::{ + ClientHints, FromUserAgentInfo as _, RawUserAgentInfo, TransactionNameRule, + normalize_transaction_name, +}; mod ai; mod mobile; @@ -849,6 +852,66 @@ pub fn normalize_web_vital_span_segment(span: &mut SpanV2) { } } +/// Normalize the [`SENTRY__SEGMENT__NAME`] attribute (aka the transaction) +/// by running [`normalize_transaction_name`] on it. +/// +/// This exists for parity with the legacy standalone span pipeline. +pub fn normalize_segment_name( + attributes: &mut Annotated, + tx_name_rules: &[TransactionNameRule], +) { + let Some(attributes) = attributes.value_mut() else { + return; + }; + + if attributes + .get_value(SENTRY__SEGMENT__NAME) + .and_then(|v| v.as_str()) + .is_none() + { + return; + } + + // SENTRY__SEGMENT__NAME must exist and be a string. + + let Some(annotated_attr) = attributes.remove(SENTRY__SEGMENT__NAME) else { + return; + }; + + let Annotated( + Some(Attribute { + value: AttributeValue { ty, value }, + other, + }), + meta, + ) = annotated_attr + else { + return; + }; + + let mut segment_name = value.map_value(|v| match v { + Value::String(s) => s, + _ => unreachable!(), + }); + + normalize_transaction_name(&mut segment_name, tx_name_rules); + + let attribute = Annotated( + Some(Attribute { + value: AttributeValue { + ty, + value: segment_name.map_value(Value::String), + }, + other, + }), + meta, + ); + + attributes + .0 + .insert(SENTRY__SEGMENT__NAME.to_owned(), attribute); +} + /// Double writes sentry conventions attributes into legacy attributes. /// /// This achieves backwards compatibility as it allows products to continue using legacy attributes diff --git a/relay-server/src/processing/spans/mod.rs b/relay-server/src/processing/spans/mod.rs index 062864222e9..974ee49a60d 100644 --- a/relay-server/src/processing/spans/mod.rs +++ b/relay-server/src/processing/spans/mod.rs @@ -394,6 +394,9 @@ struct Settings { /// with the legacy pipeline. For V2 spans, we assume the SDK is already /// sending the correct values. clear_web_vital_segment_info: bool, + /// Normalize the segment name by scrubbing identifiers and applying rules + /// from the project config. + normalize_segment_name: bool, } /// Spans which have been parsed and expanded from their serialized state. diff --git a/relay-server/src/processing/spans/process.rs b/relay-server/src/processing/spans/process.rs index 1616747cc9b..d000f346cef 100644 --- a/relay-server/src/processing/spans/process.rs +++ b/relay-server/src/processing/spans/process.rs @@ -113,10 +113,7 @@ fn expand_span_container(item: &Item) -> Result<(Settings, ContainerItems Default::default(), @@ -155,6 +152,9 @@ fn expand_legacy_spans( // We want to do this for V1 standalone spans for parity // with the legacy pipeline. clear_web_vital_segment_info: true, + // We want to do this for V1 standalone spans for parity + // with the legacy pipeline. + normalize_segment_name: true, }; (settings, spans) @@ -229,6 +229,7 @@ fn normalize_span( hints: meta.client_hints(), }); let performance_score = ctx.project_info.config().performance_score.as_ref(); + let tx_name_rules = &ctx.project_info.config.tx_name_rules; validate_timestamps(span)?; @@ -241,6 +242,9 @@ fn normalize_span( if settings.clear_web_vital_segment_info { eap::normalize_web_vital_span_segment(span); } + if settings.normalize_segment_name { + eap::normalize_segment_name(&mut span.attributes, tx_name_rules); + } eap::normalize_span_category(&mut span.attributes); eap::normalize_received(&mut span.attributes, meta.received_at()); eap::normalize_client_address(&mut span.attributes, meta.client_addr()); From dac237371ba0a0e8a26d795914bd1f34a2eef770 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Wed, 1 Jul 2026 19:10:06 +0200 Subject: [PATCH 3/4] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 013d35e3cd8..8bd254b4de3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Unset segment info for web vital spans. ([#6042](https://github.com/getsentry/relay/pull/6042)) - Set sentry.trace.status on segment spans. ([#6140](https://github.com/getsentry/relay/pull/6140)) - Don't modify segment information for V2 web vital spans. ([#6160](https://github.com/getsentry/relay/pull/6160)) +- Normalize segment names for standalone spans. ([#6163](https://github.com/getsentry/relay/pull/6163)) **Internal**: From d3606ba4371a04485ac8507771ea733945a17e1e Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Thu, 2 Jul 2026 15:00:54 +0200 Subject: [PATCH 4/4] ref --- relay-event-normalization/src/eap/mod.rs | 46 +++---------------- relay-event-schema/src/protocol/attributes.rs | 9 ++++ 2 files changed, 15 insertions(+), 40 deletions(-) diff --git a/relay-event-normalization/src/eap/mod.rs b/relay-event-normalization/src/eap/mod.rs index 53d60d6ad2c..063e7b709f6 100644 --- a/relay-event-normalization/src/eap/mod.rs +++ b/relay-event-normalization/src/eap/mod.rs @@ -10,7 +10,7 @@ use relay_common::time::UnixTimestamp; use relay_conventions::attributes::*; use relay_conventions::{AttributeInfo, ReplacementName, WriteBehavior}; use relay_event_schema::protocol::{ - Attribute, AttributeType, AttributeValue, Attributes, BrowserContext, Geo, SpanV2, SpanV2Status, + Attribute, AttributeType, Attributes, BrowserContext, Geo, SpanV2, SpanV2Status, }; use relay_protocol::{Annotated, Empty, Error, ErrorKind, Meta, Remark, RemarkType, Value}; use relay_sampling::DynamicSamplingContext; @@ -864,52 +864,18 @@ pub fn normalize_segment_name( return; }; - if attributes - .get_value(SENTRY__SEGMENT__NAME) - .and_then(|v| v.as_str()) - .is_none() - { - return; - } - - // SENTRY__SEGMENT__NAME must exist and be a string. - - let Some(annotated_attr) = attributes.remove(SENTRY__SEGMENT__NAME) else { + let Some(attr_value) = attributes.get_annotated_value_mut(SENTRY__SEGMENT__NAME) else { return; }; - let Annotated( - Some(Attribute { - value: AttributeValue { ty, value }, - other, - }), - meta, - ) = annotated_attr - else { - return; + let mut segment_name = match &attr_value.0 { + Some(Value::String(s)) => Annotated(Some(s.to_owned()), attr_value.1.clone()), + _ => return, }; - let mut segment_name = value.map_value(|v| match v { - Value::String(s) => s, - _ => unreachable!(), - }); - normalize_transaction_name(&mut segment_name, tx_name_rules); - let attribute = Annotated( - Some(Attribute { - value: AttributeValue { - ty, - value: segment_name.map_value(Value::String), - }, - other, - }), - meta, - ); - - attributes - .0 - .insert(SENTRY__SEGMENT__NAME.to_owned(), attribute); + *attr_value = segment_name.map_value(Value::String); } /// Double writes sentry conventions attributes into legacy attributes. diff --git a/relay-event-schema/src/protocol/attributes.rs b/relay-event-schema/src/protocol/attributes.rs index d1245976561..ba84dc0dc73 100644 --- a/relay-event-schema/src/protocol/attributes.rs +++ b/relay-event-schema/src/protocol/attributes.rs @@ -272,6 +272,15 @@ impl Attributes { Some(&self.0.get(key)?.value()?.value.value) } + /// Returns the mutable attribute value as annotated. + pub fn get_annotated_value_mut(&mut self, key: &Q) -> Option<&mut Annotated> + where + String: Borrow, + Q: Ord + ?Sized, + { + Some(&mut self.0.get_mut(key)?.value_mut().as_mut()?.value.value) + } + /// Inserts an attribute with the given value into this collection. pub fn insert, V: Into>(&mut self, key: K, value: V) { fn inner(slf: &mut Attributes, key: String, value: AttributeValue) {