Skip to main content

actix_web_csp/monitoring/
stats.rs

1use std::fmt;
2use std::sync::atomic::{AtomicUsize, Ordering};
3use std::time::Instant;
4
5#[derive(Debug)]
6pub struct CspStats {
7    request_count: AtomicUsize,
8    nonce_generation_count: AtomicUsize,
9    policy_update_count: AtomicUsize,
10    header_generation_time_ns: AtomicUsize,
11    violation_count: AtomicUsize,
12    cache_hit_count: AtomicUsize,
13    policy_hash_time_ns: AtomicUsize,
14    policy_serialize_time_ns: AtomicUsize,
15    policy_validations: AtomicUsize,
16    start_time: Instant,
17}
18
19impl Default for CspStats {
20    fn default() -> Self {
21        Self {
22            request_count: Default::default(),
23            nonce_generation_count: Default::default(),
24            policy_update_count: Default::default(),
25            header_generation_time_ns: Default::default(),
26            violation_count: Default::default(),
27            cache_hit_count: Default::default(),
28            policy_hash_time_ns: Default::default(),
29            policy_serialize_time_ns: Default::default(),
30            policy_validations: Default::default(),
31            start_time: Instant::now(),
32        }
33    }
34}
35
36impl CspStats {
37    #[inline]
38    pub fn request_count(&self) -> usize {
39        self.request_count.load(Ordering::Relaxed)
40    }
41
42    #[inline]
43    pub fn nonce_generation_count(&self) -> usize {
44        self.nonce_generation_count.load(Ordering::Relaxed)
45    }
46
47    #[inline]
48    pub fn policy_update_count(&self) -> usize {
49        self.policy_update_count.load(Ordering::Relaxed)
50    }
51
52    #[inline]
53    pub fn avg_header_generation_time_ns(&self) -> f64 {
54        let count = self.request_count.load(Ordering::Relaxed);
55        if count == 0 {
56            0.0
57        } else {
58            self.header_generation_time_ns.load(Ordering::Relaxed) as f64 / count as f64
59        }
60    }
61
62    #[inline]
63    pub fn violation_count(&self) -> usize {
64        self.violation_count.load(Ordering::Relaxed)
65    }
66
67    #[inline]
68    pub fn cache_hit_count(&self) -> usize {
69        self.cache_hit_count.load(Ordering::Relaxed)
70    }
71
72    #[inline]
73    pub fn total_policy_hash_time_ns(&self) -> usize {
74        self.policy_hash_time_ns.load(Ordering::Relaxed)
75    }
76
77    #[inline]
78    pub fn total_policy_serialize_time_ns(&self) -> usize {
79        self.policy_serialize_time_ns.load(Ordering::Relaxed)
80    }
81
82    #[inline]
83    pub fn policy_validations(&self) -> usize {
84        self.policy_validations.load(Ordering::Relaxed)
85    }
86
87    #[inline]
88    pub fn uptime_secs(&self) -> u64 {
89        self.start_time.elapsed().as_secs()
90    }
91
92    #[inline]
93    pub fn requests_per_second(&self) -> f64 {
94        let uptime = self.start_time.elapsed().as_secs_f64();
95        if uptime > 0.0 {
96            self.request_count() as f64 / uptime
97        } else {
98            0.0
99        }
100    }
101
102    #[inline]
103    pub(crate) fn increment_request_count(&self) {
104        self.request_count.fetch_add(1, Ordering::Relaxed);
105    }
106
107    #[inline]
108    pub(crate) fn increment_nonce_generation_count(&self) {
109        self.nonce_generation_count.fetch_add(1, Ordering::Relaxed);
110    }
111
112    #[inline]
113    pub(crate) fn increment_policy_update_count(&self) {
114        self.policy_update_count.fetch_add(1, Ordering::Relaxed);
115    }
116
117    #[inline]
118    #[allow(dead_code)]
119    pub(crate) fn add_header_generation_time(&self, time_ns: usize) {
120        self.header_generation_time_ns
121            .fetch_add(time_ns, Ordering::Relaxed);
122    }
123
124    #[inline]
125    pub(crate) fn increment_violation_count(&self) {
126        self.violation_count.fetch_add(1, Ordering::Relaxed);
127    }
128
129    #[inline]
130    pub(crate) fn increment_cache_hit_count(&self) {
131        self.cache_hit_count.fetch_add(1, Ordering::Relaxed);
132    }
133
134    #[inline]
135    pub(crate) fn add_policy_hash_time(&self, time_ns: usize) {
136        self.policy_hash_time_ns
137            .fetch_add(time_ns, Ordering::Relaxed);
138    }
139
140    #[inline]
141    pub(crate) fn add_policy_serialize_time(&self, time_ns: usize) {
142        self.policy_serialize_time_ns
143            .fetch_add(time_ns, Ordering::Relaxed);
144    }
145
146    #[inline]
147    pub fn new() -> Self {
148        Self {
149            start_time: Instant::now(),
150            ..Default::default()
151        }
152    }
153
154    #[inline]
155    pub fn reset(&self) {
156        self.request_count.store(0, Ordering::Relaxed);
157        self.nonce_generation_count.store(0, Ordering::Relaxed);
158        self.policy_update_count.store(0, Ordering::Relaxed);
159        self.header_generation_time_ns.store(0, Ordering::Relaxed);
160        self.violation_count.store(0, Ordering::Relaxed);
161        self.cache_hit_count.store(0, Ordering::Relaxed);
162        self.policy_hash_time_ns.store(0, Ordering::Relaxed);
163        self.policy_serialize_time_ns.store(0, Ordering::Relaxed);
164        self.policy_validations.store(0, Ordering::Relaxed);
165    }
166}
167
168impl fmt::Display for CspStats {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        writeln!(f, "CSP Middleware Statistics:")?;
171        writeln!(f, "  Uptime: {} seconds", self.uptime_secs())?;
172        writeln!(f, "  Requests processed: {}", self.request_count())?;
173        writeln!(
174            f,
175            "  Requests per second: {:.2}",
176            self.requests_per_second()
177        )?;
178        writeln!(f, "  Nonces generated: {}", self.nonce_generation_count())?;
179        writeln!(f, "  Policy updates: {}", self.policy_update_count())?;
180        writeln!(f, "  Policy validations: {}", self.policy_validations())?;
181        writeln!(
182            f,
183            "  Average header generation time: {:.2} ns",
184            self.avg_header_generation_time_ns()
185        )?;
186        writeln!(
187            f,
188            "  Total policy hash time: {} ns",
189            self.total_policy_hash_time_ns()
190        )?;
191        writeln!(
192            f,
193            "  Total policy serialize time: {} ns",
194            self.total_policy_serialize_time_ns()
195        )?;
196        writeln!(f, "  Violations reported: {}", self.violation_count())?;
197        writeln!(f, "  Cache hits: {}", self.cache_hit_count())?;
198        Ok(())
199    }
200}