lazy histograms

This commit is contained in:
Robin Appelman 2025-03-26 18:13:07 +01:00
commit 8299a90578
4 changed files with 56 additions and 37 deletions

View file

@ -3,6 +3,7 @@ use crate::matcher::MatchResult;
use crate::timegraph::TimeGraph; use crate::timegraph::TimeGraph;
use logsmash_data::{LoggingStatementWithPathPrefix, StatementList}; use logsmash_data::{LoggingStatementWithPathPrefix, StatementList};
use regex::{escape, Regex, RegexBuilder}; use regex::{escape, Regex, RegexBuilder};
use std::cell::OnceCell;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::Display; use std::fmt::Display;
@ -42,39 +43,46 @@ impl<'a> App<'a> {
pub struct LogMatch { pub struct LogMatch {
pub result: Option<MatchResult>, pub result: Option<MatchResult>,
pub count: usize, pub count: usize,
pub histogram: TimeGraph, pub histogram: OnceCell<TimeGraph>,
pub sparkline: String, pub sparkline: OnceCell<String>,
pub all: GroupedLines, pub all: GroupedLines,
pub grouped: Vec<GroupedLines>, pub grouped: Vec<GroupedLines>,
} }
impl LogMatch { impl LogMatch {
pub fn new(result: Option<MatchResult>, lines: Vec<usize>, all_lines: &[LogLine]) -> Self { pub fn new(result: Option<MatchResult>, lines: Vec<usize>, all_lines: &[LogLine]) -> Self {
let min_time = all_lines[0].time;
let max_time = all_lines.last().unwrap().time;
let mut histogram = TimeGraph::new(min_time, max_time);
for line in lines.iter().map(|line| &all_lines[*line]) {
histogram.add(line.time);
}
let count = lines.len(); let count = lines.len();
let grouped = group_lines(all_lines, lines.iter().copied()); let grouped = group_lines(all_lines, lines.iter().copied());
let sparkline = histogram.sparkline::<10>(); let all = GroupedLines::new(lines);
let all = GroupedLines {
sparkline: sparkline.clone(),
histogram: histogram.clone(),
lines,
};
LogMatch { LogMatch {
result, result,
count, count,
histogram, histogram: OnceCell::new(),
sparkline, sparkline: OnceCell::new(),
grouped, grouped,
all, all,
} }
} }
pub fn sparkline(&self, app: &App) -> &str {
self.sparkline
.get_or_init(|| self.histogram(app).sparkline::<10>())
.as_str()
}
pub fn histogram(&self, app: &App) -> &TimeGraph {
self.histogram.get_or_init(|| {
let min_time = app.lines[0].time;
let max_time = app.lines.last().unwrap().time;
let mut histogram = TimeGraph::new(min_time, max_time);
for line in self.all.lines.iter().map(|line| &app.lines[*line]) {
histogram.add(line.time);
}
histogram
})
}
pub fn row_count(&self) -> usize { pub fn row_count(&self) -> usize {
self.result.as_ref().map(|res| res.len()).unwrap_or(1) self.result.as_ref().map(|res| res.len()).unwrap_or(1)
} }
@ -126,7 +134,7 @@ fn group_lines<I: Iterator<Item = usize>>(all_lines: &[LogLine], indices: I) ->
let mut list: Vec<_> = map let mut list: Vec<_> = map
.into_values() .into_values()
.map(|lines| GroupedLines::new(lines, all_lines)) .map(|lines| GroupedLines::new(lines))
.collect(); .collect();
list.sort_by_key(|list| list.len()); list.sort_by_key(|list| list.len());
list.reverse(); list.reverse();
@ -135,26 +143,37 @@ fn group_lines<I: Iterator<Item = usize>>(all_lines: &[LogLine], indices: I) ->
pub struct GroupedLines { pub struct GroupedLines {
pub lines: Vec<usize>, pub lines: Vec<usize>,
pub histogram: TimeGraph, pub histogram: OnceCell<TimeGraph>,
pub sparkline: String, pub sparkline: OnceCell<String>,
} }
impl GroupedLines { impl GroupedLines {
pub fn new(lines: Vec<usize>, all_lines: &[LogLine]) -> Self { pub fn new(lines: Vec<usize>) -> Self {
let min_time = all_lines[0].time;
let max_time = all_lines.last().unwrap().time;
let mut histogram = TimeGraph::new(min_time, max_time);
for line in lines.iter().map(|line| &all_lines[*line]) {
histogram.add(line.time);
}
let sparkline = histogram.sparkline::<10>();
GroupedLines { GroupedLines {
lines, lines,
histogram, histogram: OnceCell::new(),
sparkline, sparkline: OnceCell::new(),
} }
} }
pub fn sparkline(&self, app: &App) -> &str {
self.sparkline
.get_or_init(|| self.histogram(app).sparkline::<10>())
.as_str()
}
pub fn histogram(&self, app: &App) -> &TimeGraph {
self.histogram.get_or_init(|| {
let min_time = app.lines[0].time;
let max_time = app.lines.last().unwrap().time;
let mut histogram = TimeGraph::new(min_time, max_time);
for line in self.lines.iter().map(|line| &app.lines[*line]) {
histogram.add(line.time);
}
histogram
})
}
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.lines.len() self.lines.len()
} }

View file

@ -65,7 +65,7 @@ fn log_row<'a>(result: &'a LogMatch, app: &'a App, name: &'static str) -> Row<'a
Text::from(message), Text::from(message),
Text::from(paths), Text::from(paths),
Text::from(lines).alignment(Alignment::Right), Text::from(lines).alignment(Alignment::Right),
Text::from(result.sparkline.as_str()), Text::from(result.sparkline(app)),
Text::from(result.count().to_string()), Text::from(result.count().to_string()),
]) ])
.height(match_result.len() as u16) .height(match_result.len() as u16)
@ -74,7 +74,7 @@ fn log_row<'a>(result: &'a LogMatch, app: &'a App, name: &'static str) -> Row<'a
Text::from(name), Text::from(name),
Text::from(""), Text::from(""),
Text::from(""), Text::from(""),
Text::from(result.sparkline.as_str()), Text::from(result.sparkline(app)),
Text::from(result.count().to_string()), Text::from(result.count().to_string()),
]) ])
} }

