1#[cfg(feature = "metrics")]
14use prometheus::{Counter, Encoder, Gauge, Histogram, HistogramOpts, Opts, Registry, TextEncoder};
15#[cfg(feature = "metrics")]
16use std::sync::Arc;
17#[cfg(feature = "metrics")]
18use tracing::warn;
19
20pub struct MetricsCollector {
22 #[cfg(feature = "metrics")]
23 registry: Registry,
24
25 #[cfg(feature = "metrics")]
26 sessions_total: Gauge,
27
28 #[cfg(feature = "metrics")]
29 sessions_active: Gauge,
30
31 #[cfg(feature = "metrics")]
32 packets_received: Counter,
33
34 #[cfg(feature = "metrics")]
35 packets_sent: Counter,
36
37 #[cfg(feature = "metrics")]
38 bytes_received: Counter,
39
40 #[cfg(feature = "metrics")]
41 bytes_sent: Counter,
42
43 #[cfg(feature = "metrics")]
44 packet_processing_time: Histogram,
45
46 #[cfg(feature = "metrics")]
47 tag_validation_time: Histogram,
48
49 #[cfg(feature = "metrics")]
50 mask_rotations: Counter,
51
52 #[cfg(feature = "metrics")]
53 key_rotations: Counter,
54
55 #[cfg(feature = "metrics")]
56 neural_checks_total: Counter,
57
58 #[cfg(feature = "metrics")]
59 neural_checks_failed: Counter,
60
61 #[cfg(feature = "metrics")]
62 dpi_attacks_detected: Counter,
63}
64
65impl MetricsCollector {
66 pub fn new() -> Self {
68 #[cfg(feature = "metrics")]
69 {
70 let registry = Registry::new();
71
72 let sessions_total = Gauge::with_opts(Opts::new(
74 "aivpn_sessions_total",
75 "Total number of sessions",
76 ))
77 .unwrap();
78 registry.register(Box::new(sessions_total.clone())).unwrap();
79
80 let sessions_active = Gauge::with_opts(Opts::new(
81 "aivpn_sessions_active",
82 "Number of active sessions",
83 ))
84 .unwrap();
85 registry
86 .register(Box::new(sessions_active.clone()))
87 .unwrap();
88
89 let packets_received = Counter::with_opts(Opts::new(
91 "aivpn_packets_received_total",
92 "Total packets received",
93 ))
94 .unwrap();
95 registry
96 .register(Box::new(packets_received.clone()))
97 .unwrap();
98
99 let packets_sent =
100 Counter::with_opts(Opts::new("aivpn_packets_sent_total", "Total packets sent"))
101 .unwrap();
102 registry.register(Box::new(packets_sent.clone())).unwrap();
103
104 let bytes_received = Counter::with_opts(Opts::new(
106 "aivpn_bytes_received_total",
107 "Total bytes received",
108 ))
109 .unwrap();
110 registry.register(Box::new(bytes_received.clone())).unwrap();
111
112 let bytes_sent =
113 Counter::with_opts(Opts::new("aivpn_bytes_sent_total", "Total bytes sent"))
114 .unwrap();
115 registry.register(Box::new(bytes_sent.clone())).unwrap();
116
117 let packet_processing_time = Histogram::with_opts(HistogramOpts::new(
120 "aivpn_packet_processing_seconds",
121 "Packet processing time",
122 ))
123 .unwrap();
124 registry
125 .register(Box::new(packet_processing_time.clone()))
126 .unwrap();
127
128 let tag_validation_time = Histogram::with_opts(HistogramOpts::new(
129 "aivpn_tag_validation_seconds",
130 "Tag validation time",
131 ))
132 .unwrap();
133 registry
134 .register(Box::new(tag_validation_time.clone()))
135 .unwrap();
136
137 let mask_rotations = Counter::with_opts(Opts::new(
139 "aivpn_mask_rotations_total",
140 "Total mask rotations",
141 ))
142 .unwrap();
143 registry.register(Box::new(mask_rotations.clone())).unwrap();
144
145 let key_rotations = Counter::with_opts(Opts::new(
146 "aivpn_key_rotations_total",
147 "Total key rotations",
148 ))
149 .unwrap();
150 registry.register(Box::new(key_rotations.clone())).unwrap();
151
152 let neural_checks_total = Counter::with_opts(Opts::new(
154 "aivpn_neural_checks_total",
155 "Total neural resonance checks",
156 ))
157 .unwrap();
158 registry
159 .register(Box::new(neural_checks_total.clone()))
160 .unwrap();
161
162 let neural_checks_failed = Counter::with_opts(Opts::new(
163 "aivpn_neural_checks_failed_total",
164 "Failed neural resonance checks",
165 ))
166 .unwrap();
167 registry
168 .register(Box::new(neural_checks_failed.clone()))
169 .unwrap();
170
171 let dpi_attacks_detected = Counter::with_opts(Opts::new(
173 "aivpn_dpi_attacks_detected_total",
174 "DPI attacks detected",
175 ))
176 .unwrap();
177 registry
178 .register(Box::new(dpi_attacks_detected.clone()))
179 .unwrap();
180
181 Self {
182 registry,
183 sessions_total,
184 sessions_active,
185 packets_received,
186 packets_sent,
187 bytes_received,
188 bytes_sent,
189 packet_processing_time,
190 tag_validation_time,
191 mask_rotations,
192 key_rotations,
193 neural_checks_total,
194 neural_checks_failed,
195 dpi_attacks_detected,
196 }
197 }
198
199 #[cfg(not(feature = "metrics"))]
200 Self {}
201 }
202
203 pub fn update_session_count(&self, total: usize, active: usize) {
205 #[cfg(feature = "metrics")]
206 {
207 self.sessions_total.set(total as f64);
209 self.sessions_active.set(active as f64);
210 }
211
212 #[cfg(not(feature = "metrics"))]
213 let _ = (total, active);
214 }
215
216 pub fn record_packet_received(&self, bytes: usize) {
218 #[cfg(feature = "metrics")]
219 {
220 self.packets_received.inc();
221 self.bytes_received.inc_by(bytes as f64);
223 }
224
225 #[cfg(not(feature = "metrics"))]
226 let _ = bytes;
227 }
228
229 pub fn record_packet_sent(&self, bytes: usize) {
231 #[cfg(feature = "metrics")]
232 {
233 self.packets_sent.inc();
234 self.bytes_sent.inc_by(bytes as f64);
236 }
237
238 #[cfg(not(feature = "metrics"))]
239 let _ = bytes;
240 }
241
242 pub fn record_processing_time(&self, _seconds: f64) {
244 #[cfg(feature = "metrics")]
245 {
246 self.packet_processing_time.observe(_seconds);
247 }
248 }
249
250 pub fn record_tag_validation_time(&self, _seconds: f64) {
252 #[cfg(feature = "metrics")]
253 {
254 self.tag_validation_time.observe(_seconds);
255 }
256 }
257
258 pub fn record_mask_rotation(&self) {
260 #[cfg(feature = "metrics")]
261 {
262 self.mask_rotations.inc();
263 }
264 }
265
266 pub fn record_key_rotation(&self) {
268 #[cfg(feature = "metrics")]
269 {
270 self.key_rotations.inc();
271 }
272 }
273
274 pub fn record_neural_check(&self, _failed: bool) {
276 #[cfg(feature = "metrics")]
277 {
278 self.neural_checks_total.inc();
279 if _failed {
280 self.neural_checks_failed.inc();
281 }
282 }
283 }
284
285 pub fn record_dpi_attack(&self) {
287 #[cfg(feature = "metrics")]
288 {
289 self.dpi_attacks_detected.inc();
290 warn!("DPI attack detected!");
291 }
292 }
293
294 pub fn gather(&self) -> String {
296 #[cfg(feature = "metrics")]
297 {
298 let encoder = TextEncoder::new();
299 let metric_families = self.registry.gather();
300 let mut buf = Vec::new();
302 encoder
303 .encode(&metric_families, &mut buf)
304 .unwrap_or_default();
305 String::from_utf8(buf).unwrap_or_default()
306 }
307
308 #[cfg(not(feature = "metrics"))]
309 {
310 String::new()
311 }
312 }
313}
314
315impl Default for MetricsCollector {
316 fn default() -> Self {
317 Self::new()
318 }
319}
320
321#[cfg(feature = "metrics")]
325pub async fn metrics_handler(collector: Arc<MetricsCollector>) -> String {
326 collector.gather()
327}