relay_metrics/
utils.rs

1use core::fmt;
2use std::collections::BTreeMap;
3use std::ops::{AddAssign, SubAssign};
4
5use relay_base_schema::metrics::MetricNamespace;
6
7/// Estimates the number of bytes needed to encode the tags.
8///
9/// Note that this does not necessarily match the exact memory footprint of the tags,
10/// because data structures or their serialization have overheads.
11pub fn tags_cost(tags: &BTreeMap<String, String>) -> usize {
12    tags.iter().map(|(k, v)| k.len() + v.len()).sum()
13}
14
15/// Utility to store information for each [`MetricNamespace`].
16#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct ByNamespace<T> {
18    /// Value for the [`MetricNamespace::Sessions`] namespace.
19    pub sessions: T,
20    /// Value for the [`MetricNamespace::Transactions`] namespace.
21    pub transactions: T,
22    /// Value for the [`MetricNamespace::Spans`] namespace.
23    pub spans: T,
24    /// Value for the [`MetricNamespace::Custom`] namespace.
25    pub custom: T,
26    /// Value for the [`MetricNamespace::Unsupported`] namespace.
27    pub unsupported: T,
28}
29
30impl<T> ByNamespace<T> {
31    /// Returns a reference for the value stored for `namespace`.
32    pub fn get(&self, namespace: MetricNamespace) -> &T {
33        match namespace {
34            MetricNamespace::Sessions => &self.sessions,
35            MetricNamespace::Transactions => &self.transactions,
36            MetricNamespace::Spans => &self.spans,
37            MetricNamespace::Custom => &self.custom,
38            MetricNamespace::Unsupported => &self.unsupported,
39        }
40    }
41
42    /// Returns a mutable reference for the value stored for `namespace`.
43    pub fn get_mut(&mut self, namespace: MetricNamespace) -> &mut T {
44        match namespace {
45            MetricNamespace::Sessions => &mut self.sessions,
46            MetricNamespace::Transactions => &mut self.transactions,
47            MetricNamespace::Spans => &mut self.spans,
48            MetricNamespace::Custom => &mut self.custom,
49            MetricNamespace::Unsupported => &mut self.unsupported,
50        }
51    }
52}
53
54impl<T> IntoIterator for ByNamespace<T> {
55    type Item = (MetricNamespace, T);
56    type IntoIter = std::array::IntoIter<(MetricNamespace, T), 5>;
57
58    fn into_iter(self) -> Self::IntoIter {
59        let Self {
60            sessions,
61            transactions,
62            spans,
63            custom,
64            unsupported,
65        } = self;
66
67        [
68            (MetricNamespace::Sessions, sessions),
69            (MetricNamespace::Transactions, transactions),
70            (MetricNamespace::Spans, spans),
71            (MetricNamespace::Custom, custom),
72            (MetricNamespace::Unsupported, unsupported),
73        ]
74        .into_iter()
75    }
76}
77
78impl<T: fmt::Debug + Default + PartialEq> fmt::Debug for ByNamespace<T> {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        // A more compact representation. Mainly for snapshot testing.
81        write!(f, "(")?;
82
83        let mut values = MetricNamespace::all()
84            .into_iter()
85            .map(|ns| (ns, self.get(ns)))
86            .filter(|(_, v)| v != &&T::default())
87            .enumerate()
88            .peekable();
89
90        match values.peek() {
91            None => write!(f, "{:?}", T::default())?,
92            Some(_) => {
93                for (i, (namespace, value)) in values {
94                    if i > 0 {
95                        write!(f, ", ")?;
96                    }
97                    write!(f, "{namespace}:{value:?}")?;
98                }
99            }
100        }
101
102        write!(f, ")")
103    }
104}
105
106macro_rules! impl_op {
107    ($op:tt, $opfn:ident) => {
108        impl<T: $op> $op for ByNamespace<T> {
109            fn $opfn(&mut self, rhs: Self) {
110                let Self {
111                    sessions,
112                    transactions,
113                    spans,
114                    custom,
115                    unsupported,
116                } = self;
117
118                $op::$opfn(sessions, rhs.sessions);
119                $op::$opfn(transactions, rhs.transactions);
120                $op::$opfn(spans, rhs.spans);
121                $op::$opfn(custom, rhs.custom);
122                $op::$opfn(unsupported, rhs.unsupported);
123            }
124        }
125    };
126}
127
128impl_op!(AddAssign, add_assign);
129impl_op!(SubAssign, sub_assign);
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn test_get() {
137        let mut v = ByNamespace::<usize>::default();
138
139        for (i, namespace) in MetricNamespace::all().into_iter().enumerate() {
140            assert_eq!(*v.get(namespace), 0);
141            assert_eq!(*v.get_mut(namespace), 0);
142            *v.get_mut(namespace) += i;
143            assert_eq!(*v.get(namespace), i);
144        }
145
146        // Make sure a getter does not override another value by iterating over all values again.
147        for (i, namespace) in MetricNamespace::all().into_iter().enumerate() {
148            assert_eq!(*v.get(namespace), i);
149            assert_eq!(*v.get_mut(namespace), i);
150        }
151    }
152
153    #[test]
154    fn test_add_sub() {
155        let mut v = ByNamespace::<usize>::default();
156        for (i, namespace) in MetricNamespace::all().into_iter().enumerate() {
157            *v.get_mut(namespace) += i;
158        }
159
160        let mut v2 = v;
161        v2 -= v;
162        assert_eq!(v2, Default::default());
163
164        v2 += v;
165        assert_eq!(v2, v);
166    }
167}