Skip to content

Commit f51ca94

Browse files
fix(webvitals): Drop negative web vitals from measurements (#2982)
Add `allow_negative` to `BuiltinMeasurementKey`. Filter out negative BuiltinMeasurements if `allow_negative` is false.
1 parent f437f20 commit f51ca94

File tree

4 files changed

+87
-3
lines changed

4 files changed

+87
-3
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- Proactively move on-disk spool to memory. ([#2949](https://github.com/getsentry/relay/pull/2949))
1212
- Default missing `Event.platform` and `Event.level` fields during light normalization. ([#2961](https://github.com/getsentry/relay/pull/2961))
1313
- Copy event measurements to span & normalize span measurements. ([#2953](https://github.com/getsentry/relay/pull/2953))
14+
- Add `allow_negative` to `BuiltinMeasurementKey`. Filter out negative BuiltinMeasurements if `allow_negative` is false. ([#2982](https://github.com/getsentry/relay/pull/2982))
1415
- Add possiblity to block metrics or their tags with glob-patterns. ([#2954](https://github.com/getsentry/relay/pull/2954), [#2973](https://github.com/getsentry/relay/pull/2973))
1516

1617
**Bug Fixes**:

py/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- Add `allow_negative` to `BuiltinMeasurementKey`. Filter out negative BuiltinMeasurements if `allow_negative` is false. ([#2982](https://github.com/getsentry/relay/pull/2982))
6+
37
## 0.8.44
48

59
### Various fixes & improvements

relay-event-normalization/src/event.rs

+70-3
Original file line numberDiff line numberDiff line change
@@ -725,16 +725,16 @@ pub fn normalize_measurements(
725725
) {
726726
normalize_mobile_measurements(measurements);
727727
normalize_units(measurements);
728-
if let Some(measurements_config) = measurements_config {
729-
remove_invalid_measurements(measurements, meta, measurements_config, max_mri_len);
730-
}
731728

732729
let duration_millis = match (start_timestamp, end_timestamp) {
733730
(Some(start), Some(end)) => relay_common::time::chrono_to_positive_millis(end - start),
734731
_ => 0.0,
735732
};
736733

737734
compute_measurements(duration_millis, measurements);
735+
if let Some(measurements_config) = measurements_config {
736+
remove_invalid_measurements(measurements, meta, measurements_config, max_mri_len);
737+
}
738738
}
739739

740740
/// Computes performance score measurements.
@@ -994,6 +994,16 @@ fn remove_invalid_measurements(
994994
// Check if this is a builtin measurement:
995995
for builtin_measurement in measurements_config.builtin_measurement_keys() {
996996
if builtin_measurement.name() == name {
997+
let value = measurement.value.value().unwrap_or(&0.0);
998+
// Drop negative values if the builtin measurement does not allow them.
999+
if !builtin_measurement.allow_negative() && *value < 0.0 {
1000+
meta.add_error(Error::invalid(format!(
1001+
"Negative value for measurement {name} not allowed: {value}",
1002+
)));
1003+
removed_measurements
1004+
.insert(name.clone(), Annotated::new(std::mem::take(measurement)));
1005+
return false;
1006+
}
9971007
// If the unit matches a built-in measurement, we allow it.
9981008
// If the name matches but the unit is wrong, we do not even accept it as a custom measurement,
9991009
// and just drop it instead.
@@ -2543,4 +2553,61 @@ mod tests {
25432553
"invalid transaction event: timestamp is too stale"
25442554
);
25452555
}
2556+
2557+
#[test]
2558+
fn test_filter_negative_web_vital_measurements() {
2559+
let json = r#"
2560+
{
2561+
"type": "transaction",
2562+
"timestamp": "2021-04-26T08:00:05+0100",
2563+
"start_timestamp": "2021-04-26T08:00:00+0100",
2564+
"measurements": {
2565+
"ttfb": {"value": -100, "unit": "millisecond"}
2566+
}
2567+
}
2568+
"#;
2569+
let mut event = Annotated::<Event>::from_json(json).unwrap().0.unwrap();
2570+
2571+
// Allow ttfb as a builtinMeasurement with allow_negative defaulted to false.
2572+
let project_measurement_config: MeasurementsConfig = serde_json::from_value(json!({
2573+
"builtinMeasurements": [
2574+
{"name": "ttfb", "unit": "millisecond"},
2575+
],
2576+
}))
2577+
.unwrap();
2578+
2579+
let dynamic_measurement_config =
2580+
DynamicMeasurementsConfig::new(Some(&project_measurement_config), None);
2581+
2582+
normalize_event_measurements(&mut event, Some(dynamic_measurement_config), None);
2583+
2584+
insta::assert_ron_snapshot!(SerializableAnnotated(&Annotated::new(event)), {}, @r###"
2585+
{
2586+
"type": "transaction",
2587+
"timestamp": 1619420405.0,
2588+
"start_timestamp": 1619420400.0,
2589+
"measurements": {},
2590+
"_meta": {
2591+
"measurements": {
2592+
"": Meta(Some(MetaInner(
2593+
err: [
2594+
[
2595+
"invalid_data",
2596+
{
2597+
"reason": "Negative value for measurement ttfb not allowed: -100",
2598+
},
2599+
],
2600+
],
2601+
val: Some({
2602+
"ttfb": {
2603+
"unit": "millisecond",
2604+
"value": -100.0,
2605+
},
2606+
}),
2607+
))),
2608+
},
2609+
},
2610+
}
2611+
"###);
2612+
}
25462613
}

relay-event-normalization/src/normalize/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ mod contexts;
3636
pub struct BuiltinMeasurementKey {
3737
name: String,
3838
unit: MetricUnit,
39+
#[serde(skip_serializing_if = "is_false")]
40+
allow_negative: bool,
41+
}
42+
43+
fn is_false(b: &bool) -> bool {
44+
!b
3945
}
4046

4147
impl BuiltinMeasurementKey {
@@ -44,6 +50,7 @@ impl BuiltinMeasurementKey {
4450
Self {
4551
name: name.into(),
4652
unit,
53+
allow_negative: false,
4754
}
4855
}
4956

@@ -56,6 +63,11 @@ impl BuiltinMeasurementKey {
5663
pub fn unit(&self) -> &MetricUnit {
5764
&self.unit
5865
}
66+
67+
/// Return true if the built in measurement key allows negative values.
68+
pub fn allow_negative(&self) -> &bool {
69+
&self.allow_negative
70+
}
5971
}
6072

6173
/// Configuration for measurements normalization.

0 commit comments

Comments
 (0)