#![doc = include_str!("../README.md")]
use std::fs::File;
use std::io::Write;
use std::sync::Mutex;
pub mod moments;
mod sensors;
pub use sensors::Counter;
pub use sensors::Gauge;
pub use sensors::Moments;
pub trait Sensor {
type Reading;
fn label(&'static self) -> &'static str;
fn read(&'static self) -> Self::Reading;
}
struct SensorRegistry<S: Sensor + 'static> {
sensors: Mutex<Vec<&'static S>>,
register: &'static Counter,
emit: &'static Counter,
err: &'static Counter,
}
impl<S: Sensor + 'static> SensorRegistry<S> {
pub fn new(register: &'static Counter, emit: &'static Counter, err: &'static Counter) -> Self {
Self {
sensors: Mutex::new(Vec::new()),
register,
emit,
err,
}
}
pub fn register(&self, sensor: &'static S) {
{
let mut sensors = self.sensors.lock().unwrap();
sensors.push(sensor);
}
self.register.click();
}
fn emit<EM: Emitter<Error = ERR>, ERR>(
&self,
emitter: &mut EM,
emit: &dyn Fn(&mut EM, &'static S, u64) -> Result<(), ERR>,
now: u64,
) -> Result<(), ERR> {
let num_sensors = { self.sensors.lock().unwrap().len() };
let mut sensors: Vec<&'static S> = Vec::with_capacity(num_sensors);
{
let sensors_guard = self.sensors.lock().unwrap();
for s in sensors_guard.iter() {
sensors.push(*s);
}
}
let mut result = Ok(());
for sensor in sensors {
match emit(emitter, sensor, now) {
Ok(_) => self.emit.click(),
Err(e) => {
if let Ok(()) = result {
result = Err(e);
}
self.err.click();
}
}
}
result
}
}
pub struct Collector {
counters: SensorRegistry<Counter>,
gauges: SensorRegistry<Gauge>,
moments: SensorRegistry<Moments>,
}
static COLLECTOR_REGISTER_COUNTER: Counter = Counter::new("biometrics.collector.register.counter");
static COLLECTOR_REGISTER_GAUGE: Counter = Counter::new("biometrics.collector.register.gauge");
static COLLECTOR_REGISTER_MOMENTS: Counter = Counter::new("biometrics.collector.register.moments");
static COLLECTOR_EMIT_COUNTER: Counter = Counter::new("biometrics.collector.emit.counter");
static COLLECTOR_EMIT_GAUGE: Counter = Counter::new("biometrics.collector.emit.gauge");
static COLLECTOR_EMIT_MOMENTS: Counter = Counter::new("biometrics.collector.emit.moments");
static COLLECTOR_EMIT_FAILURE: Counter = Counter::new("biometrics.collector.emit.failure");
static COLLECTOR_TIME_FAILURE: Counter = Counter::new("biometrics.collector.time.failure");
impl Collector {
pub fn new() -> Self {
let collector = Self {
counters: SensorRegistry::new(
&COLLECTOR_REGISTER_COUNTER,
&COLLECTOR_EMIT_COUNTER,
&COLLECTOR_EMIT_FAILURE,
),
gauges: SensorRegistry::new(
&COLLECTOR_REGISTER_GAUGE,
&COLLECTOR_EMIT_GAUGE,
&COLLECTOR_EMIT_FAILURE,
),
moments: SensorRegistry::new(
&COLLECTOR_REGISTER_MOMENTS,
&COLLECTOR_EMIT_MOMENTS,
&COLLECTOR_EMIT_FAILURE,
),
};
collector.register_counter(&COLLECTOR_REGISTER_COUNTER);
collector.register_counter(&COLLECTOR_REGISTER_GAUGE);
collector.register_counter(&COLLECTOR_REGISTER_MOMENTS);
collector.register_counter(&COLLECTOR_EMIT_COUNTER);
collector.register_counter(&COLLECTOR_EMIT_GAUGE);
collector.register_counter(&COLLECTOR_EMIT_MOMENTS);
collector.register_counter(&COLLECTOR_EMIT_FAILURE);
collector.register_counter(&COLLECTOR_TIME_FAILURE);
collector
}
pub fn register_counter(&self, counter: &'static Counter) {
self.counters.register(counter);
}
pub fn register_gauge(&self, gauge: &'static Gauge) {
self.gauges.register(gauge);
}
pub fn register_moments(&self, moments: &'static Moments) {
self.moments.register(moments);
}
pub fn emit<EM: Emitter<Error = ERR>, ERR: std::fmt::Debug>(
&self,
emitter: &mut EM,
now: u64,
) -> Result<(), ERR> {
let result = Ok(());
let result = result.and(self.counters.emit(emitter, &EM::emit_counter, now));
let result = result.and(self.gauges.emit(emitter, &EM::emit_gauge, now));
result.and(self.moments.emit(emitter, &EM::emit_moments, now))
}
}
impl Default for Collector {
fn default() -> Self {
Self::new()
}
}
pub trait Emitter {
type Error;
fn emit_counter(
&mut self,
counter: &'static Counter,
now_millis: u64,
) -> Result<(), Self::Error>;
fn emit_gauge(&mut self, gauge: &'static Gauge, now_millis: u64) -> Result<(), Self::Error>;
fn emit_moments(
&mut self,
moments: &'static Moments,
now_millis: u64,
) -> Result<(), Self::Error>;
}
pub struct PlainTextEmitter {
output: File,
}
impl PlainTextEmitter {
pub fn new(output: File) -> Self {
Self { output }
}
}
impl Emitter for PlainTextEmitter {
type Error = std::io::Error;
fn emit_counter(&mut self, counter: &'static Counter, now: u64) -> Result<(), std::io::Error> {
self.output.write_fmt(format_args!(
"{} {} {}\n",
counter.label(),
now,
counter.read()
))
}
fn emit_gauge(&mut self, gauge: &'static Gauge, now: u64) -> Result<(), std::io::Error> {
self.output
.write_fmt(format_args!("{} {} {}\n", gauge.label(), now, gauge.read()))
}
fn emit_moments(&mut self, moments: &'static Moments, now: u64) -> Result<(), std::io::Error> {
let label = moments.label();
let moments = moments.read();
self.output.write_fmt(format_args!(
"{} {} {} {} {} {} {}\n",
label, now, moments.n, moments.m1, moments.m2, moments.m3, moments.m4,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn collector_new() {
let _: Collector = Collector::new();
}
}