helix_driver_host/metrics/
labels.rs1pub 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}