cache sparklines

This commit is contained in:
Robin Appelman 2024-07-29 20:45:02 +02:00
commit 5b1ae14639
7 changed files with 46 additions and 41 deletions

View file

@ -37,6 +37,7 @@ pub struct LogMatch {
pub result: Option<MatchResult>,
pub lines: Vec<usize>,
pub histogram: TimeGraph,
pub sparkline: String,
pub grouped: Vec<GroupedLines>,
}
@ -49,11 +50,13 @@ impl LogMatch {
histogram.add(line.time);
}
let grouped = group_lines(all_lines, lines.iter().copied());
let sparkline = histogram.sparkline::<10>();
LogMatch {
result,
lines,
histogram,
sparkline,
grouped,
}
}
@ -84,6 +87,7 @@ fn group_lines<I: Iterator<Item = usize>>(all_lines: &[LogLine], indices: I) ->
pub struct GroupedLines {
pub lines: Vec<usize>,
pub histogram: TimeGraph,
pub sparkline: String,
}
impl GroupedLines {
@ -94,7 +98,12 @@ impl GroupedLines {
for line in lines.iter().map(|line| &all_lines[*line]) {
histogram.add(line.time);
}
GroupedLines { lines, histogram }
let sparkline = histogram.sparkline::<10>();
GroupedLines {
lines,
histogram,
sparkline,
}
}
pub fn len(&self) -> usize {

View file

@ -28,12 +28,30 @@ impl TimeGraph {
.unwrap()
}
pub fn counts(&self, buckets: usize) -> Vec<u64> {
pub fn counts(&self, buckets: usize) -> impl Iterator<Item = u64> + '_ {
let step = (self.end - self.start + 1) / buckets as u64;
self.histogram
.iter_linear(step)
.map(|val| val.count_since_last_iteration())
}
pub fn sparkline<const N: usize>(&self) -> String {
let mut values = [0; N];
for (value, count) in values.iter_mut().zip(self.counts(N)) {
*value = count;
}
let max = values.iter().copied().max().unwrap() as f64;
let len = SPARKS.len() as f64 - 1.0;
values
.iter()
.copied()
.map(|val| {
let rel = val as f64 / max;
SPARKS[(rel * len) as usize]
})
.collect()
}
}
const SPARKS: &[char] = &[' ', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];

View file

@ -17,28 +17,8 @@ impl Widget for UiHistogram<'_> {
where
Self: Sized,
{
let values = self.data.counts(area.width as usize);
let values: Vec<_> = self.data.counts(area.width as usize).collect();
let sparkline = Sparkline::default().data(&values);
sparkline.render(area, buf)
}
}
const SPARKS: &[char] = &[' ', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
pub fn sparkline(values: &[u64]) -> String {
let max = values.iter().copied().max().unwrap() as f64;
let len = SPARKS.len() as f64 - 1.0;
values
.iter()
.copied()
.map(|val| {
let rel = val as f64 / max;
SPARKS[(rel * len) as usize]
})
.collect()
}
#[test]
fn test_sparkline() {
assert_eq!(" ▇█", sparkline(&[0, 900, 1000]));
}

View file

@ -1,5 +1,4 @@
use crate::app::{App, LogMatch};
use crate::ui::histogram::sparkline;
use crate::ui::style::TABLE_HEADER_STYLE;
use crate::ui::table::ScrollbarTable;
use itertools::Either;
@ -46,7 +45,7 @@ pub fn match_list(app: &App) -> ScrollbarTable {
.header(header)
}
fn log_row<'a>(result: &LogMatch, app: &'a App, name: &'static str) -> Row<'a> {
fn log_row<'a>(result: &'a LogMatch, app: &'a App, name: &'static str) -> Row<'a> {
if let Some(match_result) = &result.result {
let mut message = String::new();
let mut paths = String::new();
@ -61,7 +60,7 @@ fn log_row<'a>(result: &LogMatch, app: &'a App, name: &'static str) -> Row<'a> {
Text::from(message),
Text::from(paths),
Text::from(lines).alignment(Alignment::Right),
Text::from(sparkline(&result.histogram.counts(10))),
Text::from(result.sparkline.as_str()),
Text::from(result.count().to_string()),
])
.height(match_result.len() as u16)
@ -70,7 +69,7 @@ fn log_row<'a>(result: &LogMatch, app: &'a App, name: &'static str) -> Row<'a> {
Text::from(name),
Text::from(""),
Text::from(""),
Text::from(sparkline(&result.histogram.counts(10))),
Text::from(result.sparkline.as_str()),
Text::from(result.count().to_string()),
])
}

