Skip to content

Commit 83f16fe

Browse files
authored
ref: Introduce Attributes struct (#4839)
This struct reduces the amount of ceremony required to insert an attribute into a collection or get an attribute's value. I only added the methods I needed, there's probably more that could be added. This PR also removes some redundant debug snapshots, or in some cases replaces them with JSON snapshots.
1 parent 19bbf1c commit 83f16fe

File tree

8 files changed

+306
-589
lines changed

8 files changed

+306
-589
lines changed

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

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
33
use chrono::{DateTime, Duration, Utc};
44
use relay_event_schema::protocol::{
5-
Attribute, AttributeValue, NetworkReportRaw, OurLog, OurLogLevel, Timestamp, TraceId,
5+
Attributes, NetworkReportRaw, OurLog, OurLogLevel, Timestamp, TraceId,
66
};
7-
use relay_protocol::{Annotated, Object};
7+
use relay_protocol::Annotated;
88

99
/// Creates a [`OurLog`] from the provided [`NetworkReportRaw`].
1010
pub fn create_log(nel: Annotated<NetworkReportRaw>, received_at: DateTime<Utc>) -> Option<OurLog> {
@@ -30,18 +30,12 @@ pub fn create_log(nel: Annotated<NetworkReportRaw>, received_at: DateTime<Utc>)
3030
.checked_sub_signed(Duration::milliseconds(*nel.age.value().unwrap_or(&0)))
3131
.unwrap_or(received_at);
3232

33-
let mut attributes: Object<Attribute> = Default::default();
33+
let mut attributes: Attributes = Default::default();
3434

3535
macro_rules! add_attribute {
3636
($name:literal, $value:expr) => {{
3737
if let Some(value) = $value.into_value() {
38-
attributes.insert(
39-
$name.to_owned(),
40-
Annotated::new(Attribute {
41-
value: AttributeValue::from(value),
42-
other: Default::default(),
43-
}),
44-
);
38+
attributes.insert($name.to_owned(), value);
4539
}
4640
}};
4741
}

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

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use relay_protocol::{Annotated, Empty, FromValue, IntoValue, Object, SkipSerialization, Value};
2-
use std::fmt;
2+
use std::{borrow::Borrow, fmt};
33

44
use crate::processor::ProcessValue;
55

@@ -159,3 +159,82 @@ impl IntoValue for AttributeType {
159159
serde::ser::Serialize::serialize(self.as_str(), s)
160160
}
161161
}
162+
163+
/// Wrapper struct around a collection of attributes with some convenience methods.
164+
#[derive(Debug, Clone, Default, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
165+
pub struct Attributes(Object<Attribute>);
166+
167+
impl Attributes {
168+
/// Creates an empty collection of attributes.
169+
pub fn new() -> Self {
170+
Self(Object::new())
171+
}
172+
173+
/// Returns the value of the attribute with the given key.
174+
pub fn get_value<Q>(&self, key: &Q) -> Option<&Value>
175+
where
176+
String: Borrow<Q>,
177+
Q: Ord + ?Sized,
178+
{
179+
self.get_attribute(key)?.value.value.value()
180+
}
181+
182+
/// Returns the attribute with the given key.
183+
pub fn get_attribute<Q>(&self, key: &Q) -> Option<&Attribute>
184+
where
185+
String: Borrow<Q>,
186+
Q: Ord + ?Sized,
187+
{
188+
self.0.get(key)?.value()
189+
}
190+
191+
/// Inserts an attribute with the given value into this collection.
192+
pub fn insert<V: Into<AttributeValue>>(&mut self, key: String, value: V) {
193+
fn inner(slf: &mut Attributes, key: String, value: AttributeValue) {
194+
let attribute = Annotated::new(Attribute {
195+
value,
196+
other: Default::default(),
197+
});
198+
slf.insert_raw(key, attribute);
199+
}
200+
let value = value.into();
201+
inner(self, key, value);
202+
}
203+
204+
/// Inserts an annotated attribute into this collection.
205+
pub fn insert_raw(&mut self, key: String, attribute: Annotated<Attribute>) {
206+
self.0.insert(key, attribute);
207+
}
208+
209+
/// Checks whether this collection contains an attribute with the given key.
210+
pub fn contains_key<Q>(&self, key: &Q) -> bool
211+
where
212+
String: Borrow<Q>,
213+
Q: Ord + ?Sized,
214+
{
215+
self.0.contains_key(key)
216+
}
217+
218+
/// Iterates mutably over this collection's attribute keys and values
219+
pub fn iter_mut(
220+
&mut self,
221+
) -> std::collections::btree_map::IterMut<String, Annotated<Attribute>> {
222+
self.0.iter_mut()
223+
}
224+
}
225+
226+
impl IntoIterator for Attributes {
227+
type Item = (String, Annotated<Attribute>);
228+
229+
type IntoIter = std::collections::btree_map::IntoIter<String, Annotated<Attribute>>;
230+
231+
fn into_iter(self) -> Self::IntoIter {
232+
self.0.into_iter()
233+
}
234+
}
235+
236+
impl FromIterator<(String, Annotated<Attribute>)> for Attributes {
237+
fn from_iter<T: IntoIterator<Item = (String, Annotated<Attribute>)>>(iter: T) -> Self {
238+
Self(Object::from_iter(iter))
239+
}
240+
}

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

Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::fmt::{self, Display};
44
use serde::{Serialize, Serializer};
55

66
use crate::processor::ProcessValue;
7-
use crate::protocol::{Attribute, SpanId, Timestamp, TraceId};
7+
use crate::protocol::{Attributes, SpanId, Timestamp, TraceId};
88

99
#[derive(Clone, Debug, Default, PartialEq, Empty, FromValue, IntoValue, ProcessValue)]
1010
#[metastructure(process_func = "process_ourlog", value_type = "OurLog")]
@@ -31,19 +31,13 @@ pub struct OurLog {
3131

3232
/// Arbitrary attributes on a log.
3333
#[metastructure(pii = "true", trim = false)]
34-
pub attributes: Annotated<Object<Attribute>>,
34+
pub attributes: Annotated<Attributes>,
3535

3636
/// Additional arbitrary fields for forwards compatibility.
3737
#[metastructure(additional_properties, retain = true, pii = "maybe")]
3838
pub other: Object<Value>,
3939
}
4040

41-
impl OurLog {
42-
pub fn attribute(&self, key: &str) -> Option<&Annotated<Value>> {
43-
Some(&self.attributes.value()?.get(key)?.value()?.value.value)
44-
}
45-
}
46-
4741
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
4842
pub enum OurLogLevel {
4943
Trace,
@@ -168,69 +162,6 @@ mod tests {
168162
}"#;
169163

170164
let data = Annotated::<OurLog>::from_json(json).unwrap();
171-
insta::assert_debug_snapshot!(data, @r###"
172-
OurLog {
173-
timestamp: Timestamp(
174-
2018-12-13T16:51:00Z,
175-
),
176-
trace_id: TraceId("5b8efff798038103d269b633813fc60c"),
177-
span_id: SpanId("eee19b7ec3c1b174"),
178-
level: Info,
179-
body: "Example log record",
180-
attributes: {
181-
"boolean.attribute": Attribute {
182-
value: Bool(
183-
true,
184-
),
185-
type: Boolean,
186-
other: {},
187-
},
188-
"double.attribute": Attribute {
189-
value: F64(
190-
1.23,
191-
),
192-
type: Double,
193-
other: {},
194-
},
195-
"sentry.observed_timestamp_nanos": Attribute {
196-
value: String(
197-
"1544712660300000000",
198-
),
199-
type: Integer,
200-
other: {},
201-
},
202-
"sentry.severity_number": Attribute {
203-
value: String(
204-
"10",
205-
),
206-
type: Integer,
207-
other: {},
208-
},
209-
"sentry.severity_text": Attribute {
210-
value: String(
211-
"info",
212-
),
213-
type: String,
214-
other: {},
215-
},
216-
"sentry.trace_flags": Attribute {
217-
value: String(
218-
"10",
219-
),
220-
type: Integer,
221-
other: {},
222-
},
223-
"string.attribute": Attribute {
224-
value: String(
225-
"some string",
226-
),
227-
type: String,
228-
other: {},
229-
},
230-
},
231-
other: {},
232-
}
233-
"###);
234165

235166
insta::assert_json_snapshot!(SerializableAnnotated(&data), @r###"
236167
{

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

Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ use std::str::FromStr;
66
use serde::Serialize;
77

88
use crate::processor::ProcessValue;
9-
use crate::protocol::{Attribute, SpanId, Timestamp, TraceId};
10-
11-
use super::OperationType;
9+
use crate::protocol::{Attributes, OperationType, SpanId, Timestamp, TraceId};
1210

1311
/// A version 2 (transactionless) span.
1412
#[derive(Clone, Debug, Default, PartialEq, Empty, FromValue, IntoValue)]
@@ -64,20 +62,13 @@ pub struct SpanV2 {
6462

6563
/// Arbitrary attributes on a span.
6664
#[metastructure(pii = "true", trim = false)]
67-
pub attributes: Annotated<Object<Attribute>>,
65+
pub attributes: Annotated<Attributes>,
6866

6967
/// Additional arbitrary fields for forwards compatibility.
7068
#[metastructure(additional_properties, pii = "maybe")]
7169
pub other: Object<Value>,
7270
}
7371

74-
impl SpanV2 {
75-
/// Returns the value of the attribute with the given name.
76-
pub fn attribute(&self, key: &str) -> Option<&Annotated<Value>> {
77-
Some(&self.attributes.value()?.get(key)?.value()?.value.value)
78-
}
79-
}
80-
8172
/// Status of a V2 span.
8273
///
8374
/// This is a subset of OTEL's statuses (unset, ok, error), plus
@@ -294,7 +285,7 @@ pub struct SpanV2Link {
294285

295286
/// Span link attributes, similar to span attributes/data.
296287
#[metastructure(pii = "maybe", trim = false)]
297-
pub attributes: Annotated<Object<Attribute>>,
288+
pub attributes: Annotated<Attributes>,
298289

299290
/// Additional arbitrary fields for forwards compatibility.
300291
#[metastructure(additional_properties, pii = "maybe", trim = false)]
@@ -308,20 +299,6 @@ mod tests {
308299

309300
use super::*;
310301

311-
macro_rules! attrs {
312-
($($name:expr => $val:expr , $ty:ident),* $(,)?) => {
313-
std::collections::BTreeMap::from([$((
314-
$name.to_owned(),
315-
relay_protocol::Annotated::new(
316-
$crate::protocol::Attribute::new(
317-
$crate::protocol::AttributeType::$ty,
318-
$val.into()
319-
)
320-
)
321-
),)*])
322-
};
323-
}
324-
325302
#[test]
326303
fn test_span_serialization() {
327304
let json = r#"{
@@ -394,27 +371,31 @@ mod tests {
394371
}
395372
}"#;
396373

397-
let attributes = attrs!(
398-
"custom.error_rate" => 0.5, Double,
399-
"custom.is_green" => true, Boolean,
400-
"sentry.release" => "1.0.0" , String,
401-
"sentry.environment" => "local", String,
402-
"sentry.platform" => "php", String,
403-
"sentry.sdk.name" => "sentry.php", String,
404-
"sentry.sdk.version" => "4.10.0", String,
405-
"sentry.transaction_info.source" => "url", String,
406-
"sentry.origin" => "manual", String,
407-
"server.address" => "DHWKN7KX6N.local", String,
408-
"http.response.status_code" => 200i64, Integer,
374+
let mut attributes = Attributes::new();
375+
376+
attributes.insert("custom.error_rate".to_owned(), 0.5);
377+
attributes.insert("custom.is_green".to_owned(), true);
378+
attributes.insert("sentry.release".to_owned(), "1.0.0".to_owned());
379+
attributes.insert("sentry.environment".to_owned(), "local".to_owned());
380+
attributes.insert("sentry.platform".to_owned(), "php".to_owned());
381+
attributes.insert("sentry.sdk.name".to_owned(), "sentry.php".to_owned());
382+
attributes.insert("sentry.sdk.version".to_owned(), "4.10.0".to_owned());
383+
attributes.insert(
384+
"sentry.transaction_info.source".to_owned(),
385+
"url".to_owned(),
409386
);
387+
attributes.insert("sentry.origin".to_owned(), "manual".to_owned());
388+
attributes.insert("server.address".to_owned(), "DHWKN7KX6N.local".to_owned());
389+
attributes.insert("http.response.status_code".to_owned(), 200i64);
390+
391+
let mut link_attributes = Attributes::new();
392+
link_attributes.insert("sentry.link.type".to_owned(), "previous_trace".to_owned());
410393

411394
let links = vec![Annotated::new(SpanV2Link {
412395
trace_id: Annotated::new("627a2885119dcc8184fae7eef09438cb".parse().unwrap()),
413396
span_id: Annotated::new("6c71fc6b09b8b716".parse().unwrap()),
414397
sampled: Annotated::new(true),
415-
attributes: Annotated::new(attrs!(
416-
"sentry.link.type" => "previous_trace", String
417-
)),
398+
attributes: Annotated::new(link_attributes),
418399
..Default::default()
419400
})];
420401
let span = Annotated::new(SpanV2 {

0 commit comments

Comments
 (0)