From 61a998eb8ffe00a55ee62d4acd05096cd7a847e5 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Wed, 10 Jun 2026 16:24:20 +0200 Subject: [PATCH 1/7] WIP --- relay-event-normalization/src/eap/mod.rs | 53 +++++--- relay-server/src/processing/spans/mod.rs | 1 + relay-server/src/processing/spans/process.rs | 22 +++- .../src/processing/transactions/spans.rs | 11 +- relay-spans/src/description.rs | 40 ++++++ relay-spans/src/lib.rs | 2 + relay-spans/src/name.rs | 105 ++++++++++++++- tests/integration/test_spans.py | 5 +- tests/integration/test_spans_standalone.py | 120 +++++++++++++++++- 9 files changed, 326 insertions(+), 33 deletions(-) create mode 100644 relay-spans/src/description.rs diff --git a/relay-event-normalization/src/eap/mod.rs b/relay-event-normalization/src/eap/mod.rs index 6aaeed6221d..940e2195bc4 100644 --- a/relay-event-normalization/src/eap/mod.rs +++ b/relay-event-normalization/src/eap/mod.rs @@ -11,7 +11,7 @@ use relay_conventions::attributes::*; use relay_conventions::{AttributeInfo, ReplacementName, WriteBehavior}; use relay_event_schema::protocol::{Attribute, AttributeType, Attributes, BrowserContext, Geo}; use relay_protocol::{Annotated, Error, ErrorKind, Meta, Remark, RemarkType, Value}; -use relay_spans::derive_op_for_v2_span; +use relay_spans::{derive_description_for_v2_span, derive_op_for_v2_span}; use crate::span::TABLE_NAME_REGEX; use crate::span::description::{scrub_db_query, scrub_http}; @@ -46,6 +46,33 @@ pub fn normalize_sentry_op(attributes: &mut Annotated) { attrs.insert_if_missing(SENTRY__OP, || inferred_op); } +/// Normalizes a V2 span's [`SENTRY__DESCRIPTION`] attribute. +/// +/// For now, this tries the following steps, in order: +/// - backfill from the span's name if its [`SENTRY__ORIGIN`] attribute is `"manual"` +/// - backfill from the span's [`DB__QUERY__TEXT`] attribute if it exists +/// - backfill a combination of the span's [`HTTP__REQUEST__METHOD`] and +/// [`URL__FULL`] attributes, if they both exists. +/// +/// In the future, this logic will be partly moved to and extended in `sentry-conventions`. +pub fn normalize_sentry_description( + attributes: &mut Annotated, + name: &Annotated, +) { + if attributes + .value() + .is_some_and(|attrs| attrs.contains_key(SENTRY__DESCRIPTION)) + { + return; + } + + if let Some(description) = derive_description_for_v2_span(attributes, name) { + attributes + .get_or_insert_with(Default::default) + .insert(SENTRY__DESCRIPTION, description); + } +} + /// Infers the sentry.category attribute and inserts it into `attributes` if not /// already set. The category is derived from the span operation or other span /// attributes. @@ -765,7 +792,6 @@ pub fn write_legacy_attributes(attributes: &mut Annotated) { )] let current_to_legacy_attributes = [ // DB attributes - (DB__QUERY__TEXT, SENTRY__DESCRIPTION), (SENTRY__NORMALIZED_DB_QUERY, SENTRY__NORMALIZED_DESCRIPTION), (DB__OPERATION__NAME, SENTRY__ACTION), (DB__SYSTEM__NAME, DB__SYSTEM), @@ -778,12 +804,15 @@ pub fn write_legacy_attributes(attributes: &mut Annotated) { ]; for (current_attribute, legacy_attribute) in current_to_legacy_attributes { - if attributes.contains_key(current_attribute) { - let Some(attr) = attributes.get_attribute(current_attribute) else { - continue; - }; - attributes.insert(legacy_attribute, attr.value.clone()); + if attributes.contains_key(legacy_attribute) { + continue; } + + let Some(attr) = attributes.get_attribute(current_attribute) else { + continue; + }; + + attributes.insert(legacy_attribute, attr.value.clone()); } if !attributes.contains_key(SENTRY__DOMAIN) @@ -803,12 +832,6 @@ pub fn write_legacy_attributes(attributes: &mut Annotated) { }, ); } - - if let Some(&Value::String(method)) = attributes.get_value(HTTP__REQUEST__METHOD).as_ref() - && let Some(&Value::String(url)) = attributes.get_value(URL__FULL).as_ref() - { - attributes.insert(SENTRY__DESCRIPTION, format!("{method} {url}")) - } } #[cfg(test)] @@ -2249,10 +2272,6 @@ mod tests { "type": "string", "value": "FIND" }, - "sentry.description": { - "type": "string", - "value": "{\"find\": \"documents\", \"foo\": \"bar\"}" - }, "sentry.domain": { "type": "string", "value": ",documents," diff --git a/relay-server/src/processing/spans/mod.rs b/relay-server/src/processing/spans/mod.rs index c249b16d637..e20f48b0d86 100644 --- a/relay-server/src/processing/spans/mod.rs +++ b/relay-server/src/processing/spans/mod.rs @@ -204,6 +204,7 @@ impl processing::Processor for SpansProcessor { }; process::scrub(&mut spans, ctx); + process::backfill_description(&mut spans); match dynamic_sampling::try_split_indexed_and_total(spans, ctx) { Either::Left(spans) => Ok(Output::just(SpanOutput::TotalAndIndexed(spans))), diff --git a/relay-server/src/processing/spans/process.rs b/relay-server/src/processing/spans/process.rs index 4ffb3c0c391..d6630b12a08 100644 --- a/relay-server/src/processing/spans/process.rs +++ b/relay-server/src/processing/spans/process.rs @@ -1,4 +1,5 @@ use std::collections::BTreeMap; +use std::convert::Infallible; use std::time::Duration; use relay_event_normalization::eap::ClientUserAgentInfo; @@ -288,6 +289,21 @@ fn normalize_span( Ok(()) } +pub fn backfill_description(spans: &mut Managed) { + spans.retain_with_context( + |spans| (&mut spans.spans, &()), + |span, _, _| backfill_span_description(&mut span.span), + ); +} + +fn backfill_span_description(span: &mut Annotated) -> Result<(), Infallible> { + let Some(span) = span.value_mut() else { + return Ok(()); + }; + eap::normalize_sentry_description(&mut span.attributes, &span.name); + Ok(()) +} + /// Validates the start and end timestamps of a span. /// /// The start timestamp must not be after the end timestamp. @@ -928,7 +944,6 @@ mod tests { assert_attributes_contains( &span, &[ - (SENTRY__DESCRIPTION, "select * from users where id = 1"), ( SENTRY__NORMALIZED_DESCRIPTION, "SELECT * FROM users WHERE id = %s", @@ -990,10 +1005,6 @@ mod tests { &[ (SENTRY__CATEGORY, "http"), (SENTRY__OP, "http.client"), - ( - SENTRY__DESCRIPTION, - "GET https://www.example.com/path?param=value", - ), (SENTRY__ACTION, "GET"), (SENTRY__DOMAIN, "*.example.com"), ], @@ -1113,7 +1124,6 @@ mod tests { &span, &[ (SENTRY__OP, "db"), - (SENTRY__DESCRIPTION, "select * from users where id = 1"), ( SENTRY__NORMALIZED_DESCRIPTION, "SELECT * FROM users WHERE id = %s", diff --git a/relay-server/src/processing/transactions/spans.rs b/relay-server/src/processing/transactions/spans.rs index 46251aca0ce..330396855c2 100644 --- a/relay-server/src/processing/transactions/spans.rs +++ b/relay-server/src/processing/transactions/spans.rs @@ -4,7 +4,7 @@ use crate::processing; use crate::processing::utils::event::event_type; use relay_base_schema::events::EventType; use relay_config::Config; -use relay_event_schema::protocol::{Event, Measurement, Measurements, Span, SpanV2}; +use relay_event_schema::protocol::{Event, Measurement, Measurements, Span, SpanV2, TraceContext}; use relay_metrics::MetricNamespace; use relay_metrics::{FractionUnit, MetricUnit}; use relay_protocol::{Annotated, Empty}; @@ -41,6 +41,11 @@ pub fn extract_from_event( // Add child spans. if let Some(spans) = event.spans.value() { + let origin = event + .context::() + .map(|trace| trace.origin.clone()) + .unwrap_or_default(); + for span in spans { let Some(inner_span) = span.value() else { continue; @@ -54,6 +59,10 @@ pub fn extract_from_event( new_span.segment_id = transaction_span.segment_id.clone(); new_span.platform = transaction_span.platform.clone(); + if new_span.origin.value().is_none() { + new_span.origin = origin.clone(); + } + // If a profile is associated with the transaction, also associate it with its // child spans. new_span.profile_id = transaction_span.profile_id.clone(); diff --git a/relay-spans/src/description.rs b/relay-spans/src/description.rs new file mode 100644 index 00000000000..90d10dde0a9 --- /dev/null +++ b/relay-spans/src/description.rs @@ -0,0 +1,40 @@ +use relay_conventions::attributes::*; +use relay_event_schema::protocol::Attributes; +use relay_protocol::{Annotated, Value}; + +/// Derives a description for a V2 span, based on its name +/// and attributes. +/// +/// For now, this tries the following steps, in order: +/// - returns the span's name if its [`SENTRY__ORIGIN`] attribute is `"manual"` +/// - returns the span's [`DB__QUERY__TEXT`] attribute if it exists +/// - returns a combination of the span's [`HTTP__REQUEST__METHOD`] and +/// [`URL__FULL`] attributes, if they both exists. +/// +/// In the future, this logic will be partly moved to and extended in `sentry-conventions`. +pub fn derive_description_for_v2_span( + attributes: &Annotated, + name: &Annotated, +) -> Option { + let attributes = attributes.value()?; + + if attributes + .get_value(SENTRY__ORIGIN) + .and_then(|o| o.as_str()) + == Some("manual") + { + return name.value().cloned(); + } + + if let Some(&Value::String(db_query)) = attributes.get_value(DB__QUERY__TEXT).as_ref() { + return Some(db_query.clone()); + } + + if let Some(&Value::String(method)) = attributes.get_value(HTTP__REQUEST__METHOD).as_ref() + && let Some(&Value::String(url)) = attributes.get_value(URL__FULL).as_ref() + { + return Some(format!("{method} {url}")); + } + + None +} diff --git a/relay-spans/src/lib.rs b/relay-spans/src/lib.rs index 8a066268b35..ba8d22fb1a1 100644 --- a/relay-spans/src/lib.rs +++ b/relay-spans/src/lib.rs @@ -6,6 +6,7 @@ html_favicon_url = "https://raw.githubusercontent.com/getsentry/relay/master/artwork/relay-icon.png" )] +pub use crate::description::derive_description_for_v2_span; pub use crate::name::name_for_span; pub use crate::op::derive_op_for_v2_span; pub use crate::otel_to_sentry_v2::otel_to_sentry_span as otel_to_sentry_span_v2; @@ -13,6 +14,7 @@ pub use crate::v1_to_v2::span_v1_to_span_v2; pub use opentelemetry_proto::tonic::trace::v1 as otel_trace; +mod description; mod name; mod op; mod otel_to_sentry_v2; diff --git a/relay-spans/src/name.rs b/relay-spans/src/name.rs index 4d1adc1f255..5e2c816713c 100644 --- a/relay-spans/src/name.rs +++ b/relay-spans/src/name.rs @@ -1,10 +1,20 @@ -use relay_conventions::attributes::SENTRY__OP; +use relay_conventions::attributes::{SENTRY__DESCRIPTION, SENTRY__OP, SENTRY__ORIGIN}; use relay_conventions::name_for_op_and_attributes; use relay_event_schema::protocol::{Attributes, Span}; use relay_protocol::{Getter, GetterIter, Val}; -/// Constructs a name attribute for a span, following the rules defined in sentry-conventions. +/// Constructs a name attribute for a V1 span. +/// +/// If the span's origin is `"manual"`, its description will be used as the name. +/// Otherwise, the name is constructed following the rules defined in sentry-conventions. pub fn name_for_span(span: &Span) -> Option { + let origin = span.origin.value().map(|o| o.as_str()); + let description = span.description.value().map(|d| d.as_str()); + + if let Some(name) = name_for_origin_and_description(origin, description) { + return Some(name); + } + let op = span.op.value()?; let Some(data) = span.data.value() else { @@ -20,12 +30,38 @@ pub fn name_for_span(span: &Span) -> Option { )) } -/// Constructs a name attribute for a span, following the rules defined in sentry-conventions. +/// Constructs a name attribute for a V2 span, based on its attributes. +/// +/// If the attributes contain [`SENTRY__ORIGIN`] with the value `"manual"`, +/// the description (contained in [`SENTRY__DESCRIPTION`]) will be used as the name. +/// Otherwise, the name is constructed following the rules defined in sentry-conventions. pub fn name_for_attributes(attributes: &Attributes) -> Option { + let origin = attributes + .get_value(SENTRY__ORIGIN) + .and_then(|o| o.as_str()); + let description = attributes + .get_value(SENTRY__DESCRIPTION) + .and_then(|d| d.as_str()); + + if let Some(name) = name_for_origin_and_description(origin, description) { + return Some(name); + } + let op = attributes.get_value(SENTRY__OP)?.as_str()?; Some(name_for_op_and_attributes(op, &AttributeGetter(attributes))) } +fn name_for_origin_and_description( + origin: Option<&str>, + description: Option<&str>, +) -> Option { + if origin == Some("manual") { + description.map(String::from) + } else { + None + } +} + struct EmptyGetter {} impl Getter for EmptyGetter { @@ -203,4 +239,67 @@ mod tests { Some("Database operation".to_owned()) ); } + + #[test] + fn test_manual_spans_use_description_v1() { + let span = Span { + origin: Annotated::new("manual".to_owned()), + description: Annotated::new("Custom name".to_owned()), + op: Annotated::new("db".to_owned()), + data: Annotated::new(SpanData { + other: Object::from([ + ( + "db.query.summary".to_owned(), + Value::String("SELECT users".to_owned()).into(), + ), + ( + "db.operation.name".to_owned(), + Value::String("INSERT".to_owned()).into(), + ), + ( + "db.collection.name".to_owned(), + Value::String("widgets".to_owned()).into(), + ), + ]), + ..Default::default() + }), + ..Default::default() + }; + assert_eq!(name_for_span(&span), Some("Custom name".to_owned())); + } + + #[test] + fn test_manual_spans_use_description_v2() { + let attributes = Attributes::from([ + ( + "sentry.origin".to_owned(), + Annotated::new("manual".to_owned().into()), + ), + ( + "sentry.description".to_owned(), + Annotated::new("Custom name".to_owned().into()), + ), + ( + "sentry.op".to_owned(), + Annotated::new("db".to_owned().into()), + ), + ( + "db.query.summary".to_owned(), + Annotated::new("SELECT users".to_owned().into()), + ), + ( + "db.operation.name".to_owned(), + Annotated::new("INSERT".to_owned().into()), + ), + ( + "db.collection.name".to_owned(), + Annotated::new("widgets".to_owned().into()), + ), + ]); + + assert_eq!( + name_for_attributes(&attributes), + Some("Custom name".to_owned()) + ); + } } diff --git a/tests/integration/test_spans.py b/tests/integration/test_spans.py index 6abf5bcf004..3be93f6066e 100644 --- a/tests/integration/test_spans.py +++ b/tests/integration/test_spans.py @@ -186,7 +186,8 @@ def test_span_extraction( "attributes": {"span_key": {"type": "string", "value": "span_value"}}, }, ], - "name": "http", + # This span has origin "manual", so the name should just be the description. + "name": "GET /api/0/organizations/?member=1", "organization_id": 1, "parent_span_id": "968cff94913ebb07", "project_id": 42, @@ -268,6 +269,8 @@ def test_span_extraction( "attributes": {"txn_key": {"type": "integer", "value": 123}}, }, ], + # This is a segment span, so its name should be the transaction + # (despite the origin being "manual"). "name": "hi", "organization_id": 1, "project_id": 42, diff --git a/tests/integration/test_spans_standalone.py b/tests/integration/test_spans_standalone.py index 142943d47f5..a24fe603c1f 100644 --- a/tests/integration/test_spans_standalone.py +++ b/tests/integration/test_spans_standalone.py @@ -162,7 +162,6 @@ def test_lcp_span( envelope = envelope_with_spans( { "data": { - "sentry.origin": "auto.http.browser.lcp", "sentry.op": "ui.webvital.lcp", "release": "frontend@488531b11e6401fa530ac25554d44426e6ef0f0b", "environment": "prod", @@ -365,7 +364,6 @@ def test_cls_span( envelope = envelope_with_spans( { "data": { - "sentry.origin": "auto.http.browser.cls", "sentry.op": "ui.webvital.cls", "release": "frontend@488531b11e6401fa530ac25554d44426e6ef0f0b", "environment": "prod", @@ -574,7 +572,6 @@ def test_inp_span( envelope = envelope_with_spans( { "data": { - "sentry.origin": "auto.http.browser.inp", "sentry.op": "ui.interaction.click", "release": "frontend@488531b11e6401fa530ac25554d44426e6ef0f0b", "environment": "prod", @@ -817,7 +814,6 @@ def test_mobile_measurements( envelope = envelope_with_spans( { "data": { - "sentry.origin": "auto.http.browser.inp", "sentry.op": "ui.interaction.click", "release": "frontend@488531b11e6401fa530ac25554d44426e6ef0f0b", "environment": "prod", @@ -937,7 +933,6 @@ def test_ua_ip_inference( envelope = envelope_with_spans( { "data": { - "sentry.origin": "auto.http.browser.lcp", "sentry.op": "ui.webvital.lcp", "release": "frontend@488531b11e6401fa530ac25554d44426e6ef0f0b", "environment": "prod", @@ -1024,5 +1019,120 @@ def test_ua_ip_inference( } +@pytest.mark.parametrize("mode", ["legacy", "v2"]) +@pytest.mark.parametrize("origin", ["manual", "auto.http.browser.lcp"]) +def test_name_inference( + mini_sentry, relay, relay_with_processing, spans_consumer, mode, origin +): + """ + Tests that span names are inferred. + """ + spans_consumer = spans_consumer() + + project_id = 42 + project_config = mini_sentry.add_full_project_config(project_id) + if mode == "v2": + project_config["config"].setdefault("features", []).append( + "projects:span-v2-experimental-processing" + ) + + relay = relay(relay_with_processing()) + + ts = datetime.now(timezone.utc) + + envelope = envelope_with_spans( + { + "data": { + "sentry.op": "ui.webvital.lcp", + "release": "frontend@488531b11e6401fa530ac25554d44426e6ef0f0b", + "environment": "prod", + "replay_id": "3d76a6311de149b9b3f560827ea0ecf9", + "transaction": "/insights/projects/", + "sentry.exclusive_time": 0, + "sentry.pageload.span_id": "8a6626cc9bdd5d9b", + "sentry.report_event": "navigation", + }, + "description": "Test span", + "op": "ui.webvital.lcp", + "parent_span_id": "8a6626cc9bdd5d9b", + "span_id": "9fd17741416e8e4e", + "start_timestamp": ts.timestamp() - 0.5, + "timestamp": ts.timestamp(), + "trace_id": "d3d20f000885466b8c8f947c9b92b8d3", + "origin": origin, + "exclusive_time": 0, + "measurements": {}, + "segment_id": "8a6626cc9bdd5d9b", + }, + trace_info={ + "trace_id": "d3d20f000885466b8c8f947c9b92b8d3", + "public_key": project_config["publicKeys"][0]["publicKey"], + "transaction": "/insights/projects/", + }, + ) + + relay.send_envelope(project_id, envelope) + + attributes, fields = lcp_cls_inp_differences(mode) + + # If the span's origin is "manual", the name should be the same as the description. + # Otherwise it should be backfilled according to conventions, which falls back to op. + expected_name = "ui.webvital.lcp" + if origin == "manual": + expected_name = "Test span" + + assert spans_consumer.get_span() == { + "attributes": { + "client.address": {"type": "string", "value": "127.0.0.1"}, + "browser.name": {"type": "string", "value": "Firefox"}, + "sentry.description": {"type": "string", "value": "Test span"}, + "sentry.dsc.transaction": { + "type": "string", + "value": "/insights/projects/", + }, + "sentry.dsc.project_id": {"type": "string", "value": "42"}, + "sentry.dsc.trace_id": { + "type": "string", + "value": "d3d20f000885466b8c8f947c9b92b8d3", + }, + "sentry.environment": {"type": "string", "value": "prod"}, + "sentry.exclusive_time": {"type": "double", "value": 0.0}, + "sentry.op": {"type": "string", "value": "ui.webvital.lcp"}, + "sentry.origin": {"type": "string", "value": origin}, + "sentry.pageload.span_id": {"type": "string", "value": "8a6626cc9bdd5d9b"}, + "sentry.release": { + "type": "string", + "value": "frontend@488531b11e6401fa530ac25554d44426e6ef0f0b", + }, + "sentry.replay_id": { + "type": "string", + "value": "3d76a6311de149b9b3f560827ea0ecf9", + }, + "sentry.report_event": {"type": "string", "value": "navigation"}, + "sentry.segment.name": {"type": "string", "value": "/insights/projects/"}, + "sentry.transaction": {"type": "string", "value": "/insights/projects/"}, + "user_agent.original": { + "type": "string", + "value": "RelayIntegrationTests/1.0.0 Firefox/42.0", + }, + **attributes, + }, + "downsampled_retention_days": 90, + "end_timestamp": time_within(ts), + "key_id": 123, + "name": expected_name, + "organization_id": 1, + "project_id": 42, + "received": time_within(ts), + "retention_days": 90, + "accepted_outcome_emitted": True, + "span_id": "9fd17741416e8e4e", + "start_timestamp": time_within(ts.timestamp() - 0.5), + "status": "ok", + "trace_id": "d3d20f000885466b8c8f947c9b92b8d3", + **fields, + } + + def _if_dict(cond, then): return then if cond else {} From 5a42efc61222f74dfd314d5952b8e69dada8c925 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Wed, 10 Jun 2026 16:59:17 +0200 Subject: [PATCH 2/7] doc comments --- relay-spans/src/name.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/relay-spans/src/name.rs b/relay-spans/src/name.rs index 5e2c816713c..e42a896530d 100644 --- a/relay-spans/src/name.rs +++ b/relay-spans/src/name.rs @@ -5,7 +5,7 @@ use relay_protocol::{Getter, GetterIter, Val}; /// Constructs a name attribute for a V1 span. /// -/// If the span's origin is `"manual"`, its description will be used as the name. +/// If the span's origin is `"manual"`, its description is used as the name. /// Otherwise, the name is constructed following the rules defined in sentry-conventions. pub fn name_for_span(span: &Span) -> Option { let origin = span.origin.value().map(|o| o.as_str()); @@ -33,7 +33,7 @@ pub fn name_for_span(span: &Span) -> Option { /// Constructs a name attribute for a V2 span, based on its attributes. /// /// If the attributes contain [`SENTRY__ORIGIN`] with the value `"manual"`, -/// the description (contained in [`SENTRY__DESCRIPTION`]) will be used as the name. +/// the description (contained in [`SENTRY__DESCRIPTION`]) is used as the name. /// Otherwise, the name is constructed following the rules defined in sentry-conventions. pub fn name_for_attributes(attributes: &Attributes) -> Option { let origin = attributes From 03de6020263dfe37e3854c5a5af44c137aee38f7 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Wed, 10 Jun 2026 17:24:27 +0200 Subject: [PATCH 3/7] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67b4e4ab4e7..d25d1aa61dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Add metadata support for the `/upload` endpoint. ([#6028](https://github.com/getsentry/relay/pull/6028)) - Infer user agents and client addresses in the V2 standalone span pipeline. ([#6047](https://github.com/getsentry/relay/pull/6047)) +- Equalize name and description for spans with "manual" origin. ([#6070](https://github.com/getsentry/relay/pull/6070)) **Bug Fixes**: From 01e0fff80e1621c7acbf175722c2788c8616b196 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Thu, 11 Jun 2026 10:25:15 +0200 Subject: [PATCH 4/7] Simplify --- relay-server/src/processing/spans/process.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/relay-server/src/processing/spans/process.rs b/relay-server/src/processing/spans/process.rs index d6630b12a08..00db24433be 100644 --- a/relay-server/src/processing/spans/process.rs +++ b/relay-server/src/processing/spans/process.rs @@ -290,18 +290,15 @@ fn normalize_span( } pub fn backfill_description(spans: &mut Managed) { - spans.retain_with_context( - |spans| (&mut spans.spans, &()), - |span, _, _| backfill_span_description(&mut span.span), - ); -} + spans.modify(|span, _| { + for span in &mut span.spans { + let Some(span) = span.span.value_mut() else { + return; + }; -fn backfill_span_description(span: &mut Annotated) -> Result<(), Infallible> { - let Some(span) = span.value_mut() else { - return Ok(()); - }; - eap::normalize_sentry_description(&mut span.attributes, &span.name); - Ok(()) + eap::normalize_sentry_description(&mut span.attributes, &span.name); + } + }); } /// Validates the start and end timestamps of a span. From 18d7a6f963ca6e2a34905433231d07c574a3243d Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Thu, 11 Jun 2026 10:41:58 +0200 Subject: [PATCH 5/7] Fix --- relay-server/src/processing/spans/process.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relay-server/src/processing/spans/process.rs b/relay-server/src/processing/spans/process.rs index 00db24433be..07d84338ea5 100644 --- a/relay-server/src/processing/spans/process.rs +++ b/relay-server/src/processing/spans/process.rs @@ -293,7 +293,7 @@ pub fn backfill_description(spans: &mut Managed) { spans.modify(|span, _| { for span in &mut span.spans { let Some(span) = span.span.value_mut() else { - return; + continue; }; eap::normalize_sentry_description(&mut span.attributes, &span.name); From e7c73068cd051d00ea76a137bdb3a8b706d9ff47 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Thu, 11 Jun 2026 12:50:14 +0200 Subject: [PATCH 6/7] import --- relay-server/src/processing/spans/process.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/relay-server/src/processing/spans/process.rs b/relay-server/src/processing/spans/process.rs index 07d84338ea5..abbcb8f64ab 100644 --- a/relay-server/src/processing/spans/process.rs +++ b/relay-server/src/processing/spans/process.rs @@ -1,5 +1,4 @@ use std::collections::BTreeMap; -use std::convert::Infallible; use std::time::Duration; use relay_event_normalization::eap::ClientUserAgentInfo; From 7dbff4a1ba3459407690097118369de17b33c754 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Thu, 11 Jun 2026 14:18:34 +0200 Subject: [PATCH 7/7] feedback --- relay-spans/src/name.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/relay-spans/src/name.rs b/relay-spans/src/name.rs index e42a896530d..bd076fa35f5 100644 --- a/relay-spans/src/name.rs +++ b/relay-spans/src/name.rs @@ -11,7 +11,7 @@ pub fn name_for_span(span: &Span) -> Option { let origin = span.origin.value().map(|o| o.as_str()); let description = span.description.value().map(|d| d.as_str()); - if let Some(name) = name_for_origin_and_description(origin, description) { + if let Some(name) = name_from_origin_and_description(origin, description) { return Some(name); } @@ -43,7 +43,7 @@ pub fn name_for_attributes(attributes: &Attributes) -> Option { .get_value(SENTRY__DESCRIPTION) .and_then(|d| d.as_str()); - if let Some(name) = name_for_origin_and_description(origin, description) { + if let Some(name) = name_from_origin_and_description(origin, description) { return Some(name); } @@ -51,7 +51,7 @@ pub fn name_for_attributes(attributes: &Attributes) -> Option { Some(name_for_op_and_attributes(op, &AttributeGetter(attributes))) } -fn name_for_origin_and_description( +fn name_from_origin_and_description( origin: Option<&str>, description: Option<&str>, ) -> Option {