Skip to main content

relay_spans/
name.rs

1use relay_conventions::attributes::{SENTRY__DESCRIPTION, SENTRY__OP, SENTRY__ORIGIN};
2use relay_conventions::name::name_for_op_and_attributes;
3use relay_event_schema::protocol::Attributes;
4
5/// Constructs a name attribute for a V2 span, based on its attributes.
6///
7/// If the attributes contain [`SENTRY__ORIGIN`] with the value `"manual"`,
8/// the description (contained in [`SENTRY__DESCRIPTION`]) is used as the name.
9/// Otherwise, the name is constructed following the rules defined in sentry-conventions.
10///
11/// If no rule in `sentry-conventions` matches the span's [`SENTRY__OP`], the op is
12/// returned as the name.
13///
14/// Finally, if the span doesn't have an op, `None` is returned.
15pub fn name_for_attributes(attributes: &Attributes) -> Option<String> {
16    let origin = attributes
17        .get_value(SENTRY__ORIGIN)
18        .and_then(|o| o.as_str());
19    let description = attributes
20        .get_value(SENTRY__DESCRIPTION)
21        .and_then(|d| d.as_str());
22
23    if let Some(description) = description
24        && origin == Some("manual")
25    {
26        return Some(description.to_owned());
27    }
28
29    let op = attributes.get_value(SENTRY__OP)?.as_str()?;
30    Some(name_for_op_and_attributes(op, |name| attributes.get_value(name)).unwrap_or(op.to_owned()))
31}
32
33#[cfg(test)]
34mod tests {
35    use relay_protocol::Annotated;
36
37    use super::*;
38
39    #[test]
40    fn test_attributes_falls_back_to_op_when_no_templates_defined() {
41        let attributes = Attributes::from([(
42            "sentry.op".to_owned(),
43            Annotated::new("foo".to_owned().into()),
44        )]);
45
46        assert_eq!(name_for_attributes(&attributes), Some("foo".to_owned()));
47    }
48
49    #[test]
50    fn test_attributes_uses_the_first_matching_template() {
51        let attributes = Attributes::from([
52            (
53                "sentry.op".to_owned(),
54                Annotated::new("db".to_owned().into()),
55            ),
56            (
57                "db.query.summary".to_owned(),
58                Annotated::new("SELECT users".to_owned().into()),
59            ),
60            (
61                "db.operation.name".to_owned(),
62                Annotated::new("INSERT".to_owned().into()),
63            ),
64            (
65                "db.collection.name".to_owned(),
66                Annotated::new("widgets".to_owned().into()),
67            ),
68        ]);
69
70        assert_eq!(
71            name_for_attributes(&attributes),
72            Some("SELECT users".to_owned())
73        );
74    }
75
76    #[test]
77    fn test_attributes_uses_fallback_templates_when_data_is_missing() {
78        let attributes = Attributes::from([
79            (
80                "sentry.op".to_owned(),
81                Annotated::new("db".to_owned().into()),
82            ),
83            (
84                "db.operation.name".to_owned(),
85                Annotated::new("INSERT".to_owned().into()),
86            ),
87            (
88                "db.collection.name".to_owned(),
89                Annotated::new("widgets".to_owned().into()),
90            ),
91        ]);
92
93        assert_eq!(
94            name_for_attributes(&attributes),
95            Some("INSERT widgets".to_owned())
96        );
97    }
98
99    #[test]
100    fn test_attributes_falls_back_to_hardcoded_name_when_nothing_matches() {
101        let attributes = Attributes::from([(
102            "sentry.op".to_owned(),
103            Annotated::new("db".to_owned().into()),
104        )]);
105
106        assert_eq!(
107            name_for_attributes(&attributes),
108            Some("Database operation".to_owned())
109        );
110    }
111
112    #[test]
113    fn test_manual_spans_use_description_v2() {
114        let attributes = Attributes::from([
115            (
116                "sentry.origin".to_owned(),
117                Annotated::new("manual".to_owned().into()),
118            ),
119            (
120                "sentry.description".to_owned(),
121                Annotated::new("Custom name".to_owned().into()),
122            ),
123            (
124                "sentry.op".to_owned(),
125                Annotated::new("db".to_owned().into()),
126            ),
127            (
128                "db.query.summary".to_owned(),
129                Annotated::new("SELECT users".to_owned().into()),
130            ),
131            (
132                "db.operation.name".to_owned(),
133                Annotated::new("INSERT".to_owned().into()),
134            ),
135            (
136                "db.collection.name".to_owned(),
137                Annotated::new("widgets".to_owned().into()),
138            ),
139        ]);
140
141        assert_eq!(
142            name_for_attributes(&attributes),
143            Some("Custom name".to_owned())
144        );
145    }
146}