mirror of
https://git.mentality.rip/numas13/xash3d-master.git
synced 2025-01-22 04:44:31 +00:00
master: add stats feature flag
This commit is contained in:
parent
c3031d371a
commit
8cf76e55ac
@ -14,6 +14,7 @@ repository = "https://git.mentality.rip/numas13/xash3d-master"
|
|||||||
[features]
|
[features]
|
||||||
default = ["logtime"]
|
default = ["logtime"]
|
||||||
logtime = ["chrono"]
|
logtime = ["chrono"]
|
||||||
|
stats = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = "1.0.49"
|
thiserror = "1.0.49"
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
## Time in seconds before next admin request is allowed after wrong password
|
## Time in seconds before next admin request is allowed after wrong password
|
||||||
#admin = 10
|
#admin = 10
|
||||||
|
|
||||||
|
# NOTE: require build with `stats` feature flag
|
||||||
|
#
|
||||||
#[stat]
|
#[stat]
|
||||||
# Interval in seconds, set zero to disable
|
# Interval in seconds, set zero to disable
|
||||||
#interval = 0
|
#interval = 0
|
||||||
|
@ -1,155 +1,8 @@
|
|||||||
use std::fmt::{self, Write};
|
#[cfg(feature = "stats")]
|
||||||
use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering::Relaxed};
|
mod imp;
|
||||||
use std::sync::mpsc;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::thread;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
use log::info;
|
#[cfg(not(feature = "stats"))]
|
||||||
|
#[path = "stats/stub.rs"]
|
||||||
|
mod imp;
|
||||||
|
|
||||||
use crate::config::StatConfig;
|
pub use self::imp::*;
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct Counters {
|
|
||||||
servers: AtomicUsize,
|
|
||||||
server_add: AtomicU32,
|
|
||||||
server_del: AtomicU32,
|
|
||||||
query_servers: AtomicU32,
|
|
||||||
errors: AtomicU32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Counters {
|
|
||||||
fn print(&self, mut format: &str, buf: &mut String, time: Duration) -> fmt::Result {
|
|
||||||
let time = time.as_secs_f64();
|
|
||||||
let servers = self.servers.load(Relaxed);
|
|
||||||
let server_add = self.server_add.swap(0, Relaxed);
|
|
||||||
let server_del = self.server_del.swap(0, Relaxed);
|
|
||||||
let query_servers = self.query_servers.swap(0, Relaxed);
|
|
||||||
let errors = self.errors.swap(0, Relaxed);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// TODO: precompile format string
|
|
||||||
match format.find('%').map(|i| format.split_at(i)) {
|
|
||||||
Some((head, tail)) => {
|
|
||||||
format = &tail[1..];
|
|
||||||
write!(buf, "{}", head)?;
|
|
||||||
let mut chars = format.char_indices();
|
|
||||||
match chars.next().map(|(_, c)| c) {
|
|
||||||
Some('s') => write!(buf, "{}", servers)?,
|
|
||||||
Some('A') => write!(buf, "{}", server_add)?,
|
|
||||||
Some('D') => write!(buf, "{}", server_del)?,
|
|
||||||
Some('Q') => write!(buf, "{}", query_servers)?,
|
|
||||||
Some('E') => write!(buf, "{}", errors)?,
|
|
||||||
Some('a') => write!(buf, "{:.1}", server_add as f64 / time)?,
|
|
||||||
Some('d') => write!(buf, "{:.1}", server_del as f64 / time)?,
|
|
||||||
Some('q') => write!(buf, "{:.1}", query_servers as f64 / time)?,
|
|
||||||
Some('e') => write!(buf, "{:.1}", errors as f64 / time)?,
|
|
||||||
Some(c) => write!(buf, "%{}", c)?,
|
|
||||||
None => write!(buf, "%")?,
|
|
||||||
}
|
|
||||||
match chars.next() {
|
|
||||||
Some((i, _)) => format = &format[i..],
|
|
||||||
None => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
write!(buf, "{}", format)?;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear(&self) {
|
|
||||||
self.servers.store(0, Relaxed);
|
|
||||||
self.server_add.store(0, Relaxed);
|
|
||||||
self.server_del.store(0, Relaxed);
|
|
||||||
self.query_servers.store(0, Relaxed);
|
|
||||||
self.errors.store(0, Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Stats {
|
|
||||||
enabled: bool,
|
|
||||||
tx: mpsc::Sender<StatConfig>,
|
|
||||||
counters: Arc<Counters>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stats {
|
|
||||||
pub fn new(mut config: StatConfig) -> Self {
|
|
||||||
let counters_ = Arc::new(Counters::default());
|
|
||||||
let (tx, rx) = mpsc::channel();
|
|
||||||
|
|
||||||
let enabled = config.interval != 0;
|
|
||||||
let counters = counters_.clone();
|
|
||||||
thread::spawn(move || -> fmt::Result {
|
|
||||||
let buf = &mut String::new();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if config.interval == 0 {
|
|
||||||
config = rx.recv().unwrap();
|
|
||||||
counters.clear();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let duration = Duration::from_secs(config.interval as u64);
|
|
||||||
let start = Instant::now();
|
|
||||||
if let Ok(new_config) = rx.recv_timeout(duration) {
|
|
||||||
config = new_config;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.clear();
|
|
||||||
counters.print(&config.format, buf, start.elapsed())?;
|
|
||||||
info!("{}", buf);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
enabled,
|
|
||||||
tx,
|
|
||||||
counters: counters_,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_config(&mut self, config: StatConfig) {
|
|
||||||
self.enabled = config.interval != 0;
|
|
||||||
self.tx.send(config).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&self) {
|
|
||||||
self.counters.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn servers_count(&self, n: usize) {
|
|
||||||
if self.enabled {
|
|
||||||
self.counters.servers.store(n, Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_server_add(&self) {
|
|
||||||
if self.enabled {
|
|
||||||
self.counters.server_add.fetch_add(1, Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_server_del(&self) {
|
|
||||||
if self.enabled {
|
|
||||||
self.counters.server_del.fetch_add(1, Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_query_servers(&self) {
|
|
||||||
if self.enabled {
|
|
||||||
self.counters.query_servers.fetch_add(1, Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_error(&self) {
|
|
||||||
if self.enabled {
|
|
||||||
self.counters.errors.fetch_add(1, Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
155
master/src/stats/imp.rs
Normal file
155
master/src/stats/imp.rs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
use std::fmt::{self, Write};
|
||||||
|
use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering::Relaxed};
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use crate::config::StatConfig;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Counters {
|
||||||
|
servers: AtomicUsize,
|
||||||
|
server_add: AtomicU32,
|
||||||
|
server_del: AtomicU32,
|
||||||
|
query_servers: AtomicU32,
|
||||||
|
errors: AtomicU32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Counters {
|
||||||
|
fn print(&self, mut format: &str, buf: &mut String, time: Duration) -> fmt::Result {
|
||||||
|
let time = time.as_secs_f64();
|
||||||
|
let servers = self.servers.load(Relaxed);
|
||||||
|
let server_add = self.server_add.swap(0, Relaxed);
|
||||||
|
let server_del = self.server_del.swap(0, Relaxed);
|
||||||
|
let query_servers = self.query_servers.swap(0, Relaxed);
|
||||||
|
let errors = self.errors.swap(0, Relaxed);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// TODO: precompile format string
|
||||||
|
match format.find('%').map(|i| format.split_at(i)) {
|
||||||
|
Some((head, tail)) => {
|
||||||
|
format = &tail[1..];
|
||||||
|
write!(buf, "{}", head)?;
|
||||||
|
let mut chars = format.char_indices();
|
||||||
|
match chars.next().map(|(_, c)| c) {
|
||||||
|
Some('s') => write!(buf, "{}", servers)?,
|
||||||
|
Some('A') => write!(buf, "{}", server_add)?,
|
||||||
|
Some('D') => write!(buf, "{}", server_del)?,
|
||||||
|
Some('Q') => write!(buf, "{}", query_servers)?,
|
||||||
|
Some('E') => write!(buf, "{}", errors)?,
|
||||||
|
Some('a') => write!(buf, "{:.1}", server_add as f64 / time)?,
|
||||||
|
Some('d') => write!(buf, "{:.1}", server_del as f64 / time)?,
|
||||||
|
Some('q') => write!(buf, "{:.1}", query_servers as f64 / time)?,
|
||||||
|
Some('e') => write!(buf, "{:.1}", errors as f64 / time)?,
|
||||||
|
Some(c) => write!(buf, "%{}", c)?,
|
||||||
|
None => write!(buf, "%")?,
|
||||||
|
}
|
||||||
|
match chars.next() {
|
||||||
|
Some((i, _)) => format = &format[i..],
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
write!(buf, "{}", format)?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear(&self) {
|
||||||
|
self.servers.store(0, Relaxed);
|
||||||
|
self.server_add.store(0, Relaxed);
|
||||||
|
self.server_del.store(0, Relaxed);
|
||||||
|
self.query_servers.store(0, Relaxed);
|
||||||
|
self.errors.store(0, Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Stats {
|
||||||
|
enabled: bool,
|
||||||
|
tx: mpsc::Sender<StatConfig>,
|
||||||
|
counters: Arc<Counters>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stats {
|
||||||
|
pub fn new(mut config: StatConfig) -> Self {
|
||||||
|
let counters_ = Arc::new(Counters::default());
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
|
let enabled = config.interval != 0;
|
||||||
|
let counters = counters_.clone();
|
||||||
|
thread::spawn(move || -> fmt::Result {
|
||||||
|
let buf = &mut String::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if config.interval == 0 {
|
||||||
|
config = rx.recv().unwrap();
|
||||||
|
counters.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let duration = Duration::from_secs(config.interval as u64);
|
||||||
|
let start = Instant::now();
|
||||||
|
if let Ok(new_config) = rx.recv_timeout(duration) {
|
||||||
|
config = new_config;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.clear();
|
||||||
|
counters.print(&config.format, buf, start.elapsed())?;
|
||||||
|
info!("{}", buf);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
enabled,
|
||||||
|
tx,
|
||||||
|
counters: counters_,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_config(&mut self, config: StatConfig) {
|
||||||
|
self.enabled = config.interval != 0;
|
||||||
|
self.tx.send(config).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&self) {
|
||||||
|
self.counters.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn servers_count(&self, n: usize) {
|
||||||
|
if self.enabled {
|
||||||
|
self.counters.servers.store(n, Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_server_add(&self) {
|
||||||
|
if self.enabled {
|
||||||
|
self.counters.server_add.fetch_add(1, Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_server_del(&self) {
|
||||||
|
if self.enabled {
|
||||||
|
self.counters.server_del.fetch_add(1, Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_query_servers(&self) {
|
||||||
|
if self.enabled {
|
||||||
|
self.counters.query_servers.fetch_add(1, Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_error(&self) {
|
||||||
|
if self.enabled {
|
||||||
|
self.counters.errors.fetch_add(1, Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
master/src/stats/stub.rs
Normal file
17
master/src/stats/stub.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use crate::config::StatConfig;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Counters;
|
||||||
|
|
||||||
|
pub struct Stats;
|
||||||
|
|
||||||
|
impl Stats {
|
||||||
|
pub fn new(_: StatConfig) -> Self { Self }
|
||||||
|
pub fn update_config(&mut self, _: StatConfig) {}
|
||||||
|
pub fn clear(&self) {}
|
||||||
|
pub fn servers_count(&self, _: usize) {}
|
||||||
|
pub fn on_server_add(&self) {}
|
||||||
|
pub fn on_server_del(&self) {}
|
||||||
|
pub fn on_query_servers(&self) {}
|
||||||
|
pub fn on_error(&self) {}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user