relay_config/
aggregator.rs1use relay_metrics::aggregator::AggregatorConfig;
4use relay_metrics::{MetricNamespace, UnixTimestamp};
5use serde::{Deserialize, Serialize};
6
7#[derive(Clone, Debug, Deserialize, Serialize)]
9#[serde(default)]
10pub struct AggregatorServiceConfig {
11 #[serde(flatten)]
13 pub aggregator: AggregatorConfig,
14
15 pub max_name_length: usize,
19
20 pub max_tag_key_length: usize,
24
25 pub max_tag_value_length: usize,
29
30 pub max_flush_bytes: usize,
37}
38
39impl AggregatorServiceConfig {
40 pub fn timestamp_range(&self) -> std::ops::Range<UnixTimestamp> {
44 let now = UnixTimestamp::now().as_secs();
45 let min_timestamp =
46 UnixTimestamp::from_secs(now.saturating_sub(self.aggregator.max_secs_in_past));
47 let max_timestamp =
48 UnixTimestamp::from_secs(now.saturating_add(self.aggregator.max_secs_in_future));
49 min_timestamp..max_timestamp
50 }
51}
52
53impl Default for AggregatorServiceConfig {
54 fn default() -> Self {
55 Self {
56 aggregator: AggregatorConfig::default(),
57 max_name_length: 200,
58 max_tag_key_length: 200,
59 max_tag_value_length: 200,
60 max_flush_bytes: 5_000_000, }
62 }
63}
64
65#[derive(Clone, Debug, Deserialize, Serialize)]
69pub struct ScopedAggregatorConfig {
70 pub name: String,
72 pub condition: Condition,
75 pub config: AggregatorServiceConfig,
77}
78
79#[derive(Clone, Debug, Deserialize, Serialize)]
82#[serde(tag = "op", rename_all = "lowercase")]
83pub enum Condition {
84 Eq(FieldCondition),
86 And {
88 inner: Vec<Condition>,
90 },
91 Or {
93 inner: Vec<Condition>,
95 },
96 Not {
98 inner: Box<Condition>,
100 },
101}
102
103impl Condition {
104 pub fn matches(&self, namespace: Option<MetricNamespace>) -> bool {
106 match self {
107 Condition::Eq(field) => field.matches(namespace),
108 Condition::And { inner } => inner.iter().all(|cond| cond.matches(namespace)),
109 Condition::Or { inner } => inner.iter().any(|cond| cond.matches(namespace)),
110 Condition::Not { inner } => !inner.matches(namespace),
111 }
112 }
113}
114
115#[derive(Clone, Debug, Deserialize, Serialize)]
117#[serde(tag = "field", content = "value", rename_all = "lowercase")]
118pub enum FieldCondition {
119 Namespace(MetricNamespace),
121}
122
123impl FieldCondition {
124 fn matches(&self, namespace: Option<MetricNamespace>) -> bool {
125 match (self, namespace) {
126 (FieldCondition::Namespace(expected), Some(actual)) => expected == &actual,
127 _ => false,
128 }
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use insta::assert_debug_snapshot;
135 use relay_metrics::MetricNamespace;
136 use serde_json::json;
137
138 use super::*;
139
140 #[test]
141 fn condition_roundtrip() {
142 let json = json!({"op": "eq", "field": "namespace", "value": "spans"});
143 assert_debug_snapshot!(
144 serde_json::from_value::<Condition>(json).unwrap(),
145 @r###"
146 Eq(
147 Namespace(
148 Spans,
149 ),
150 )
151 "###
152 );
153 }
154
155 #[test]
156 fn condition_multiple_namespaces() {
157 let json = json!({
158 "op": "or",
159 "inner": [
160 {"op": "eq", "field": "namespace", "value": "spans"},
161 {"op": "eq", "field": "namespace", "value": "custom"}
162 ]
163 });
164
165 let condition = serde_json::from_value::<Condition>(json).unwrap();
166 assert!(condition.matches(Some(MetricNamespace::Spans)));
167 assert!(condition.matches(Some(MetricNamespace::Custom)));
168 assert!(!condition.matches(Some(MetricNamespace::Transactions)));
169 }
170}