diff --git a/CHANGELOG.md b/CHANGELOG.md index 49c77b27130..f5a158bfb61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,6 @@ ## Unreleased - **Bug fixes:** - Respect country code TLDs when scrubbing span tags. ([#3458](https://github.com/getsentry/relay/pull/3458)) @@ -11,6 +10,7 @@ - Use same keys for OTel span attributes and Sentry span data. ([#3457](https://github.com/getsentry/relay/pull/3457)) - Support passing owner when upserting Monitors. ([#3468](https://github.com/getsentry/relay/pull/3468)) +- Extract `frames.slow`, `frames.frozen`, and `frames.total` metrics from mobile spans. ([#3473](https://github.com/getsentry/relay/pull/3473)) **Internal**: diff --git a/relay-dynamic-config/src/defaults.rs b/relay-dynamic-config/src/defaults.rs index 790a2d9385e..7b46f674c8c 100644 --- a/relay-dynamic-config/src/defaults.rs +++ b/relay-dynamic-config/src/defaults.rs @@ -631,6 +631,102 @@ fn span_metrics( .always(), // already guarded by condition on metric ], }, + MetricSpec { + category: DataCategory::Span, + mri: "g:spans/mobile.slow_frames@none".into(), + field: Some("span.measurements.frames.slow.value".into()), + condition: Some(is_mobile.clone() & duration_condition.clone()), + tags: vec![ + Tag::with_key("transaction") + .from_field("span.sentry_tags.transaction") + .always(), + Tag::with_key("environment") + .from_field("span.sentry_tags.environment") + .always(), + Tag::with_key("release") + .from_field("span.sentry_tags.release") + .always(), + Tag::with_key("span.description") + .from_field("span.sentry_tags.description") + .always(), + Tag::with_key("span.op") + .from_field("span.sentry_tags.op") + .always(), + Tag::with_key("span.group") + .from_field("span.sentry_tags.group") + .always(), + Tag::with_key("device.class") + .from_field("span.sentry_tags.device.class") + .always(), + Tag::with_key("os.name") + .from_field("span.sentry_tags.os.name") + .always(), + ], + }, + MetricSpec { + category: DataCategory::Span, + mri: "g:spans/mobile.frozen_frames@none".into(), + field: Some("span.measurements.frames.frozen.value".into()), + condition: Some(is_mobile.clone() & duration_condition.clone()), + tags: vec![ + Tag::with_key("transaction") + .from_field("span.sentry_tags.transaction") + .always(), + Tag::with_key("environment") + .from_field("span.sentry_tags.environment") + .always(), + Tag::with_key("release") + .from_field("span.sentry_tags.release") + .always(), + Tag::with_key("span.description") + .from_field("span.sentry_tags.description") + .always(), + Tag::with_key("span.op") + .from_field("span.sentry_tags.op") + .always(), + Tag::with_key("span.group") + .from_field("span.sentry_tags.group") + .always(), + Tag::with_key("device.class") + .from_field("span.sentry_tags.device.class") + .always(), + Tag::with_key("os.name") + .from_field("span.sentry_tags.os.name") + .always(), + ], + }, + MetricSpec { + category: DataCategory::Span, + mri: "g:spans/mobile.total_frames@none".into(), + field: Some("span.measurements.frames.total.value".into()), + condition: Some(is_mobile.clone() & duration_condition.clone()), + tags: vec![ + Tag::with_key("transaction") + .from_field("span.sentry_tags.transaction") + .always(), + Tag::with_key("environment") + .from_field("span.sentry_tags.environment") + .always(), + Tag::with_key("release") + .from_field("span.sentry_tags.release") + .always(), + Tag::with_key("span.description") + .from_field("span.sentry_tags.description") + .always(), + Tag::with_key("span.op") + .from_field("span.sentry_tags.op") + .always(), + Tag::with_key("span.group") + .from_field("span.sentry_tags.group") + .always(), + Tag::with_key("device.class") + .from_field("span.sentry_tags.device.class") + .always(), + Tag::with_key("os.name") + .from_field("span.sentry_tags.os.name") + .always(), + ], + }, ]; if double_write_distributions_as_gauges { diff --git a/relay-event-normalization/src/normalize/span/tag_extraction.rs b/relay-event-normalization/src/normalize/span/tag_extraction.rs index 8d49da145b5..13f49fe9f7c 100644 --- a/relay-event-normalization/src/normalize/span/tag_extraction.rs +++ b/relay-event-normalization/src/normalize/span/tag_extraction.rs @@ -197,7 +197,7 @@ pub fn extract_span_tags(event: &Event, spans: &mut [Annotated], max_tag_v .collect(), ); - extract_measurements(span); + extract_measurements(span, is_mobile); } } @@ -598,7 +598,7 @@ pub fn extract_tags( } /// Copies specific numeric values from span data to span measurements. -pub fn extract_measurements(span: &mut Span) { +pub fn extract_measurements(span: &mut Span, is_mobile: bool) { let Some(span_op) = span.op.as_str() else { return; }; @@ -659,6 +659,33 @@ pub fn extract_measurements(span: &mut Span) { } } } + + if is_mobile { + if let Some(data) = span.data.value() { + for (field, key) in [ + (&data.frames_frozen, "frames.frozen"), + (&data.frames_slow, "frames.slow"), + (&data.frames_total, "frames.total"), + ] { + if let Some(value) = match field.value() { + Some(Value::F64(f)) => Some(*f), + Some(Value::I64(i)) => Some(*i as f64), + Some(Value::U64(u)) => Some(*u as f64), + _ => None, + } { + let measurements = span.measurements.get_or_insert_with(Default::default); + measurements.insert( + key.into(), + Measurement { + value: value.into(), + unit: MetricUnit::None.into(), + } + .into(), + ); + } + } + } + } } /// Finds first matching span and get its timestamp. diff --git a/relay-event-schema/src/protocol/span.rs b/relay-event-schema/src/protocol/span.rs index 9d1fa845f87..92088f734d9 100644 --- a/relay-event-schema/src/protocol/span.rs +++ b/relay-event-schema/src/protocol/span.rs @@ -303,6 +303,18 @@ pub struct SpanData { #[metastructure(field = "sentry.sdk.name")] pub sdk_name: Annotated, + /// Slow Frames + #[metastructure(field = "sentry.frames.slow", legacy_alias = "frames.slow")] + pub frames_slow: Annotated, + + /// Frozen Frames + #[metastructure(field = "sentry.frames.frozen", legacy_alias = "frames.frozen")] + pub frames_frozen: Annotated, + + /// Total Frames + #[metastructure(field = "sentry.frames.total", legacy_alias = "frames.total")] + pub frames_total: Annotated, + /// Other fields in `span.data`. #[metastructure(additional_properties, pii = "true", retain = "true")] other: Object, @@ -497,7 +509,10 @@ mod tests { "code.filepath": "task.py", "code.lineno": 123, "code.function": "fn()", - "code.namespace": "ns" + "code.namespace": "ns", + "frames.slow": 1, + "frames.frozen": 2, + "frames.total": 9 }"#; let data = Annotated::::from_json(data) .unwrap() @@ -547,6 +562,15 @@ mod tests { user: ~, replay_id: ~, sdk_name: ~, + frames_slow: I64( + 1, + ), + frames_frozen: I64( + 2, + ), + frames_total: I64( + 9, + ), other: { "bar": String( "3", diff --git a/relay-event-schema/src/protocol/span/convert.rs b/relay-event-schema/src/protocol/span/convert.rs index 035768ef810..0df665c36cf 100644 --- a/relay-event-schema/src/protocol/span/convert.rs +++ b/relay-event-schema/src/protocol/span/convert.rs @@ -288,6 +288,9 @@ mod tests { user: ~, replay_id: ~, sdk_name: "sentry.php", + frames_slow: ~, + frames_frozen: ~, + frames_total: ~, other: {}, }, sentry_tags: ~, diff --git a/relay-server/src/metrics_extraction/event.rs b/relay-server/src/metrics_extraction/event.rs index c31b048a2ea..f1f10a4a987 100644 --- a/relay-server/src/metrics_extraction/event.rs +++ b/relay-server/src/metrics_extraction/event.rs @@ -1151,7 +1151,12 @@ mod tests { "span_id": "bd429c44b67a3eb2", "start_timestamp": 1597976300.0000000, "timestamp": 1597976303.0000000, - "trace_id": "ff62a8b040f340bda5d830223def1d81" + "trace_id": "ff62a8b040f340bda5d830223def1d81", + "data": { + "frames.slow": 1, + "frames.frozen": 2, + "frames.total": 9 + } }, { "op": "app.start.cold", diff --git a/relay-server/src/metrics_extraction/snapshots/relay_server__metrics_extraction__event__tests__extract_span_metrics_mobile.snap b/relay-server/src/metrics_extraction/snapshots/relay_server__metrics_extraction__event__tests__extract_span_metrics_mobile.snap index 60e7f3f3292..fdcfec4593f 100644 --- a/relay-server/src/metrics_extraction/snapshots/relay_server__metrics_extraction__event__tests__extract_span_metrics_mobile.snap +++ b/relay-server/src/metrics_extraction/snapshots/relay_server__metrics_extraction__event__tests__extract_span_metrics_mobile.snap @@ -74,7 +74,50 @@ expression: "(&event.value().unwrap().spans, metrics)" tags: ~, origin: ~, profile_id: ~, - data: ~, + data: SpanData { + app_start_type: ~, + browser_name: ~, + code_filepath: ~, + code_lineno: ~, + code_function: ~, + code_namespace: ~, + db_operation: ~, + db_system: ~, + environment: ~, + release: ~, + http_decoded_response_content_length: ~, + http_request_method: ~, + http_response_content_length: ~, + http_response_transfer_size: ~, + resource_render_blocking_status: ~, + server_address: ~, + cache_hit: ~, + cache_item_size: ~, + http_response_status_code: ~, + ai_pipeline_name: ~, + ai_input_messages: ~, + ai_completion_tokens_used: ~, + ai_prompt_tokens_used: ~, + ai_total_tokens_used: ~, + ai_responses: ~, + thread_name: ~, + segment_name: ~, + ui_component_name: ~, + url_scheme: ~, + user: ~, + replay_id: ~, + sdk_name: ~, + frames_slow: I64( + 1, + ), + frames_frozen: I64( + 2, + ), + frames_total: I64( + 9, + ), + other: {}, + }, sentry_tags: { "app_start_type": "warm", "device.class": "1", @@ -91,7 +134,22 @@ expression: "(&event.value().unwrap().spans, metrics)" "ttid": "ttid", }, received: ~, - measurements: ~, + measurements: Measurements( + { + "frames.frozen": Measurement { + value: 2.0, + unit: None, + }, + "frames.slow": Measurement { + value: 1.0, + unit: None, + }, + "frames.total": Measurement { + value: 9.0, + unit: None, + }, + }, + ), _metrics_summary: ~, platform: ~, was_transaction: ~, @@ -346,6 +404,9 @@ expression: "(&event.value().unwrap().spans, metrics)" user: ~, replay_id: ~, sdk_name: ~, + frames_slow: ~, + frames_frozen: ~, + frames_total: ~, other: {}, }, sentry_tags: { @@ -430,6 +491,9 @@ expression: "(&event.value().unwrap().spans, metrics)" user: ~, replay_id: ~, sdk_name: ~, + frames_slow: ~, + frames_frozen: ~, + frames_total: ~, other: {}, }, sentry_tags: { @@ -717,6 +781,84 @@ expression: "(&event.value().unwrap().spans, metrics)" merges: 1, }, }, + Bucket { + timestamp: UnixTimestamp(1597976303), + width: 0, + name: MetricName( + "g:spans/mobile.slow_frames@none", + ), + value: Gauge( + GaugeValue { + last: 1.0, + min: 1.0, + max: 1.0, + sum: 1.0, + count: 1, + }, + ), + tags: { + "device.class": "1", + "os.name": "iOS", + "release": "1.2.3", + "span.op": "ui.load.initial_display", + "transaction": "gEt /api/:version/users/", + }, + metadata: BucketMetadata { + merges: 1, + }, + }, + Bucket { + timestamp: UnixTimestamp(1597976303), + width: 0, + name: MetricName( + "g:spans/mobile.frozen_frames@none", + ), + value: Gauge( + GaugeValue { + last: 2.0, + min: 2.0, + max: 2.0, + sum: 2.0, + count: 1, + }, + ), + tags: { + "device.class": "1", + "os.name": "iOS", + "release": "1.2.3", + "span.op": "ui.load.initial_display", + "transaction": "gEt /api/:version/users/", + }, + metadata: BucketMetadata { + merges: 1, + }, + }, + Bucket { + timestamp: UnixTimestamp(1597976303), + width: 0, + name: MetricName( + "g:spans/mobile.total_frames@none", + ), + value: Gauge( + GaugeValue { + last: 9.0, + min: 9.0, + max: 9.0, + sum: 9.0, + count: 1, + }, + ), + tags: { + "device.class": "1", + "os.name": "iOS", + "release": "1.2.3", + "span.op": "ui.load.initial_display", + "transaction": "gEt /api/:version/users/", + }, + metadata: BucketMetadata { + merges: 1, + }, + }, Bucket { timestamp: UnixTimestamp(1597976303), width: 0, diff --git a/relay-server/src/services/processor/span/processing.rs b/relay-server/src/services/processor/span/processing.rs index f8b80f1d293..7699c238796 100644 --- a/relay-server/src/services/processor/span/processing.rs +++ b/relay-server/src/services/processor/span/processing.rs @@ -527,7 +527,7 @@ fn normalize( normalize_performance_score(&mut event, performance_score); span.measurements = event.measurements; - tag_extraction::extract_measurements(span); + tag_extraction::extract_measurements(span, is_mobile); process_value( annotated_span, diff --git a/relay-spans/src/span.rs b/relay-spans/src/span.rs index bd94ada6066..7391aa88112 100644 --- a/relay-spans/src/span.rs +++ b/relay-spans/src/span.rs @@ -657,6 +657,9 @@ mod tests { user: ~, replay_id: ~, sdk_name: "sentry.php", + frames_slow: ~, + frames_frozen: ~, + frames_total: ~, other: {}, }, sentry_tags: ~,