Skip to main content

helix_driver_host/metrics/
labels.rs

1pub const MAX_METRIC_LABELS: usize = 8;
2
3#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
4pub enum LabelKey {
5    Layer,
6    Scenario,
7    Stage,
8    Action,
9    TickKind,
10    EffectKind,
11    StorageOp,
12    Platform,
13    ChannelShape,
14    PayloadBucket,
15    RateBucket,
16    Status,
17    ServiceName,
18    DeploymentEnvironment,
19    Protocol,
20    Operation,
21    Direction,
22    StatusClass,
23    ErrorKind,
24    ExporterState,
25    Pool,
26    Consumer,
27    Overflow,
28    LifecycleState,
29    Terminal,
30    Path,
31    Role,
32    Result,
33    RecipientScope,
34    State,
35}
36
37impl LabelKey {
38    pub const fn as_str(self) -> &'static str {
39        match self {
40            Self::Layer => "layer",
41            Self::Scenario => "scenario",
42            Self::Stage => "stage",
43            Self::Action => "action",
44            Self::TickKind => "tick_kind",
45            Self::EffectKind => "effect_kind",
46            Self::StorageOp => "storage_op",
47            Self::Platform => "platform",
48            Self::ChannelShape => "channel_shape",
49            Self::PayloadBucket => "payload_bucket",
50            Self::RateBucket => "rate_bucket",
51            Self::Status => "status",
52            Self::ServiceName => "service_name",
53            Self::DeploymentEnvironment => "deployment_environment",
54            Self::Protocol => "protocol",
55            Self::Operation => "operation",
56            Self::Direction => "direction",
57            Self::StatusClass => "status_class",
58            Self::ErrorKind => "error_kind",
59            Self::ExporterState => "exporter_state",
60            Self::Pool => "pool",
61            Self::Consumer => "consumer",
62            Self::Overflow => "overflow",
63            Self::LifecycleState => "lifecycle_state",
64            Self::Terminal => "terminal",
65            Self::Path => "path",
66            Self::Role => "role",
67            Self::Result => "result",
68            Self::RecipientScope => "recipient_scope",
69            Self::State => "state",
70        }
71    }
72}
73
74pub const ALLOWED_LABEL_KEYS: &[LabelKey] = &[
75    LabelKey::Layer,
76    LabelKey::Scenario,
77    LabelKey::Stage,
78    LabelKey::Action,
79    LabelKey::TickKind,
80    LabelKey::EffectKind,
81    LabelKey::StorageOp,
82    LabelKey::Platform,
83    LabelKey::ChannelShape,
84    LabelKey::PayloadBucket,
85    LabelKey::RateBucket,
86    LabelKey::Status,
87    LabelKey::ServiceName,
88    LabelKey::DeploymentEnvironment,
89    LabelKey::Protocol,
90    LabelKey::Operation,
91    LabelKey::Direction,
92    LabelKey::StatusClass,
93    LabelKey::ErrorKind,
94    LabelKey::ExporterState,
95    LabelKey::Pool,
96    LabelKey::Consumer,
97    LabelKey::Overflow,
98    LabelKey::LifecycleState,
99    LabelKey::Terminal,
100    LabelKey::Path,
101    LabelKey::Role,
102    LabelKey::Result,
103    LabelKey::RecipientScope,
104    LabelKey::State,
105];
106
107#[derive(Clone, Copy, Debug, PartialEq, Eq)]
108pub struct MetricLabel {
109    pub key: LabelKey,
110    pub value: &'static str,
111}
112
113#[derive(Clone, Copy, Debug, PartialEq, Eq)]
114pub struct MetricLabels {
115    values: [Option<MetricLabel>; MAX_METRIC_LABELS],
116    len: u8,
117}
118
119impl MetricLabels {
120    pub const EMPTY: Self = Self {
121        values: [None; MAX_METRIC_LABELS],
122        len: 0,
123    };
124
125    pub const fn one(key: LabelKey, value: &'static str) -> Self {
126        let mut labels = Self::EMPTY;
127        labels.values[0] = Some(MetricLabel { key, value });
128        labels.len = 1;
129        labels
130    }
131
132    pub fn with(mut self, key: LabelKey, value: &'static str) -> Self {
133        let index = self.len as usize;
134        if index < MAX_METRIC_LABELS {
135            self.values[index] = Some(MetricLabel { key, value });
136            self.len += 1;
137        }
138        self
139    }
140
141    pub fn iter(&self) -> impl Iterator<Item = MetricLabel> + '_ {
142        self.values[..self.len as usize].iter().flatten().copied()
143    }
144
145    pub const fn len(&self) -> usize {
146        self.len as usize
147    }
148
149    pub const fn is_empty(&self) -> bool {
150        self.len == 0
151    }
152}
153
154impl Default for MetricLabels {
155    fn default() -> Self {
156        Self::EMPTY
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    fn whitelist_is_exact_and_excludes_high_cardinality_keys() {
166        assert_eq!(ALLOWED_LABEL_KEYS.len(), 30);
167        let names: Vec<_> = ALLOWED_LABEL_KEYS.iter().map(|key| key.as_str()).collect();
168        for forbidden in [
169            "runId",
170            "corr_key",
171            "req_id",
172            "temporaryId",
173            "postId",
174            "channelId",
175            "userId",
176            "eventSeq",
177        ] {
178            assert!(!names.contains(&forbidden));
179        }
180    }
181}