diff --git a/crates/kcli/src/top/factories.rs b/crates/kcli/src/top/factories.rs index 594ca2d5..7d570d1d 100644 --- a/crates/kcli/src/top/factories.rs +++ b/crates/kcli/src/top/factories.rs @@ -1,5 +1,5 @@ use crate::top::accumulator::*; -use crate::top::{SeriesChartOptions, SeriesFactory, TimeSeries}; +use crate::top::{Histogram, HistogramFactory, SeriesChartOptions, SeriesFactory, TimeSeries}; use kumo_prometheus::parser::Metric; pub struct ThreadPoolFactory {} @@ -121,3 +121,39 @@ impl SeriesFactory for HistogramEventAvgFactory { series } } + +pub struct HistogramHistoFactory {} + +impl HistogramFactory for HistogramHistoFactory { + fn matches(&self, metric: &Metric) -> Option { + if metric.is_histogram() { + let h = metric.as_histogram(); + + match h.labels.values().next() { + Some(l) => { + let label = l.as_str(); + if label == "init" || label == "pre_init" { + return None; + } + Some(format!("{} - {label}", h.name.as_str())) + } + None => Some(h.name.as_str().to_string()), + } + } else { + None + } + } + + fn factory(&self, _series_name: &str, metric: &Metric) -> Histogram { + let h = metric.as_histogram(); + match h.labels.iter().next() { + Some((key, value)) => Histogram::new_with_label_match( + metric.name().to_string(), + key.to_string(), + value.to_string(), + "s", + ), + None => Histogram::new(metric.name().to_string(), "s"), + } + } +} diff --git a/crates/kcli/src/top/histogram.rs b/crates/kcli/src/top/histogram.rs index 396726d8..173b3976 100644 --- a/crates/kcli/src/top/histogram.rs +++ b/crates/kcli/src/top/histogram.rs @@ -72,3 +72,10 @@ impl Histogram { } } } + +pub trait HistogramFactory { + /// Returns the name that should be created for this metric + fn matches(&self, metric: &Metric) -> Option; + /// Constructs the appropriate histogram for this metric + fn factory(&self, series_name: &str, metric: &Metric) -> Histogram; +} diff --git a/crates/kcli/src/top/mod.rs b/crates/kcli/src/top/mod.rs index 6152ee09..29b58851 100644 --- a/crates/kcli/src/top/mod.rs +++ b/crates/kcli/src/top/mod.rs @@ -50,6 +50,8 @@ impl TopCommand { state.add_factory(HistogramEventFreqFactory {}); state.add_factory(HistogramEventAvgFactory {}); + state.add_histogram_factory(HistogramHistoFactory {}); + state.add_series( "message_count", TimeSeries::new(DirectAccumulator::new("message_count")), @@ -144,21 +146,6 @@ impl TopCommand { )), ); - state.add_histogram( - "Inbound SMTP Transaction Duration", - Histogram::new("smtpsrv_transaction_duration", "s"), - ); - - state.add_histogram( - "Inbound SMTP Data Receive Latency", - Histogram::new("smtpsrv_read_data_duration", "s"), - ); - - state.add_histogram( - "Inbound SMTP Data Process Latency", - Histogram::new("smtpsrv_process_data_duration", "s"), - ); - let mut ticker = tokio::time::interval(Duration::from_secs(self.update_interval)); ticker.set_missed_tick_behavior(MissedTickBehavior::Skip); diff --git a/crates/kcli/src/top/state.rs b/crates/kcli/src/top/state.rs index d06f6124..fe001c52 100644 --- a/crates/kcli/src/top/state.rs +++ b/crates/kcli/src/top/state.rs @@ -84,6 +84,7 @@ pub struct State { time_series: HashMap, histograms: HashMap, factories: Vec>, + histo_factories: Vec>, error: String, vert_scroll: ScrollbarState, vert_scroll_position: usize, @@ -96,6 +97,10 @@ impl State { self.factories.push(Box::new(f)); } + pub fn add_histogram_factory(&mut self, f: impl HistogramFactory + 'static) { + self.histo_factories.push(Box::new(f)); + } + fn accumulate_series(&mut self, metric: &Metric) { let mut new_series = vec![]; for factory in &self.factories { @@ -114,6 +119,19 @@ impl State { series.accumulate(metric); } + let mut new_histo = vec![]; + for factory in &self.histo_factories { + if let Some(name) = factory.matches(metric) { + if !self.histograms.contains_key(&name) { + let histogram = factory.factory(&name, metric); + new_histo.push((name, histogram)); + } + } + } + for (name, histo) in new_histo { + self.add_histogram(name, histo); + } + for histo in self.histograms.values_mut() { histo.accumulate(metric); } @@ -413,8 +431,12 @@ impl State { .vert_scroll_position .min(content_length.saturating_sub(1)); + let mut names: Vec<_> = self.histograms.keys().map(|s| s.to_string()).collect(); + names.sort(); + let mut y = area.top(); - for (caption, histo) in self.histograms.iter().skip(vert_scroll_position) { + for caption in names.iter().skip(vert_scroll_position) { + let histo = self.histograms.get(caption).expect("caption to be valid"); let heatmap = HeatMap::new(histo, caption).block(Block::bordered()); let height = heatmap.height() + 2 /* borders */;