Skip to content
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

fix(webvitals): Drop negative web vitals from measurements #2982

Merged
merged 6 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Default missing `Event.platform` and `Event.level` fields during light normalization. ([#2961](https://github.com/getsentry/relay/pull/2961))
- Copy event measurements to span & normalize span measurements. ([#2953](https://github.com/getsentry/relay/pull/2953))
- Add possiblity to block metrics with glob-patterns. ([#2954](https://github.com/getsentry/relay/pull/2954))
- Add `allow_negative` to `BuiltinMeasurementKey`. Filter out negative BuiltinMeasurements if `allow_negative` is false. ([#2982](https://github.com/getsentry/relay/pull/2982))

**Bug Fixes**:

Expand Down
4 changes: 4 additions & 0 deletions py/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- Add `allow_negative` to `BuiltinMeasurementKey`. Filter out negative BuiltinMeasurements if `allow_negative` is false. ([#2982](https://github.com/getsentry/relay/pull/2982))

## 0.8.44

### Various fixes & improvements
Expand Down
73 changes: 70 additions & 3 deletions relay-event-normalization/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,16 +725,16 @@ pub fn normalize_measurements(
) {
normalize_mobile_measurements(measurements);
normalize_units(measurements);
if let Some(measurements_config) = measurements_config {
remove_invalid_measurements(measurements, meta, measurements_config, max_mri_len);
}

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

compute_measurements(duration_millis, measurements);
if let Some(measurements_config) = measurements_config {
remove_invalid_measurements(measurements, meta, measurements_config, max_mri_len);
}
}

/// Computes performance score measurements.
Expand Down Expand Up @@ -1026,6 +1026,16 @@ fn remove_invalid_measurements(
// Check if this is a builtin measurement:
for builtin_measurement in measurements_config.builtin_measurement_keys() {
if builtin_measurement.name() == name {
let value = measurement.value.value().unwrap_or(&0.0);
// Drop negative values if the builtin measurement does not allow them.
if !builtin_measurement.allow_negative() && *value < 0.0 {
meta.add_error(Error::invalid(format!(
"Negative value for measurement {name} not allowed: {value}",
)));
removed_measurements
.insert(name.clone(), Annotated::new(std::mem::take(measurement)));
return false;
}
// If the unit matches a built-in measurement, we allow it.
// If the name matches but the unit is wrong, we do not even accept it as a custom measurement,
// and just drop it instead.
Expand Down Expand Up @@ -2575,4 +2585,61 @@ mod tests {
"invalid transaction event: timestamp is too stale"
);
}

#[test]
fn test_filter_negative_web_vital_measurements() {
let json = r#"
{
"type": "transaction",
"timestamp": "2021-04-26T08:00:05+0100",
"start_timestamp": "2021-04-26T08:00:00+0100",
"measurements": {
"ttfb": {"value": -100, "unit": "millisecond"}
}
}
"#;
let mut event = Annotated::<Event>::from_json(json).unwrap().0.unwrap();

// Allow ttfb as a builtinMeasurement with allow_negative defaulted to false.
let project_measurement_config: MeasurementsConfig = serde_json::from_value(json!({
"builtinMeasurements": [
{"name": "ttfb", "unit": "millisecond"},
],
}))
.unwrap();

let dynamic_measurement_config =
DynamicMeasurementsConfig::new(Some(&project_measurement_config), None);

normalize_event_measurements(&mut event, Some(dynamic_measurement_config), None);

insta::assert_ron_snapshot!(SerializableAnnotated(&Annotated::new(event)), {}, @r###"
{
"type": "transaction",
"timestamp": 1619420405.0,
"start_timestamp": 1619420400.0,
"measurements": {},
"_meta": {
"measurements": {
"": Meta(Some(MetaInner(
err: [
[
"invalid_data",
{
"reason": "Negative value for measurement ttfb not allowed: -100",
},
],
],
val: Some({
"ttfb": {
"unit": "millisecond",
"value": -100.0,
},
}),
))),
},
},
}
"###);
}
}
12 changes: 12 additions & 0 deletions relay-event-normalization/src/normalize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ mod contexts;
pub struct BuiltinMeasurementKey {
name: String,
unit: MetricUnit,
#[serde(default, skip_serializing_if = "is_false")]
allow_negative: bool,
}

fn is_false(b: &bool) -> bool {
!b
}

impl BuiltinMeasurementKey {
Expand All @@ -44,6 +50,7 @@ impl BuiltinMeasurementKey {
Self {
name: name.into(),
unit,
allow_negative: false,
}
}

Expand All @@ -56,6 +63,11 @@ impl BuiltinMeasurementKey {
pub fn unit(&self) -> &MetricUnit {
&self.unit
}

/// Return true if the built in measurement key allows negative values.
pub fn allow_negative(&self) -> &bool {
&self.allow_negative
}
}

/// Configuration for measurements normalization.
Expand Down