diff --git a/bare-metrics-gui/src/config.rs b/bare-metrics-gui/src/config.rs index 974ebbe..b988b45 100644 --- a/bare-metrics-gui/src/config.rs +++ b/bare-metrics-gui/src/config.rs @@ -1,13 +1,31 @@ use bare_metrics_core::structures::MetricKind; use serde::Deserialize; +use std::collections::BTreeMap; #[derive(Deserialize, Default, Clone)] pub struct DashboardConfig { pub graphs: Vec, } +#[derive(Deserialize, Clone)] +pub enum MetricTransform { + Rate { + // Divisor in seconds. Would usually expect to see 1 (/sec), 60 (/min) or 3600 (/hour). + time_unit: f32, + }, +} + #[derive(Deserialize, Clone)] pub struct GraphConfig { pub name: String, pub kind: MetricKind, + #[serde(default)] + pub transform: Option, +} + +#[derive(Clone, Debug, Hash)] +pub struct GraphRequest { + pub metric_name: String, + pub metric_labels: BTreeMap, + pub derivation: Option, } diff --git a/bare-metrics-gui/src/graph.rs b/bare-metrics-gui/src/graph.rs index e4a9af7..ac84894 100644 --- a/bare-metrics-gui/src/graph.rs +++ b/bare-metrics-gui/src/graph.rs @@ -1,26 +1,27 @@ +use crate::config::MetricTransform; use anyhow::{anyhow, bail}; use bare_metrics_core::structures::{ Frame, MetricDescriptor, MetricId, MetricKind, UnixTimestampMilliseconds, }; use bare_metrics_reader::{MetricsLogReader, SeekToken}; use eframe::egui::{ - Color32, Frame as EguiFrame, Galley, PointerButton, Pos2, Rect, Sense, Stroke, TextStyle, Ui, - Vec2, + Color32, Frame as EguiFrame, PointerButton, Pos2, Rect, Sense, Stroke, TextStyle, Ui, Vec2, }; use hdrhistogram::Histogram; use log::{debug, error, info}; use std::collections::{BTreeMap, HashMap}; use std::io::{Read, Seek}; -use std::ops::{Mul, RangeInclusive}; +use std::ops::{DerefMut, Mul, RangeInclusive}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{Receiver, Sender}; -use std::sync::{Arc, RwLock}; -use std::time::SystemTime; +use std::sync::{Arc, Mutex, RwLock}; /// Make a checkpoint every 10 minutes? /// This should probably be tunable since it will likely vary per-source ... pub const CHECKPOINT_EVERY_MILLISECONDS: u64 = 600_000_000; +pub type StateCallbackHook = Arc>>>; + #[derive(Clone, Debug)] pub struct MetricsWindow { pub time_range: RangeInclusive, @@ -157,6 +158,7 @@ pub enum MetricsLogReaderMessage { impl MetricsLogReadingRequester { pub fn new_manager( reader: MetricsLogReader, + on_initial_scan_done: StateCallbackHook, ) -> MetricsLogReadingRequester { let shared = Arc::new(MetricsLogReadingShared { current_window: RwLock::new(MetricsWindow { @@ -177,9 +179,12 @@ impl MetricsLogReadingRequester { std::thread::Builder::new() .name("metricslogreader".to_string()) .spawn(move || { - if let Err(err) = - MetricsLogReaderManager::new_and_run(manager_shared_ref, reader, rx) - { + if let Err(err) = MetricsLogReaderManager::new_and_run( + manager_shared_ref, + reader, + rx, + on_initial_scan_done, + ) { error!("Error in background log reader: {:?}", err); } }) @@ -554,11 +559,19 @@ impl MetricsLogReaderManager { Ok(metrics_window) } - fn run(&mut self) -> anyhow::Result<()> { + fn run(&mut self, on_initial_scan_done: StateCallbackHook) -> anyhow::Result<()> { info!("Starting manager"); self.initial_scan()?; self.update_metric_descriptors(); info!("Initial scan done."); + let mut the_func = None; + let mut callback_guard = on_initial_scan_done + .lock() + .map_err(|_| anyhow!("Can't lock"))?; + std::mem::swap(&mut the_func, callback_guard.deref_mut()); + if let Some(on_initial_scan_done) = the_func { + on_initial_scan_done(); + } while let Ok(msg) = self.rx.recv() { match msg { @@ -637,6 +650,7 @@ impl MetricsLogReaderManager { shared_ref: Arc, reader: MetricsLogReader, rx: Receiver, + on_initial_scan_done: StateCallbackHook, ) -> anyhow::Result<()> { let mut manager = MetricsLogReaderManager { shared_ref, @@ -648,7 +662,7 @@ impl MetricsLogReaderManager { checkpoints: Default::default(), }; - manager.run() + manager.run(on_initial_scan_done) } } diff --git a/bare-metrics-gui/src/main.rs b/bare-metrics-gui/src/main.rs index 6a97e98..93da5d0 100644 --- a/bare-metrics-gui/src/main.rs +++ b/bare-metrics-gui/src/main.rs @@ -82,7 +82,9 @@ fn main() -> anyhow::Result<()> { let reader = MetricsLogReader::new(file).unwrap(); let log_app_name = reader.header.application_name.clone(); - let requester = MetricsLogReadingRequester::new_manager(reader); + // TODO we can attach a callback to this hook. + let init_hook_callback = Default::default(); + let requester = MetricsLogReadingRequester::new_manager(reader, init_hook_callback); let dashboard_dir = appdirs::user_data_dir(Some("baremetrics"), Some("rei"), true) .unwrap()