Skip to content

Commit e40a841

Browse files
feat(spans): extract transaction breakdowns into measurements (#4600)
closes #4598 More details found in the issue above. This PR extracts transaction breakdowns into measurements, so that they are automatically stored in the eap dataset. (Snuba does not look at breakdowns at all, but it automatically stores all measurements) We are moving the insights overview pages to eap, and the product relies on these breakdown values. --------- Co-authored-by: Riccardo Busetti <[email protected]>
1 parent cba96da commit e40a841

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- Add ui chunk profiling data category. ([#4593](https://github.com/getsentry/relay/pull/4593))
1818
- Switch global rate limiter to a service. ([#4581](https://github.com/getsentry/relay/pull/4581))
1919
- Remove `parent_span_link` from `SpanLink` struct. ([#4594](https://github.com/getsentry/relay/pull/4594))
20+
- Extract transaction breakdowns into measurements. ([#4600](https://github.com/getsentry/relay/pull/4600))
2021

2122
## 25.3.0
2223

relay-event-normalization/src/normalize/span/tag_extraction.rs

+104
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,35 @@ fn extract_shared_tags(event: &Event) -> SharedTags {
400400
fn extract_segment_measurements(event: &Event) -> BTreeMap<String, Measurement> {
401401
let mut measurements = BTreeMap::new();
402402

403+
// Extract breakdowns into measurements, similar to /metrics_extraction/transacitons/mod.rs
404+
if let Some(breakdowns) = event.breakdowns.value() {
405+
for (breakdown, measurement_list) in breakdowns.iter() {
406+
if let Some(measurement_list) = measurement_list.value() {
407+
for (measurement_name, annotated) in measurement_list.iter() {
408+
if measurement_name == "total.time" {
409+
continue;
410+
}
411+
412+
let Some(value) = annotated
413+
.value()
414+
.and_then(|value| value.value.value())
415+
.copied()
416+
else {
417+
continue;
418+
};
419+
420+
measurements.insert(
421+
format!("{breakdown}.{measurement_name}"),
422+
Measurement {
423+
value: value.into(),
424+
unit: MetricUnit::Duration(DurationUnit::MilliSecond).into(),
425+
},
426+
);
427+
}
428+
}
429+
}
430+
}
431+
403432
if let Some(trace_context) = event.context::<TraceContext>() {
404433
if let Some(op) = extract_transaction_op(trace_context) {
405434
if op == "queue.publish" || op == "queue.process" {
@@ -2309,6 +2338,81 @@ LIMIT 1
23092338
"###);
23102339
}
23112340

2341+
#[test]
2342+
fn test_extract_breakdown_tags() {
2343+
let json = r#"
2344+
{
2345+
"type": "transaction",
2346+
"platform": "javascript",
2347+
"start_timestamp": "2021-04-26T07:59:01+0100",
2348+
"timestamp": "2021-04-26T08:00:00+0100",
2349+
"transaction": "foo",
2350+
"contexts": {
2351+
"trace": {
2352+
"trace_id": "ff62a8b040f340bda5d830223def1d81",
2353+
"span_id": "bd429c44b67a3eb4"
2354+
}
2355+
},
2356+
"breakdowns": {
2357+
"span_ops": {
2358+
"ops.http": {
2359+
"value": 1000,
2360+
"unit": "millisecond"
2361+
},
2362+
"ops.resource": {
2363+
"value": 420,
2364+
"unit": "millisecond"
2365+
},
2366+
"ops.ui": {
2367+
"value": 27000,
2368+
"unit": "millisecond"
2369+
},
2370+
"total.time": {
2371+
"value": 45000,
2372+
"unit": "millisecond"
2373+
}
2374+
}
2375+
},
2376+
"spans": []
2377+
}
2378+
"#;
2379+
2380+
let event = Annotated::<Event>::from_json(json)
2381+
.unwrap()
2382+
.into_value()
2383+
.unwrap();
2384+
2385+
let mut spans = [Span::from(&event).into()];
2386+
extract_segment_span_tags(&event, &mut spans);
2387+
let segment_span: &Annotated<Span> = &spans[0];
2388+
let measurements = segment_span.value().unwrap().measurements.value().unwrap();
2389+
2390+
assert_debug_snapshot!(measurements, @r###"
2391+
Measurements(
2392+
{
2393+
"span_ops.ops.http": Measurement {
2394+
value: 1000.0,
2395+
unit: Duration(
2396+
MilliSecond,
2397+
),
2398+
},
2399+
"span_ops.ops.resource": Measurement {
2400+
value: 420.0,
2401+
unit: Duration(
2402+
MilliSecond,
2403+
),
2404+
},
2405+
"span_ops.ops.ui": Measurement {
2406+
value: 27000.0,
2407+
unit: Duration(
2408+
MilliSecond,
2409+
),
2410+
},
2411+
},
2412+
)
2413+
"###);
2414+
}
2415+
23122416
#[test]
23132417
fn test_does_not_extract_segment_tags_and_measurements_on_child_spans() {
23142418
let json = r#"

0 commit comments

Comments
 (0)