Skip to content

Commit 40f715e

Browse files
feat(performance): Extracts lcp related tags from event trace context (#4074)
Extracts `lcp.element`, `lcp.size`, `lcp.id`, and `lcp.url` tags from the event trace context. This is to support performance views in the product that relied on these tags existing but were no longer being sent as tags. getsentry/sentry-javascript#13411 #skip-changelog
1 parent 7af92ee commit 40f715e

File tree

5 files changed

+222
-0
lines changed

5 files changed

+222
-0
lines changed

relay-event-normalization/src/event.rs

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ fn normalize(event: &mut Event, meta: &mut Meta, config: &NormalizationConfig) {
322322
normalize_ai_measurements(event, config.ai_model_costs);
323323
normalize_breakdowns(event, config.breakdowns_config); // Breakdowns are part of the metric extraction too
324324
normalize_default_attributes(event, meta, config);
325+
normalize_trace_context_tags(event);
325326

326327
let _ = processor::apply(&mut event.request, |request, _| {
327328
request::normalize_request(request);
@@ -977,6 +978,41 @@ pub fn normalize_performance_score(
977978
version
978979
}
979980

981+
// Extracts lcp related tags from the trace context.
982+
fn normalize_trace_context_tags(event: &mut Event) {
983+
let tags = &mut event.tags.value_mut().get_or_insert_with(Tags::default).0;
984+
if let Some(contexts) = event.contexts.value() {
985+
if let Some(trace_context) = contexts.get::<TraceContext>() {
986+
if let Some(data) = trace_context.data.value() {
987+
if let Some(lcp_element) = data.lcp_element.value() {
988+
if !tags.contains("lcp.element") {
989+
let tag_name = "lcp.element".to_string();
990+
tags.insert(tag_name, Annotated::new(lcp_element.clone()));
991+
}
992+
}
993+
if let Some(lcp_size) = data.lcp_size.value() {
994+
if !tags.contains("lcp.size") {
995+
let tag_name = "lcp.size".to_string();
996+
tags.insert(tag_name, Annotated::new(lcp_size.to_string()));
997+
}
998+
}
999+
if let Some(lcp_id) = data.lcp_id.value() {
1000+
let tag_name = "lcp.id".to_string();
1001+
if !tags.contains("lcp.id") {
1002+
tags.insert(tag_name, Annotated::new(lcp_id.clone()));
1003+
}
1004+
}
1005+
if let Some(lcp_url) = data.lcp_url.value() {
1006+
let tag_name = "lcp.url".to_string();
1007+
if !tags.contains("lcp.url") {
1008+
tags.insert(tag_name, Annotated::new(lcp_url.clone()));
1009+
}
1010+
}
1011+
}
1012+
}
1013+
}
1014+
}
1015+
9801016
impl MutMeasurements for Event {
9811017
fn measurements(&mut self) -> &mut Annotated<Measurements> {
9821018
&mut self.measurements
@@ -4291,4 +4327,142 @@ mod tests {
42914327
);
42924328
assert!(get_value!(event.spans[0].exclusive_time).is_some());
42934329
}
4330+
4331+
#[test]
4332+
fn test_normalize_trace_context_tags_extracts_lcp_info() {
4333+
let json = r#"{
4334+
"type": "transaction",
4335+
"start_timestamp": 1,
4336+
"timestamp": 2,
4337+
"contexts": {
4338+
"trace": {
4339+
"data": {
4340+
"lcp.element": "body > div#app > div > h1#header",
4341+
"lcp.size": 24827,
4342+
"lcp.id": "header",
4343+
"lcp.url": "http://example.com/image.jpg"
4344+
}
4345+
}
4346+
},
4347+
"measurements": {
4348+
"lcp": { "value": 146.20000000298023, "unit": "millisecond" }
4349+
}
4350+
}"#;
4351+
let mut event = Annotated::<Event>::from_json(json).unwrap().0.unwrap();
4352+
normalize_trace_context_tags(&mut event);
4353+
insta::assert_ron_snapshot!(SerializableAnnotated(&Annotated::new(event)), {}, @r#"
4354+
{
4355+
"type": "transaction",
4356+
"timestamp": 2.0,
4357+
"start_timestamp": 1.0,
4358+
"contexts": {
4359+
"trace": {
4360+
"data": {
4361+
"lcp.element": "body > div#app > div > h1#header",
4362+
"lcp.size": 24827,
4363+
"lcp.id": "header",
4364+
"lcp.url": "http://example.com/image.jpg",
4365+
},
4366+
"type": "trace",
4367+
},
4368+
},
4369+
"tags": [
4370+
[
4371+
"lcp.element",
4372+
"body > div#app > div > h1#header",
4373+
],
4374+
[
4375+
"lcp.size",
4376+
"24827",
4377+
],
4378+
[
4379+
"lcp.id",
4380+
"header",
4381+
],
4382+
[
4383+
"lcp.url",
4384+
"http://example.com/image.jpg",
4385+
],
4386+
],
4387+
"measurements": {
4388+
"lcp": {
4389+
"value": 146.20000000298023,
4390+
"unit": "millisecond",
4391+
},
4392+
},
4393+
}
4394+
"#);
4395+
}
4396+
4397+
#[test]
4398+
fn test_normalize_trace_context_tags_does_not_overwrite_lcp_tags() {
4399+
let json = r#"{
4400+
"type": "transaction",
4401+
"start_timestamp": 1,
4402+
"timestamp": 2,
4403+
"contexts": {
4404+
"trace": {
4405+
"data": {
4406+
"lcp.element": "body > div#app > div > h1#id",
4407+
"lcp.size": 33333,
4408+
"lcp.id": "id",
4409+
"lcp.url": "http://example.com/another-image.jpg"
4410+
}
4411+
}
4412+
},
4413+
"tags": {
4414+
"lcp.element": "body > div#app > div > h1#header",
4415+
"lcp.size": 24827,
4416+
"lcp.id": "header",
4417+
"lcp.url": "http://example.com/image.jpg"
4418+
},
4419+
"measurements": {
4420+
"lcp": { "value": 146.20000000298023, "unit": "millisecond" }
4421+
}
4422+
}"#;
4423+
let mut event = Annotated::<Event>::from_json(json).unwrap().0.unwrap();
4424+
normalize_trace_context_tags(&mut event);
4425+
insta::assert_ron_snapshot!(SerializableAnnotated(&Annotated::new(event)), {}, @r#"
4426+
{
4427+
"type": "transaction",
4428+
"timestamp": 2.0,
4429+
"start_timestamp": 1.0,
4430+
"contexts": {
4431+
"trace": {
4432+
"data": {
4433+
"lcp.element": "body > div#app > div > h1#id",
4434+
"lcp.size": 33333,
4435+
"lcp.id": "id",
4436+
"lcp.url": "http://example.com/another-image.jpg",
4437+
},
4438+
"type": "trace",
4439+
},
4440+
},
4441+
"tags": [
4442+
[
4443+
"lcp.element",
4444+
"body > div#app > div > h1#header",
4445+
],
4446+
[
4447+
"lcp.id",
4448+
"header",
4449+
],
4450+
[
4451+
"lcp.size",
4452+
"24827",
4453+
],
4454+
[
4455+
"lcp.url",
4456+
"http://example.com/image.jpg",
4457+
],
4458+
],
4459+
"measurements": {
4460+
"lcp": {
4461+
"value": 146.20000000298023,
4462+
"unit": "millisecond",
4463+
},
4464+
},
4465+
}
4466+
"#);
4467+
}
42944468
}

relay-event-schema/src/protocol/span.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,22 @@ pub struct SpanData {
485485
#[metastructure(field = "previousRoute", pii = "maybe", skip_serialization = "empty")]
486486
pub previous_route: Annotated<Route>,
487487

488+
// The dom element responsible for the largest contentful paint.
489+
#[metastructure(field = "lcp.element")]
490+
pub lcp_element: Annotated<String>,
491+
492+
// The size of the largest contentful paint element.
493+
#[metastructure(field = "lcp.size")]
494+
pub lcp_size: Annotated<u64>,
495+
496+
// The id of the largest contentful paint element.
497+
#[metastructure(field = "lcp.id")]
498+
pub lcp_id: Annotated<String>,
499+
500+
// The url of the largest contentful paint element.
501+
#[metastructure(field = "lcp.url")]
502+
pub lcp_url: Annotated<String>,
503+
488504
/// Other fields in `span.data`.
489505
#[metastructure(
490506
additional_properties,
@@ -892,6 +908,10 @@ mod tests {
892908
),
893909
route: ~,
894910
previous_route: ~,
911+
lcp_element: ~,
912+
lcp_size: ~,
913+
lcp_id: ~,
914+
lcp_url: ~,
895915
other: {
896916
"bar": String(
897917
"3",

relay-event-schema/src/protocol/span/convert.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@ mod tests {
220220
client_address: ~,
221221
route: ~,
222222
previous_route: ~,
223+
lcp_element: ~,
224+
lcp_size: ~,
225+
lcp_id: ~,
226+
lcp_url: ~,
223227
other: {
224228
"custom_attribute": I64(
225229
42,

relay-server/src/metrics_extraction/snapshots/relay_server__metrics_extraction__event__tests__extract_span_metrics_mobile.snap

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ expression: "(&event.value().unwrap().spans, metrics)"
149149
client_address: ~,
150150
route: ~,
151151
previous_route: ~,
152+
lcp_element: ~,
153+
lcp_size: ~,
154+
lcp_id: ~,
155+
lcp_url: ~,
152156
other: {},
153157
},
154158
sentry_tags: {
@@ -497,6 +501,10 @@ expression: "(&event.value().unwrap().spans, metrics)"
497501
client_address: ~,
498502
route: ~,
499503
previous_route: ~,
504+
lcp_element: ~,
505+
lcp_size: ~,
506+
lcp_id: ~,
507+
lcp_url: ~,
500508
other: {},
501509
},
502510
sentry_tags: {
@@ -615,6 +623,10 @@ expression: "(&event.value().unwrap().spans, metrics)"
615623
client_address: ~,
616624
route: ~,
617625
previous_route: ~,
626+
lcp_element: ~,
627+
lcp_size: ~,
628+
lcp_id: ~,
629+
lcp_url: ~,
618630
other: {},
619631
},
620632
sentry_tags: {
@@ -785,6 +797,10 @@ expression: "(&event.value().unwrap().spans, metrics)"
785797
client_address: ~,
786798
route: ~,
787799
previous_route: ~,
800+
lcp_element: ~,
801+
lcp_size: ~,
802+
lcp_id: ~,
803+
lcp_url: ~,
788804
other: {},
789805
},
790806
sentry_tags: {
@@ -903,6 +919,10 @@ expression: "(&event.value().unwrap().spans, metrics)"
903919
client_address: ~,
904920
route: ~,
905921
previous_route: ~,
922+
lcp_element: ~,
923+
lcp_size: ~,
924+
lcp_id: ~,
925+
lcp_url: ~,
906926
other: {},
907927
},
908928
sentry_tags: {

relay-spans/src/span.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,10 @@ mod tests {
687687
client_address: ~,
688688
route: ~,
689689
previous_route: ~,
690+
lcp_element: ~,
691+
lcp_size: ~,
692+
lcp_id: ~,
693+
lcp_url: ~,
690694
other: {},
691695
},
692696
sentry_tags: ~,

0 commit comments

Comments
 (0)