actix_web_csp/monitoring/
stats.rs1use 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}