cbtop/prometheus/
registry.rs1use std::collections::HashMap;
4
5use super::types::{
6 CounterValue, GaugeValue, HistogramValue, Labels, MetricDef, MetricType, DEFAULT_MAX_LABELS,
7};
8
9#[derive(Debug)]
11pub struct MetricsRegistry {
12 definitions: HashMap<String, MetricDef>,
14 gauges: HashMap<String, Vec<GaugeValue>>,
16 counters: HashMap<String, Vec<CounterValue>>,
18 histograms: HashMap<String, Vec<HistogramValue>>,
20 max_labels: usize,
22}
23
24impl Default for MetricsRegistry {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl MetricsRegistry {
31 pub fn new() -> Self {
33 Self {
34 definitions: HashMap::new(),
35 gauges: HashMap::new(),
36 counters: HashMap::new(),
37 histograms: HashMap::new(),
38 max_labels: DEFAULT_MAX_LABELS,
39 }
40 }
41
42 pub fn with_max_labels(mut self, max: usize) -> Self {
44 self.max_labels = max;
45 self
46 }
47
48 pub fn register_gauge(&mut self, name: &str, help: &str) {
50 self.definitions.insert(
51 name.to_string(),
52 MetricDef::new(name, help, MetricType::Gauge),
53 );
54 self.gauges.entry(name.to_string()).or_default();
55 }
56
57 pub fn register_counter(&mut self, name: &str, help: &str) {
59 self.definitions.insert(
60 name.to_string(),
61 MetricDef::new(name, help, MetricType::Counter),
62 );
63 self.counters.entry(name.to_string()).or_default();
64 }
65
66 pub fn register_histogram(&mut self, name: &str, help: &str) {
68 self.definitions.insert(
69 name.to_string(),
70 MetricDef::new(name, help, MetricType::Histogram),
71 );
72 self.histograms.entry(name.to_string()).or_default();
73 }
74
75 pub fn set_gauge(&mut self, name: &str, value: GaugeValue) -> bool {
77 if value.labels.len() > self.max_labels {
78 return false;
79 }
80
81 if let Some(values) = self.gauges.get_mut(name) {
82 let label_str = value.labels.format();
84 if let Some(existing) = values.iter_mut().find(|v| v.labels.format() == label_str) {
85 existing.value = value.value;
86 existing.timestamp = value.timestamp;
87 } else {
88 values.push(value);
89 }
90 true
91 } else {
92 false
93 }
94 }
95
96 pub fn inc_counter(&mut self, name: &str, labels: Labels) -> bool {
98 if labels.len() > self.max_labels {
99 return false;
100 }
101
102 if let Some(values) = self.counters.get_mut(name) {
103 let label_str = labels.format();
104 if let Some(existing) = values.iter_mut().find(|v| v.labels.format() == label_str) {
105 existing.value += 1;
106 } else {
107 values.push(CounterValue::new(1).with_labels(labels));
108 }
109 true
110 } else {
111 false
112 }
113 }
114
115 pub fn add_counter(&mut self, name: &str, amount: u64, labels: Labels) -> bool {
117 if labels.len() > self.max_labels {
118 return false;
119 }
120
121 if let Some(values) = self.counters.get_mut(name) {
122 let label_str = labels.format();
123 if let Some(existing) = values.iter_mut().find(|v| v.labels.format() == label_str) {
124 existing.value += amount;
125 } else {
126 values.push(CounterValue::new(amount).with_labels(labels));
127 }
128 true
129 } else {
130 false
131 }
132 }
133
134 pub fn observe_histogram(&mut self, name: &str, value: f64, labels: Labels) -> bool {
136 if labels.len() > self.max_labels {
137 return false;
138 }
139
140 if let Some(values) = self.histograms.get_mut(name) {
141 let label_str = labels.format();
142 if let Some(existing) = values.iter_mut().find(|v| v.labels.format() == label_str) {
143 existing.observe(value);
144 } else {
145 let mut hist = HistogramValue::new().with_labels(labels);
146 hist.observe(value);
147 values.push(hist);
148 }
149 true
150 } else {
151 false
152 }
153 }
154
155 pub fn export(&self) -> String {
157 let mut lines = Vec::new();
158
159 for (name, values) in &self.gauges {
161 if let Some(def) = self.definitions.get(name) {
162 lines.push(def.format_help());
163 lines.push(def.format_type());
164 for value in values {
165 lines.push(value.format(name));
166 }
167 }
168 }
169
170 for (name, values) in &self.counters {
172 if let Some(def) = self.definitions.get(name) {
173 lines.push(def.format_help());
174 lines.push(def.format_type());
175 for value in values {
176 lines.push(value.format(name));
177 }
178 }
179 }
180
181 for (name, values) in &self.histograms {
183 if let Some(def) = self.definitions.get(name) {
184 lines.push(def.format_help());
185 lines.push(def.format_type());
186 for value in values {
187 lines.push(value.format(name));
188 }
189 }
190 }
191
192 lines.join("\n")
193 }
194
195 pub fn metric_count(&self) -> usize {
197 self.definitions.len()
198 }
199
200 pub fn clear_values(&mut self) {
202 for values in self.gauges.values_mut() {
203 values.clear();
204 }
205 for values in self.counters.values_mut() {
206 values.clear();
207 }
208 for values in self.histograms.values_mut() {
209 values.clear();
210 }
211 }
212}