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(spans): Do not trim essential fields #3670

Merged
merged 12 commits into from
Jun 4, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Apply globally defined metric tags to legacy transaction metrics. ([#3615](https://github.com/getsentry/relay/pull/3615))
- Limit the maximum size of spans in an transaction to 800 kib. ([#3645](https://github.com/getsentry/relay/pull/3645))
- Scrub identifiers in spans with `op:db` and `db_system:redis`. ([#3642](https://github.com/getsentry/relay/pull/3642))
- Stop trimming important span fields by marking them `trim = "false"`. ([#3670](https://github.com/getsentry/relay/pull/3670))

**Features**:

Expand Down
21 changes: 21 additions & 0 deletions relay-event-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ struct FieldAttrs {
max_chars_allowance: Option<TokenStream>,
max_depth: Option<TokenStream>,
max_bytes: Option<TokenStream>,
trim: Option<bool>,
}

impl FieldAttrs {
Expand Down Expand Up @@ -366,6 +367,14 @@ impl FieldAttrs {
quote!(crate::processor::Pii::False)
};

let trim = if let Some(trim) = self.trim {
quote!(#trim)
} else if let Some(ref parent_attrs) = inherit_from_field_attrs {
quote!(#parent_attrs.trim)
} else {
quote!(true)
};

let retain = self.retain;

let max_chars = if let Some(ref max_chars) = self.max_chars {
Expand Down Expand Up @@ -421,6 +430,7 @@ impl FieldAttrs {
max_bytes: #max_bytes,
pii: #pii,
retain: #retain,
trim: #trim,
}
})
}
Expand Down Expand Up @@ -595,6 +605,17 @@ fn parse_field_attributes(
panic!("Got non string literal for retain");
}
}
} else if ident == "trim" {
match name_value.lit {
Lit::Str(litstr) => match litstr.value().as_str() {
Dav1dde marked this conversation as resolved.
Show resolved Hide resolved
"true" => rv.trim = None,
"false" => rv.trim = Some(false),
other => panic!("Unknown value {other}"),
},
_ => {
panic!("Got non string literal for retain");
Dav1dde marked this conversation as resolved.
Show resolved Hide resolved
}
}
} else if ident == "legacy_alias" || ident == "skip_serialization" {
// Skip
} else {
Expand Down
103 changes: 91 additions & 12 deletions relay-event-normalization/src/trimming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,16 @@ impl Processor for TrimmingProcessor {
});
}

if self.remaining_size() == Some(0) {
// TODO: Create remarks (ensure they do not bloat event)
return Err(ProcessingAction::DeleteValueHard);
}
if self.remaining_depth(state) == Some(0) {
// TODO: Create remarks (ensure they do not bloat event)
return Err(ProcessingAction::DeleteValueHard);
if state.attrs().trim {
if self.remaining_size() == Some(0) {
// TODO: Create remarks (ensure they do not bloat event)
return Err(ProcessingAction::DeleteValueHard);
}
if self.remaining_depth(state) == Some(0) {
// TODO: Create remarks (ensure they do not bloat event)
return Err(ProcessingAction::DeleteValueHard);
}
}

Ok(())
}

Expand Down Expand Up @@ -127,6 +128,9 @@ impl Processor for TrimmingProcessor {
meta: &mut Meta,
state: &ProcessingState<'_>,
) -> ProcessingResult {
if !state.attrs().trim {
return Ok(());
}
if let Some(max_chars) = state.attrs().max_chars {
trim_string(value, meta, max_chars, state.attrs().max_chars_allowance);
}
Expand All @@ -149,6 +153,10 @@ impl Processor for TrimmingProcessor {
where
T: ProcessValue,
{
if !state.attrs().trim {
return Ok(());
}

// If we need to check the bag size, then we go down a different path
if !self.size_state.is_empty() {
let original_length = value.len();
Expand All @@ -159,7 +167,7 @@ impl Processor for TrimmingProcessor {

let mut split_index = None;
for (index, item) in value.iter_mut().enumerate() {
if self.remaining_size().unwrap() == 0 {
if self.remaining_size() == Some(0) {
split_index = Some(index);
break;
}
Expand Down Expand Up @@ -191,6 +199,10 @@ impl Processor for TrimmingProcessor {
where
T: ProcessValue,
{
if !state.attrs().trim {
return Ok(());
}

// If we need to check the bag size, then we go down a different path
if !self.size_state.is_empty() {
let original_length = value.len();
Expand All @@ -201,7 +213,7 @@ impl Processor for TrimmingProcessor {

let mut split_key = None;
for (key, item) in value.iter_mut() {
if self.remaining_size().unwrap() == 0 {
if self.remaining_size() == Some(0) {
split_key = Some(key.to_owned());
break;
}
Expand Down Expand Up @@ -230,6 +242,10 @@ impl Processor for TrimmingProcessor {
_meta: &mut Meta,
state: &ProcessingState<'_>,
) -> ProcessingResult {
if !state.attrs().trim {
return Ok(());
}

match value {
Value::Array(_) | Value::Object(_) => {
if self.remaining_depth(state) == Some(1) {
Expand All @@ -252,6 +268,10 @@ impl Processor for TrimmingProcessor {
_meta: &mut Meta,
state: &ProcessingState<'_>,
) -> ProcessingResult {
if !state.attrs().trim {
return Ok(());
}

processor::apply(&mut stacktrace.frames, |frames, meta| {
enforce_frame_hard_limit(frames, meta, 250);
Ok(())
Expand Down Expand Up @@ -393,9 +413,10 @@ mod tests {
use std::iter::repeat;

use relay_event_schema::protocol::{
Breadcrumb, Context, Contexts, Event, Exception, ExtraValue, Span, TagEntry, Tags, Values,
Breadcrumb, Context, Contexts, Event, Exception, ExtraValue, Span, SpanId, TagEntry, Tags,
TraceId, Values,
};
use relay_protocol::{Map, Remark, SerializableAnnotated};
use relay_protocol::{get_value, Map, Remark, SerializableAnnotated};
use similar_asserts::assert_eq;

use crate::MaxChars;
Expand Down Expand Up @@ -930,4 +951,62 @@ mod tests {

assert_eq!(event.0.unwrap().spans.0.unwrap().len(), 8);
}

#[test]
fn test_too_many_spans_trimmed_trace_id() {
let original_description = "a".repeat(819163);
let original_trace_id = TraceId("b".repeat(48));
let span = Span {
description: original_description.clone().into(),
trace_id: original_trace_id.clone().into(),
..Default::default()
};
let mut event = Annotated::new(Event {
spans: Annotated::new(vec![span.into()]),
..Default::default()
});

let mut processor = TrimmingProcessor::new();
processor::process_value(&mut event, &mut processor, ProcessingState::root()).unwrap();

assert_eq!(
get_value!(event.spans[0].description!),
&original_description
);
// Trace ID would be trimmed without `trim = "false"`
assert_eq!(get_value!(event.spans[0].trace_id!), &original_trace_id);
}

#[test]
fn test_too_many_spans_trimmed_trace_id_drop() {
let original_description = "a".repeat(819191);
let original_span_id = SpanId("b".repeat(48));
let original_trace_id = TraceId("c".repeat(48));
let original_segment_id = SpanId("d".repeat(48));
let span = Span {
description: original_description.clone().into(),
span_id: original_span_id.clone().into(),
trace_id: original_trace_id.clone().into(),
segment_id: original_segment_id.clone().into(),
is_segment: false.into(),
..Default::default()
};
let mut event = Annotated::new(Event {
spans: Annotated::new(vec![span.into()]),
..Default::default()
});

let mut processor = TrimmingProcessor::new();
processor::process_value(&mut event, &mut processor, ProcessingState::root()).unwrap();

assert_eq!(
get_value!(event.spans[0].description!),
&original_description
);
// These fields would be dropped without `trim = "false"`
assert_eq!(get_value!(event.spans[0].span_id!), &original_span_id);
assert_eq!(get_value!(event.spans[0].trace_id!), &original_trace_id);
assert_eq!(get_value!(event.spans[0].segment_id!), &original_segment_id);
assert_eq!(get_value!(event.spans[0].is_segment!), &false);
}
}
3 changes: 3 additions & 0 deletions relay-event-schema/src/processor/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ pub struct FieldAttrs {
pub pii: Pii,
/// Whether additional properties should be retained during normalization.
pub retain: bool,
/// Whether the trimming processor is allowed to shorten or drop this field.
pub trim: bool,
}

/// A set of characters allowed or denied for a (string) field.
Expand Down Expand Up @@ -167,6 +169,7 @@ impl FieldAttrs {
max_bytes: None,
pii: Pii::False,
retain: false,
trim: true,
}
}

Expand Down
7 changes: 5 additions & 2 deletions relay-event-schema/src/protocol/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,26 @@ pub struct Span {
pub op: Annotated<OperationType>,

/// The Span id.
#[metastructure(required = "true")]
#[metastructure(required = "true", trim = "false")]
pub span_id: Annotated<SpanId>,

/// The ID of the span enclosing this span.
#[metastructure(trim = "false")]
pub parent_span_id: Annotated<SpanId>,

/// The ID of the trace the span belongs to.
#[metastructure(required = "true")]
#[metastructure(required = "true", trim = "false")]
pub trace_id: Annotated<TraceId>,

/// A unique identifier for a segment within a trace (8 byte hexadecimal string).
///
/// For spans embedded in transactions, the `segment_id` is the `span_id` of the containing
/// transaction.
#[metastructure(trim = "false")]
pub segment_id: Annotated<SpanId>,

/// Whether or not the current span is the root of the segment.
#[metastructure(trim = "false")]
pub is_segment: Annotated<bool>,

/// The status of a span.
Expand Down
Loading