Skip to content

Commit 6208719

Browse files
authored
fix(spans): Prevent trimming of sentry tags and span data (#4557)
Small regression when typing out `SentryTags`, the new structure does not inherit the trim flag. Now trim can be applied to the container and is inherited for all fields automatically. This fixes all cases of partial queryable tags in the span explorer.
1 parent 0ea0807 commit 6208719

File tree

4 files changed

+47
-33
lines changed

4 files changed

+47
-33
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
- Update Apple device model classes ([#4529](https://github.com/getsentry/relay/pull/4529))
1313
- Remove separate quota and rate limit counting for replay videos ([#4554](https://github.com/getsentry/relay/pull/4554))
1414

15+
**Bug Fixes**:
16+
17+
- Prevent partial trims in indexed and queryable span data. ([#4557](https://github.com/getsentry/relay/pull/4557))
18+
1519
**Internal**:
1620

1721
- Track an utilization metric for internal services. ([#4501](https://github.com/getsentry/relay/pull/4501))

relay-event-derive/src/lib.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ fn derive_process_value(mut s: synstructure::Structure<'_>) -> syn::Result<Token
4444
let bi = &variant.bindings()[0];
4545
let ident = &bi.binding;
4646
let field_attrs = parse_field_attributes(0, bi.ast(), &mut true)?;
47-
let field_attrs_tokens = field_attrs.as_tokens(Some(quote!(parent_attrs)));
47+
let field_attrs_tokens = field_attrs.as_tokens(&type_attrs, Some(quote!(parent_attrs)));
4848

4949
Ok(quote! {
5050
let parent_attrs = __state.attrs();
@@ -99,7 +99,7 @@ fn derive_process_value(mut s: synstructure::Structure<'_>) -> syn::Result<Token
9999
let field_attrs_name = Ident::new(&format!("FIELD_ATTRS_{index}"), Span::call_site());
100100
let field_name = field_attrs.field_name.clone();
101101

102-
let field_attrs_tokens = field_attrs.as_tokens(None);
102+
let field_attrs_tokens = field_attrs.as_tokens(&type_attrs, None);
103103

104104
(quote! {
105105
static #field_attrs_name: crate::processor::FieldAttrs = #field_attrs_tokens;
@@ -223,6 +223,11 @@ fn derive_process_value(mut s: synstructure::Structure<'_>) -> syn::Result<Token
223223
struct TypeAttrs {
224224
process_func: Option<String>,
225225
value_type: Vec<String>,
226+
/// The default trim value for the container.
227+
///
228+
/// If `trim` is specified on the container all fields of the container,
229+
/// will default to this value for `trim`.
230+
trim: Option<bool>,
226231
}
227232

228233
impl TypeAttrs {
@@ -257,6 +262,9 @@ fn parse_type_attributes(s: &synstructure::Structure<'_>) -> syn::Result<TypeAtt
257262
} else if ident == "value_type" {
258263
let s = meta.value()?.parse::<LitStr>()?;
259264
rv.value_type.push(s.value());
265+
} else if ident == "trim" {
266+
let s = meta.value()?.parse::<LitBool>()?;
267+
rv.trim = Some(s.value());
260268
} else {
261269
// Ignore other attributes used by `relay-protocol-derive`.
262270
if !meta.input.peek(syn::Token![,]) {
@@ -307,7 +315,11 @@ struct FieldAttrs {
307315
}
308316

309317
impl FieldAttrs {
310-
fn as_tokens(&self, inherit_from_field_attrs: Option<TokenStream>) -> TokenStream {
318+
fn as_tokens(
319+
&self,
320+
type_attrs: &TypeAttrs,
321+
inherit_from_field_attrs: Option<TokenStream>,
322+
) -> TokenStream {
311323
let field_name = &self.field_name;
312324

313325
if self.required.is_none() && self.nonempty.is_some() {
@@ -347,7 +359,7 @@ impl FieldAttrs {
347359
quote!(crate::processor::Pii::False)
348360
};
349361

350-
let trim = if let Some(trim) = self.trim {
362+
let trim = if let Some(trim) = self.trim.or(type_attrs.trim) {
351363
quote!(#trim)
352364
} else if let Some(ref parent_attrs) = inherit_from_field_attrs {
353365
quote!(#parent_attrs.trim)

relay-event-normalization/src/trimming.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -438,8 +438,8 @@ mod tests {
438438

439439
use chrono::DateTime;
440440
use relay_event_schema::protocol::{
441-
Breadcrumb, Context, Contexts, Event, Exception, ExtraValue, Span, SpanId, TagEntry, Tags,
442-
Timestamp, TraceId, Values,
441+
Breadcrumb, Context, Contexts, Event, Exception, ExtraValue, SentryTags, Span, SpanId,
442+
TagEntry, Tags, Timestamp, TraceId, Values,
443443
};
444444
use relay_protocol::{get_value, Map, Remark, SerializableAnnotated};
445445
use similar_asserts::assert_eq;
@@ -1004,7 +1004,11 @@ mod tests {
10041004
#[test]
10051005
fn test_too_many_spans_trimmed() {
10061006
let span = Span {
1007-
platform: Annotated::new("a".repeat(1024 * 100)),
1007+
platform: Annotated::new("a".repeat(1024 * 90)),
1008+
sentry_tags: Annotated::new(SentryTags {
1009+
release: Annotated::new("b".repeat(1024 * 100)),
1010+
..Default::default()
1011+
}),
10081012
..Default::default()
10091013
};
10101014
let spans: Vec<_> = std::iter::repeat_with(|| Annotated::new(span.clone()))
@@ -1020,10 +1024,10 @@ mod tests {
10201024
processor::process_value(&mut event, &mut processor, ProcessingState::root()).unwrap();
10211025

10221026
let trimmed_spans = event.0.unwrap().spans.0.unwrap();
1023-
assert_eq!(trimmed_spans.len(), 8);
1027+
assert_eq!(trimmed_spans.len(), 5);
10241028

10251029
// The actual spans were not touched:
1026-
assert_eq!(trimmed_spans.as_slice(), &spans[0..8]);
1030+
assert_eq!(trimmed_spans.as_slice(), &spans[0..5]);
10271031
}
10281032

10291033
#[test]

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

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,46 +11,42 @@ use crate::protocol::{
1111
};
1212

1313
#[derive(Clone, Debug, Default, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
14-
#[metastructure(process_func = "process_span", value_type = "Span")]
14+
#[metastructure(process_func = "process_span", value_type = "Span", trim = false)]
1515
pub struct Span {
1616
/// Timestamp when the span was ended.
17-
#[metastructure(required = true, trim = false)]
17+
#[metastructure(required = true)]
1818
pub timestamp: Annotated<Timestamp>,
1919

2020
/// Timestamp when the span started.
21-
#[metastructure(required = true, trim = false)]
21+
#[metastructure(required = true)]
2222
pub start_timestamp: Annotated<Timestamp>,
2323

2424
/// The amount of time in milliseconds spent in this span,
2525
/// excluding its immediate child spans.
26-
#[metastructure(trim = false)]
2726
pub exclusive_time: Annotated<f64>,
2827

2928
/// Span type (see `OperationType` docs).
30-
#[metastructure(max_chars = 128, trim = false)]
29+
#[metastructure(max_chars = 128)]
3130
pub op: Annotated<OperationType>,
3231

3332
/// The Span id.
34-
#[metastructure(required = true, trim = false)]
33+
#[metastructure(required = true)]
3534
pub span_id: Annotated<SpanId>,
3635

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

4139
/// The ID of the trace the span belongs to.
42-
#[metastructure(required = true, trim = false)]
40+
#[metastructure(required = true)]
4341
pub trace_id: Annotated<TraceId>,
4442

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

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

5652
/// Indicates whether a span's parent is remote.
@@ -62,66 +58,61 @@ pub struct Span {
6258
/// - `empty`: unknown
6359
/// - `false`: is not remote
6460
/// - `true`: is remote
65-
#[metastructure(trim = false)]
6661
pub is_remote: Annotated<bool>,
6762

6863
/// The status of a span.
69-
#[metastructure(trim = false)]
7064
pub status: Annotated<SpanStatus>,
7165

7266
/// Human readable description of a span (e.g. method URL).
73-
#[metastructure(pii = "maybe", trim = false)]
67+
#[metastructure(pii = "maybe")]
7468
pub description: Annotated<String>,
7569

7670
/// Arbitrary tags on a span, like on the top-level event.
77-
#[metastructure(pii = "maybe", trim = false)]
71+
#[metastructure(pii = "maybe")]
7872
pub tags: Annotated<Object<JsonLenientString>>,
7973

8074
/// The origin of the span indicates what created the span (see [OriginType] docs).
81-
#[metastructure(max_chars = 128, allow_chars = "a-zA-Z0-9_.", trim = false)]
75+
#[metastructure(max_chars = 128, allow_chars = "a-zA-Z0-9_.")]
8276
pub origin: Annotated<OriginType>,
8377

8478
/// ID of a profile that can be associated with the span.
85-
#[metastructure(trim = false)]
8679
pub profile_id: Annotated<EventId>,
8780

8881
/// Arbitrary additional data on a span.
8982
///
9083
/// Besides arbitrary user data, this object also contains SDK-provided fields used by the
9184
/// product (see <https://develop.sentry.dev/sdk/performance/span-data-conventions/>).
92-
#[metastructure(pii = "true", trim = false)]
85+
#[metastructure(pii = "true")]
9386
pub data: Annotated<SpanData>,
9487

9588
/// Links from this span to other spans
96-
#[metastructure(pii = "maybe", trim = false)]
89+
#[metastructure(pii = "maybe")]
9790
pub links: Annotated<Array<SpanLink>>,
9891

9992
/// Tags generated by Relay. These tags are a superset of the tags set on span metrics.
100-
#[metastructure(trim = false)]
10193
pub sentry_tags: Annotated<SentryTags>,
10294

10395
/// Timestamp when the span has been received by Sentry.
104-
#[metastructure(trim = false)]
10596
pub received: Annotated<Timestamp>,
10697

10798
/// Measurements which holds observed values such as web vitals.
108-
#[metastructure(skip_serialization = "empty", trim = false)]
99+
#[metastructure(skip_serialization = "empty")]
109100
#[metastructure(omit_from_schema)] // we only document error events for now
110101
pub measurements: Annotated<Measurements>,
111102

112103
/// Platform identifier.
113104
///
114105
/// See [`Event::platform`](`crate::protocol::Event::platform`).
115-
#[metastructure(skip_serialization = "empty", trim = false)]
106+
#[metastructure(skip_serialization = "empty")]
116107
pub platform: Annotated<String>,
117108

118109
/// Whether the span is a segment span that was converted from a transaction.
119-
#[metastructure(skip_serialization = "empty", trim = false)]
110+
#[metastructure(skip_serialization = "empty")]
120111
pub was_transaction: Annotated<bool>,
121112

122113
// TODO remove retain when the api stabilizes
123114
/// Additional arbitrary fields for forwards compatibility.
124-
#[metastructure(additional_properties, retain = true, pii = "maybe", trim = false)]
115+
#[metastructure(additional_properties, retain = true, pii = "maybe")]
125116
pub other: Object<Value>,
126117
}
127118

@@ -197,6 +188,7 @@ impl Getter for Span {
197188

198189
/// Indexable fields added by sentry (server-side).
199190
#[derive(Clone, Debug, Default, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
191+
#[metastructure(trim = false)]
200192
pub struct SentryTags {
201193
pub release: Annotated<String>,
202194
#[metastructure(pii = "true")]
@@ -351,6 +343,7 @@ impl Getter for SentryTags {
351343
/// Besides arbitrary user data, this type also contains SDK-provided fields used by the
352344
/// product (see <https://develop.sentry.dev/sdk/performance/span-data-conventions/>).
353345
#[derive(Clone, Debug, Default, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
346+
#[metastructure(trim = false)]
354347
pub struct SpanData {
355348
/// Mobile app start variant.
356349
///
@@ -750,6 +743,7 @@ impl Getter for SpanData {
750743

751744
/// A link from a span to another span.
752745
#[derive(Clone, Debug, Default, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
746+
#[metastructure(trim = false)]
753747
pub struct SpanLink {
754748
/// The trace id of the linked span
755749
#[metastructure(required = true, trim = false)]

0 commit comments

Comments
 (0)