use std::sync::{
Arc,
atomic::{AtomicUsize, Ordering},
};
use ahash::HashMap;
use parking_lot::RwLock;
use crate::{server::transport::Transport, service::session::Identifier};
#[derive(Debug, Clone, Copy)]
pub enum Stats {
ReceivedBytes(usize),
SendBytes(usize),
ReceivedPkts(usize),
SendPkts(usize),
ErrorPkts(usize),
}
pub trait Number {
fn add(&self, value: usize);
fn get(&self) -> usize;
}
#[derive(Default)]
pub struct Count(AtomicUsize);
impl Number for Count {
fn add(&self, value: usize) {
self.0.fetch_add(value, Ordering::Relaxed);
}
fn get(&self) -> usize {
self.0.load(Ordering::Relaxed)
}
}
pub struct Counts<T> {
pub received_bytes: T,
pub send_bytes: T,
pub received_pkts: T,
pub send_pkts: T,
pub error_pkts: T,
}
impl<T: Number> Counts<T> {
pub fn add(&self, payload: &Stats) {
match payload {
Stats::ReceivedBytes(v) => self.received_bytes.add(*v),
Stats::ReceivedPkts(v) => self.received_pkts.add(*v),
Stats::SendBytes(v) => self.send_bytes.add(*v),
Stats::SendPkts(v) => self.send_pkts.add(*v),
Stats::ErrorPkts(v) => self.error_pkts.add(*v),
}
}
}
#[derive(Clone)]
pub struct Statistics(Arc<RwLock<HashMap<Identifier, Counts<Count>>>>);
impl Default for Statistics {
#[cfg(feature = "api")]
fn default() -> Self {
use ahash::HashMapExt;
Self(Arc::new(RwLock::new(HashMap::with_capacity(1024))))
}
#[cfg(not(feature = "api"))]
fn default() -> Self {
Self(Default::default())
}
}
impl Statistics {
pub fn get_reporter(&self, transport: Transport) -> StatisticsReporter {
StatisticsReporter {
table: self.0.clone(),
transport,
}
}
pub fn register(&self, identifier: Identifier) {
#[cfg(feature = "prometheus")]
{
crate::prometheus::METRICS.allocated.inc();
}
self.0.write().insert(
identifier,
Counts {
received_bytes: Count::default(),
send_bytes: Count::default(),
received_pkts: Count::default(),
send_pkts: Count::default(),
error_pkts: Count::default(),
},
);
}
pub fn unregister(&self, identifier: &Identifier) {
#[cfg(feature = "prometheus")]
{
crate::prometheus::METRICS.allocated.dec();
}
self.0.write().remove(identifier);
}
pub fn get(&self, identifier: &Identifier) -> Option<Counts<usize>> {
self.0.read().get(identifier).map(|counts| Counts {
received_bytes: counts.received_bytes.get(),
received_pkts: counts.received_pkts.get(),
send_bytes: counts.send_bytes.get(),
send_pkts: counts.send_pkts.get(),
error_pkts: counts.error_pkts.get(),
})
}
}
#[derive(Clone)]
#[allow(unused)]
pub struct StatisticsReporter {
table: Arc<RwLock<HashMap<Identifier, Counts<Count>>>>,
transport: Transport,
}
impl StatisticsReporter {
#[allow(unused_variables)]
pub fn send(&self, identifier: &Identifier, reports: &[Stats]) {
#[cfg(feature = "api")]
{
#[cfg(feature = "prometheus")]
{
for report in reports {
crate::prometheus::METRICS.add(self.transport, report);
}
}
if let Some(counts) = self.table.read().get(identifier) {
for item in reports {
counts.add(item);
}
}
}
}
}