upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/metrics
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2025-12-11 16:53:03 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2025-12-11 16:53:03 +0000
commit2a9160836bb87fdea3ae891563b0169c68d1c2ab (patch)
tree583c890687beaf7f380fc0be131bdf17485f06fa /src/metrics
parent52489d3b1a7d79e164b4cc901b53fd06c05ce1b1 (diff)
fix: resolve all fmt and clippy warnings
Main lib (src/): - Add #[allow(dead_code)] for build_info field (stored to prevent Prometheus unregistration) - Add #[allow(dead_code)] for first_seen field (reserved for future rate limiting) - Replace .or_insert_with(RelaySyncNeeds::default) with .or_default() - Replace manual div_ceil implementations with .div_ceil(100) Test code (tests/): - Replace .expect(&format!(...)) with .unwrap_or_else(|_| panic!(...)) - Remove needless borrows in fetch_metrics() calls - Add #[allow(dead_code)] and #[allow(unused_imports)] to test helpers module grasp-audit: - Apply cargo fmt to fix formatting
Diffstat (limited to 'src/metrics')
-rw-r--r--src/metrics/bandwidth.rs13
-rw-r--r--src/metrics/connection.rs58
-rw-r--r--src/metrics/mod.rs127
3 files changed, 122 insertions, 76 deletions
diff --git a/src/metrics/bandwidth.rs b/src/metrics/bandwidth.rs
index d2c53e8..d51af12 100644
--- a/src/metrics/bandwidth.rs
+++ b/src/metrics/bandwidth.rs
@@ -80,7 +80,9 @@ impl BandwidthTracker {
80 &["repo"], 80 &["repo"],
81 ) 81 )
82 .unwrap(); 82 .unwrap();
83 registry.register(Box::new(top_repos_gauge.clone())).unwrap(); 83 registry
84 .register(Box::new(top_repos_gauge.clone()))
85 .unwrap();
84 86
85 Self { 87 Self {
86 all_repos: DashMap::new(), 88 all_repos: DashMap::new(),
@@ -120,7 +122,12 @@ impl BandwidthTracker {
120 // Try to update the timestamp atomically to prevent concurrent refreshes 122 // Try to update the timestamp atomically to prevent concurrent refreshes
121 if self 123 if self
122 .last_refresh_nanos 124 .last_refresh_nanos
123 .compare_exchange(last_refresh, elapsed_nanos, Ordering::SeqCst, Ordering::Relaxed) 125 .compare_exchange(
126 last_refresh,
127 elapsed_nanos,
128 Ordering::SeqCst,
129 Ordering::Relaxed,
130 )
124 .is_ok() 131 .is_ok()
125 { 132 {
126 self.refresh_top_n(); 133 self.refresh_top_n();
@@ -298,4 +305,4 @@ mod tests {
298 // Refresh should not panic on empty data 305 // Refresh should not panic on empty data
299 tracker.refresh_top_n(); 306 tracker.refresh_top_n();
300 } 307 }
301} \ No newline at end of file 308}
diff --git a/src/metrics/connection.rs b/src/metrics/connection.rs
index 6a7f406..2d42081 100644
--- a/src/metrics/connection.rs
+++ b/src/metrics/connection.rs
@@ -25,7 +25,8 @@ use tracing::warn;
25struct ConnectionInfo { 25struct ConnectionInfo {
26 /// Number of active connections from this IP 26 /// Number of active connections from this IP
27 count: u32, 27 count: u32,
28 /// When the first connection from this IP was established 28 /// When the first connection from this IP was established (for future rate limiting)
29 #[allow(dead_code)]
29 first_seen: Instant, 30 first_seen: Instant,
30 /// Whether this IP has been flagged as potentially abusive 31 /// Whether this IP has been flagged as potentially abusive
31 flagged_as_abuse: bool, 32 flagged_as_abuse: bool,
@@ -48,16 +49,16 @@ struct ConnectionInfo {
48pub struct ConnectionTracker { 49pub struct ConnectionTracker {
49 /// Active connections per IP (INTERNAL ONLY - never exposed to metrics) 50 /// Active connections per IP (INTERNAL ONLY - never exposed to metrics)
50 connections: DashMap<IpAddr, ConnectionInfo>, 51 connections: DashMap<IpAddr, ConnectionInfo>,
51 52
52 /// Threshold for abuse flagging (connections per IP) 53 /// Threshold for abuse flagging (connections per IP)
53 abuse_threshold: u32, 54 abuse_threshold: u32,
54 55
55 /// Prometheus gauge: total active connections 56 /// Prometheus gauge: total active connections
56 active_connections: IntGauge, 57 active_connections: IntGauge,
57 58
58 /// Prometheus gauge: number of unique IPs connected 59 /// Prometheus gauge: number of unique IPs connected
59 unique_ips: IntGauge, 60 unique_ips: IntGauge,
60 61
61 /// Prometheus gauge: number of IPs flagged as potential abusers 62 /// Prometheus gauge: number of IPs flagged as potential abusers
62 flagged_abusers: IntGauge, 63 flagged_abusers: IntGauge,
63} 64}
@@ -70,29 +71,30 @@ impl ConnectionTracker {
70 /// * `abuse_threshold` - Number of connections from a single IP before flagging 71 /// * `abuse_threshold` - Number of connections from a single IP before flagging
71 /// * `registry` - Prometheus registry to register metrics with 72 /// * `registry` - Prometheus registry to register metrics with
72 pub fn new(abuse_threshold: u32, registry: &Registry) -> Self { 73 pub fn new(abuse_threshold: u32, registry: &Registry) -> Self {
73 let active_connections = IntGauge::with_opts( 74 let active_connections = IntGauge::with_opts(Opts::new(
74 Opts::new( 75 "ngit_websocket_connections_active",
75 "ngit_websocket_connections_active", 76 "Current active WebSocket connections",
76 "Current active WebSocket connections", 77 ))
77 ) 78 .unwrap();
78 ).unwrap(); 79 registry
79 registry.register(Box::new(active_connections.clone())).unwrap(); 80 .register(Box::new(active_connections.clone()))
80 81 .unwrap();
81 let unique_ips = IntGauge::with_opts( 82
82 Opts::new( 83 let unique_ips = IntGauge::with_opts(Opts::new(
83 "ngit_websocket_unique_ips", 84 "ngit_websocket_unique_ips",
84 "Number of unique IP addresses connected (NOT the IPs themselves)", 85 "Number of unique IP addresses connected (NOT the IPs themselves)",
85 ) 86 ))
86 ).unwrap(); 87 .unwrap();
87 registry.register(Box::new(unique_ips.clone())).unwrap(); 88 registry.register(Box::new(unique_ips.clone())).unwrap();
88 89
89 let flagged_abusers = IntGauge::with_opts( 90 let flagged_abusers = IntGauge::with_opts(Opts::new(
90 Opts::new( 91 "ngit_websocket_flagged_abusers",
91 "ngit_websocket_flagged_abusers", 92 "Number of IPs exceeding connection threshold",
92 "Number of IPs exceeding connection threshold", 93 ))
93 ) 94 .unwrap();
94 ).unwrap(); 95 registry
95 registry.register(Box::new(flagged_abusers.clone())).unwrap(); 96 .register(Box::new(flagged_abusers.clone()))
97 .unwrap();
96 98
97 Self { 99 Self {
98 connections: DashMap::new(), 100 connections: DashMap::new(),
@@ -140,7 +142,7 @@ impl ConnectionTracker {
140 142
141 // Update Prometheus metrics (aggregate counts only) 143 // Update Prometheus metrics (aggregate counts only)
142 self.active_connections.inc(); 144 self.active_connections.inc();
143 145
144 if is_new_ip { 146 if is_new_ip {
145 self.unique_ips.inc(); 147 self.unique_ips.inc();
146 } 148 }
@@ -334,4 +336,4 @@ mod tests {
334 assert_eq!(tracker.active_connections(), 0); 336 assert_eq!(tracker.active_connections(), 0);
335 assert_eq!(tracker.unique_ip_count(), 0); 337 assert_eq!(tracker.unique_ip_count(), 0);
336 } 338 }
337} \ No newline at end of file 339}
diff --git a/src/metrics/mod.rs b/src/metrics/mod.rs
index 736414f..5420dfd 100644
--- a/src/metrics/mod.rs
+++ b/src/metrics/mod.rs
@@ -87,7 +87,8 @@ struct MetricsInner {
87 // === System Health Metrics === 87 // === System Health Metrics ===
88 /// Server start time for uptime calculation 88 /// Server start time for uptime calculation
89 pub start_time: Instant, 89 pub start_time: Instant,
90 /// Build information gauge 90 /// Build information gauge (stored to prevent unregistration from Prometheus)
91 #[allow(dead_code)]
91 pub build_info: GaugeVec, 92 pub build_info: GaugeVec,
92} 93}
93 94
@@ -158,7 +159,10 @@ impl Metrics {
158 159
159 /// Start timing a git operation, returns a timer 160 /// Start timing a git operation, returns a timer
160 pub fn start_git_operation_timer(&self, operation: &str) -> GitOperationTimer { 161 pub fn start_git_operation_timer(&self, operation: &str) -> GitOperationTimer {
161 GitOperationTimer::new(self.inner.git_operation_duration.clone(), operation.to_string()) 162 GitOperationTimer::new(
163 self.inner.git_operation_duration.clone(),
164 operation.to_string(),
165 )
162 } 166 }
163 167
164 /// Record bytes transferred for a git operation 168 /// Record bytes transferred for a git operation
@@ -266,13 +270,14 @@ impl MetricsInner {
266 } 270 }
267 271
268 // WebSocket metrics 272 // WebSocket metrics
269 let websocket_connections_total = Counter::with_opts( 273 let websocket_connections_total = Counter::with_opts(Opts::new(
270 Opts::new( 274 "ngit_websocket_connections_total",
271 "ngit_websocket_connections_total", 275 "Total WebSocket connections since startup",
272 "Total WebSocket connections since startup", 276 ))
273 ) 277 .unwrap();
274 ).unwrap(); 278 REGISTRY
275 REGISTRY.register(Box::new(websocket_connections_total.clone())).unwrap(); 279 .register(Box::new(websocket_connections_total.clone()))
280 .unwrap();
276 281
277 let websocket_connection_duration = Histogram::with_opts( 282 let websocket_connection_duration = Histogram::with_opts(
278 HistogramOpts::new( 283 HistogramOpts::new(
@@ -280,8 +285,11 @@ impl MetricsInner {
280 "Duration of WebSocket connections", 285 "Duration of WebSocket connections",
281 ) 286 )
282 .buckets(vec![1.0, 5.0, 15.0, 30.0, 60.0, 300.0, 900.0, 3600.0]), 287 .buckets(vec![1.0, 5.0, 15.0, 30.0, 60.0, 300.0, 900.0, 3600.0]),
283 ).unwrap(); 288 )
284 REGISTRY.register(Box::new(websocket_connection_duration.clone())).unwrap(); 289 .unwrap();
290 REGISTRY
291 .register(Box::new(websocket_connection_duration.clone()))
292 .unwrap();
285 293
286 let websocket_messages_received = CounterVec::new( 294 let websocket_messages_received = CounterVec::new(
287 Opts::new( 295 Opts::new(
@@ -289,8 +297,11 @@ impl MetricsInner {
289 "WebSocket messages received by type", 297 "WebSocket messages received by type",
290 ), 298 ),
291 &["type"], 299 &["type"],
292 ).unwrap(); 300 )
293 REGISTRY.register(Box::new(websocket_messages_received.clone())).unwrap(); 301 .unwrap();
302 REGISTRY
303 .register(Box::new(websocket_messages_received.clone()))
304 .unwrap();
294 305
295 let websocket_messages_sent = CounterVec::new( 306 let websocket_messages_sent = CounterVec::new(
296 Opts::new( 307 Opts::new(
@@ -298,8 +309,11 @@ impl MetricsInner {
298 "WebSocket messages sent by type", 309 "WebSocket messages sent by type",
299 ), 310 ),
300 &["type"], 311 &["type"],
301 ).unwrap(); 312 )
302 REGISTRY.register(Box::new(websocket_messages_sent.clone())).unwrap(); 313 .unwrap();
314 REGISTRY
315 .register(Box::new(websocket_messages_sent.clone()))
316 .unwrap();
303 317
304 // Git operation metrics 318 // Git operation metrics
305 let git_operations_total = CounterVec::new( 319 let git_operations_total = CounterVec::new(
@@ -308,8 +322,11 @@ impl MetricsInner {
308 "Git operations by type and status", 322 "Git operations by type and status",
309 ), 323 ),
310 &["operation", "status"], 324 &["operation", "status"],
311 ).unwrap(); 325 )
312 REGISTRY.register(Box::new(git_operations_total.clone())).unwrap(); 326 .unwrap();
327 REGISTRY
328 .register(Box::new(git_operations_total.clone()))
329 .unwrap();
313 330
314 let git_operation_duration = HistogramVec::new( 331 let git_operation_duration = HistogramVec::new(
315 HistogramOpts::new( 332 HistogramOpts::new(
@@ -318,8 +335,11 @@ impl MetricsInner {
318 ) 335 )
319 .buckets(vec![0.1, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0, 60.0]), 336 .buckets(vec![0.1, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0, 60.0]),
320 &["operation"], 337 &["operation"],
321 ).unwrap(); 338 )
322 REGISTRY.register(Box::new(git_operation_duration.clone())).unwrap(); 339 .unwrap();
340 REGISTRY
341 .register(Box::new(git_operation_duration.clone()))
342 .unwrap();
323 343
324 let git_bytes_total = CounterVec::new( 344 let git_bytes_total = CounterVec::new(
325 Opts::new( 345 Opts::new(
@@ -327,8 +347,11 @@ impl MetricsInner {
327 "Total bytes transferred for git operations", 347 "Total bytes transferred for git operations",
328 ), 348 ),
329 &["direction"], 349 &["direction"],
330 ).unwrap(); 350 )
331 REGISTRY.register(Box::new(git_bytes_total.clone())).unwrap(); 351 .unwrap();
352 REGISTRY
353 .register(Box::new(git_bytes_total.clone()))
354 .unwrap();
332 355
333 let git_push_authorization = CounterVec::new( 356 let git_push_authorization = CounterVec::new(
334 Opts::new( 357 Opts::new(
@@ -336,8 +359,11 @@ impl MetricsInner {
336 "Push authorization results", 359 "Push authorization results",
337 ), 360 ),
338 &["result"], 361 &["result"],
339 ).unwrap(); 362 )
340 REGISTRY.register(Box::new(git_push_authorization.clone())).unwrap(); 363 .unwrap();
364 REGISTRY
365 .register(Box::new(git_push_authorization.clone()))
366 .unwrap();
341 367
342 // Nostr event metrics 368 // Nostr event metrics
343 let events_received_total = CounterVec::new( 369 let events_received_total = CounterVec::new(
@@ -346,8 +372,11 @@ impl MetricsInner {
346 "Nostr events received by kind", 372 "Nostr events received by kind",
347 ), 373 ),
348 &["kind"], 374 &["kind"],
349 ).unwrap(); 375 )
350 REGISTRY.register(Box::new(events_received_total.clone())).unwrap(); 376 .unwrap();
377 REGISTRY
378 .register(Box::new(events_received_total.clone()))
379 .unwrap();
351 380
352 let events_stored_total = CounterVec::new( 381 let events_stored_total = CounterVec::new(
353 Opts::new( 382 Opts::new(
@@ -355,8 +384,11 @@ impl MetricsInner {
355 "Nostr events successfully stored by kind", 384 "Nostr events successfully stored by kind",
356 ), 385 ),
357 &["kind"], 386 &["kind"],
358 ).unwrap(); 387 )
359 REGISTRY.register(Box::new(events_stored_total.clone())).unwrap(); 388 .unwrap();
389 REGISTRY
390 .register(Box::new(events_stored_total.clone()))
391 .unwrap();
360 392
361 let events_rejected_total = CounterVec::new( 393 let events_rejected_total = CounterVec::new(
362 Opts::new( 394 Opts::new(
@@ -364,31 +396,36 @@ impl MetricsInner {
364 "Nostr events rejected by kind and reason", 396 "Nostr events rejected by kind and reason",
365 ), 397 ),
366 &["kind", "reason"], 398 &["kind", "reason"],
367 ).unwrap(); 399 )
368 REGISTRY.register(Box::new(events_rejected_total.clone())).unwrap(); 400 .unwrap();
401 REGISTRY
402 .register(Box::new(events_rejected_total.clone()))
403 .unwrap();
369 404
370 // Repository metrics 405 // Repository metrics
371 let repositories_total = Gauge::with_opts( 406 let repositories_total = Gauge::with_opts(Opts::new(
372 Opts::new( 407 "ngit_repositories_total",
373 "ngit_repositories_total", 408 "Total repositories hosted",
374 "Total repositories hosted", 409 ))
375 ) 410 .unwrap();
376 ).unwrap(); 411 REGISTRY
377 REGISTRY.register(Box::new(repositories_total.clone())).unwrap(); 412 .register(Box::new(repositories_total.clone()))
413 .unwrap();
378 414
379 // Build info 415 // Build info
380 let build_info = GaugeVec::new( 416 let build_info = GaugeVec::new(
381 Opts::new( 417 Opts::new("ngit_build_info", "Build information"),
382 "ngit_build_info",
383 "Build information",
384 ),
385 &["version", "commit"], 418 &["version", "commit"],
386 ).unwrap(); 419 )
420 .unwrap();
387 REGISTRY.register(Box::new(build_info.clone())).unwrap(); 421 REGISTRY.register(Box::new(build_info.clone())).unwrap();
388 422
389 // Set build info gauge to 1 (it's just for labels) 423 // Set build info gauge to 1 (it's just for labels)
390 build_info 424 build_info
391 .with_label_values(&[env!("CARGO_PKG_VERSION"), option_env!("GIT_HASH").unwrap_or("unknown")]) 425 .with_label_values(&[
426 env!("CARGO_PKG_VERSION"),
427 option_env!("GIT_HASH").unwrap_or("unknown"),
428 ])
392 .set(1.0); 429 .set(1.0);
393 430
394 Self { 431 Self {
@@ -472,7 +509,7 @@ mod tests {
472 // Note: This test may fail if run with other tests due to global registry 509 // Note: This test may fail if run with other tests due to global registry
473 // In production, consider using a test-specific registry 510 // In production, consider using a test-specific registry
474 let metrics = Metrics::new(10); 511 let metrics = Metrics::new(10);
475 512
476 // Test that we can record metrics without panicking 513 // Test that we can record metrics without panicking
477 metrics.record_websocket_connection(); 514 metrics.record_websocket_connection();
478 metrics.record_message_received("REQ"); 515 metrics.record_message_received("REQ");
@@ -484,4 +521,4 @@ mod tests {
484 metrics.record_event_rejected(1, "invalid_signature"); 521 metrics.record_event_rejected(1, "invalid_signature");
485 metrics.set_repositories_total(5); 522 metrics.set_repositories_total(5);
486 } 523 }
487} \ No newline at end of file 524}