View file

@ -112,12 +112,12 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
}) => { }) => {
let selected = table_state.selected(); let selected = table_state.selected();
let histogram = if selected == 0 { let histogram = if selected == 0 {
&app.all.histogram app.all.histogram(app)
} else if selected < app.matches.len() + 1 { } else if selected < app.matches.len() + 1 {
let log_match = &app.matches[selected - 1]; let log_match = &app.matches[selected - 1];
&log_match.histogram log_match.histogram(app)
} else { } else {
&app.unmatched.histogram app.unmatched.histogram(app)
}; };
frame.render_widget(UiHistogram::new(histogram), layout[0]); frame.render_widget(UiHistogram::new(histogram), layout[0]);
@ -137,7 +137,7 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
&result.grouped[selected - 1] &result.grouped[selected - 1]
}; };
frame.render_widget(UiHistogram::new(&selected_group.histogram), layout[0]); frame.render_widget(UiHistogram::new(&selected_group.histogram(app)), layout[0]);
frame.render_stateful_widget( frame.render_stateful_widget(
grouped_lines(app, result, filter), grouped_lines(app, result, filter),
layout[1], layout[1],

View file

@ -37,7 +37,7 @@ pub fn grouped_lines<'a>(
Text::from("All lines"), Text::from("All lines"),
Text::from(""), Text::from(""),
Text::from(""), Text::from(""),
Text::from(log_match.sparkline.as_str()), Text::from(log_match.sparkline(app)),
Text::from(log_match.count().to_string()), Text::from(log_match.count().to_string()),
])) ]))
.chain( .chain(
@ -58,7 +58,7 @@ fn group_row<'a>(app: &'a App, group: &'a GroupedLines) -> Row<'a> {
Text::from(line.level.as_str()), Text::from(line.level.as_str()),
Text::from(line.app.as_ref()), Text::from(line.app.as_ref()),
Text::from(line.display()), Text::from(line.display()),
Text::from(group.sparkline.as_str()), Text::from(group.sparkline(app)),
Text::from(group.len().to_string()), Text::from(group.len().to_string()),
]) ])
} }