View file

@ -7,7 +7,7 @@ use ratatui::text::Text;
use ratatui::widgets::{Cell, Row};
use time::format_description::well_known::Iso8601;
pub fn raw_logs(app: &App, lines: &[usize]) -> ScrollbarTable<'static> {
pub fn raw_logs<'a>(app: &'a App, lines: &[usize]) -> ScrollbarTable<'a> {
let lines = lines.iter().copied().map(|i| &app.lines[i]);
let header = [
Text::from("Level"),
@ -30,10 +30,10 @@ pub fn raw_logs(app: &App, lines: &[usize]) -> ScrollbarTable<'static> {
ScrollbarTable::new(lines.map(log_row), widths).header(header)
}
fn log_row(line: &LogLine) -> Row<'static> {
fn log_row(line: &LogLine) -> Row {
Row::new([
Text::from(line.level.as_str().to_string()),
Text::from(line.app.to_string()),
Text::from(line.level.as_str()),
Text::from(line.app.as_str()),
Text::from(line.display()),
Text::from(line.time.format(&Iso8601::<TIME_FORMAT>).unwrap()).alignment(Alignment::Right),
])

View file

@ -74,7 +74,7 @@ fn exception_trace(exception: &FullException) -> impl Iterator<Item = Row> + '_
let exception_row = Row::new([
Text::from(""),
Text::from(exception.line.to_string()).alignment(Alignment::Right),
Text::from(exception.file.clone()),
Text::from(exception.file.as_str()),
])
.style(TABLE_HEADER_STYLE);
let trace_rows = exception.trace.iter().map(trace_line);
@ -83,7 +83,7 @@ fn exception_trace(exception: &FullException) -> impl Iterator<Item = Row> + '_
fn trace_line(trace: &Trace) -> Row {
Row::new([
Text::from(trace.file.clone()),
Text::from(trace.file.as_str()),
Text::from(if trace.line > 0 {
trace.line.to_string()
} else {

View file

@ -1,12 +1,11 @@
use crate::app::{App, GroupedLines, LogMatch};
use crate::ui::histogram::sparkline;
use crate::ui::style::TABLE_HEADER_STYLE;
use crate::ui::table::ScrollbarTable;
use ratatui::layout::Constraint;
use ratatui::text::Text;
use ratatui::widgets::{Cell, Row};
pub fn grouped_lines(app: &App, log_match: &LogMatch) -> ScrollbarTable<'static> {
pub fn grouped_lines<'a>(app: &'a App, log_match: &'a LogMatch) -> ScrollbarTable<'a> {
let grouped = &log_match.grouped;
let header = [
Text::from("Level"),
@ -31,14 +30,14 @@ pub fn grouped_lines(app: &App, log_match: &LogMatch) -> ScrollbarTable<'static>
ScrollbarTable::new(grouped.iter().map(|group| group_row(app, group)), widths).header(header)
}
fn group_row(app: &App, group: &GroupedLines) -> Row<'static> {
fn group_row<'a>(app: &'a App, group: &'a GroupedLines) -> Row<'a> {
let line = &app.lines[group.lines[0]];
Row::new([
line.level.as_str().to_string(),
line.app.to_string(),
line.display(),
sparkline(&group.histogram.counts(10)),
group.len().to_string(),
Text::from(line.level.as_str()),
Text::from(line.app.as_str()),
Text::from(line.display()),
Text::from(group.sparkline.as_str()),
Text::from(group.len().to_string()),
])
}