-
Notifications
You must be signed in to change notification settings - Fork 118
feat(spans): Equalize name and description for manual spans #6070
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<Attributes>) { | |
| 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<Attributes>, | ||
| name: &Annotated<String>, | ||
| ) { | ||
| if attributes | ||
| .value() | ||
| .is_some_and(|attrs| attrs.contains_key(SENTRY__DESCRIPTION)) | ||
| { | ||
| return; | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Empty description blocks manual backfillMedium Severity
Reviewed by Cursor Bugbot for commit 03de602. Configure here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's intended that if someone manually sets a description it doesn't get clobbered by the name. This includes the case of an empty description. |
||
|
|
||
| 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<Attributes>) { | |
| )] | ||
| 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<Attributes>) { | |
| ]; | ||
|
|
||
| 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()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Legacy action prefers DB over HTTPLow Severity In Reviewed by Cursor Bugbot for commit 03de602. Configure here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The behavior for spans which have both db and http attributes (which should not happen in practice) was already arbitrary before, now it's arbitrary in the other direction. |
||
| } | ||
|
|
||
| if !attributes.contains_key(SENTRY__DOMAIN) | ||
|
|
@@ -803,12 +832,6 @@ pub fn write_legacy_attributes(attributes: &mut Annotated<Attributes>) { | |
| }, | ||
| ); | ||
| } | ||
|
|
||
| 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," | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -204,6 +204,7 @@ impl processing::Processor for SpansProcessor { | |
| }; | ||
|
|
||
| process::scrub(&mut spans, ctx); | ||
| process::backfill_description(&mut spans); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe |
||
|
|
||
| match dynamic_sampling::try_split_indexed_and_total(spans, ctx) { | ||
| Either::Left(spans) => Ok(Output::just(SpanOutput::TotalAndIndexed(spans))), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -288,6 +288,18 @@ fn normalize_span( | |
| Ok(()) | ||
| } | ||
|
|
||
| pub fn backfill_description(spans: &mut Managed<ExpandedSpans>) { | ||
| spans.modify(|span, _| { | ||
| for span in &mut span.spans { | ||
| let Some(span) = span.span.value_mut() else { | ||
| continue; | ||
| }; | ||
|
cursor[bot] marked this conversation as resolved.
|
||
|
|
||
| eap::normalize_sentry_description(&mut span.attributes, &span.name); | ||
| } | ||
| }); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Manual spans desync after scrubMedium Severity For manual-origin spans, Additional Locations (1)Reviewed by Cursor Bugbot for commit e7c7306. Configure here. |
||
|
|
||
| /// Validates the start and end timestamps of a span. | ||
| /// | ||
| /// The start timestamp must not be after the end timestamp. | ||
|
|
@@ -928,7 +940,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 +1001,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 +1120,6 @@ mod tests { | |
| &span, | ||
| &[ | ||
| (SENTRY__OP, "db"), | ||
| (SENTRY__DESCRIPTION, "select * from users where id = 1"), | ||
| ( | ||
| SENTRY__NORMALIZED_DESCRIPTION, | ||
| "SELECT * FROM users WHERE id = %s", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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::<TraceContext>() | ||
| .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(); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Trace origin applied to child spansMedium Severity When extracting transaction child spans, missing span Reviewed by Cursor Bugbot for commit 03de602. Configure here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. |
||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Transaction spans skip description backfillMedium Severity
Additional Locations (1)Reviewed by Cursor Bugbot for commit 18d7a6f. Configure here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Backfilling the description from the name is not a concern for transaction-extracted spans, they need to worry about the converse. |
||
| // If a profile is associated with the transaction, also associate it with its | ||
| // child spans. | ||
| new_span.profile_id = transaction_span.profile_id.clone(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<Attributes>, | ||
| name: &Annotated<String>, | ||
| ) -> Option<String> { | ||
| let attributes = attributes.value()?; | ||
|
|
||
| if attributes | ||
| .get_value(SENTRY__ORIGIN) | ||
| .and_then(|o| o.as_str()) | ||
| == Some("manual") | ||
|
Comment on lines
+21
to
+24
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This formatting is weird af |
||
| { | ||
| return name.value().cloned(); | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Manual origin skips query backfillMedium Severity
Additional Locations (1)Reviewed by Cursor Bugbot for commit 61a998e. Configure here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I legitimately don't understand what this is trying to say.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is trying to say "A child span might have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Manual backfill skips PII scrubMedium Severity For Additional Locations (1)Reviewed by Cursor Bugbot for commit 7dbff4a. Configure here. |
||
|
|
||
| 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 | ||
| } | ||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might need a check which doesn't just use
value()but also considers meta,Emptytrait might be able to do that for you.