mirror of
https://codeberg.org/icewind/logsmash.git
synced 2026-06-03 18:14:11 +02:00
cache sparklines
This commit is contained in:
parent
5d4a17f289
commit
5b1ae14639
7 changed files with 46 additions and 41 deletions
11
src/app.rs
11
src/app.rs
|
|
@ -37,6 +37,7 @@ pub struct LogMatch {
|
||||||
pub result: Option<MatchResult>,
|
pub result: Option<MatchResult>,
|
||||||
pub lines: Vec<usize>,
|
pub lines: Vec<usize>,
|
||||||
pub histogram: TimeGraph,
|
pub histogram: TimeGraph,
|
||||||
|
pub sparkline: String,
|
||||||
pub grouped: Vec<GroupedLines>,
|
pub grouped: Vec<GroupedLines>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,11 +50,13 @@ impl LogMatch {
|
||||||
histogram.add(line.time);
|
histogram.add(line.time);
|
||||||
}
|
}
|
||||||
let grouped = group_lines(all_lines, lines.iter().copied());
|
let grouped = group_lines(all_lines, lines.iter().copied());
|
||||||
|
let sparkline = histogram.sparkline::<10>();
|
||||||
|
|
||||||
LogMatch {
|
LogMatch {
|
||||||
result,
|
result,
|
||||||
lines,
|
lines,
|
||||||
histogram,
|
histogram,
|
||||||
|
sparkline,
|
||||||
grouped,
|
grouped,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -84,6 +87,7 @@ 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: TimeGraph,
|
||||||
|
pub sparkline: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GroupedLines {
|
impl GroupedLines {
|
||||||
|
|
@ -94,7 +98,12 @@ impl GroupedLines {
|
||||||
for line in lines.iter().map(|line| &all_lines[*line]) {
|
for line in lines.iter().map(|line| &all_lines[*line]) {
|
||||||
histogram.add(line.time);
|
histogram.add(line.time);
|
||||||
}
|
}
|
||||||
GroupedLines { lines, histogram }
|
let sparkline = histogram.sparkline::<10>();
|
||||||
|
GroupedLines {
|
||||||
|
lines,
|
||||||
|
histogram,
|
||||||
|
sparkline,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,30 @@ impl TimeGraph {
|
||||||
.unwrap()
|
.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;
|
let step = (self.end - self.start + 1) / buckets as u64;
|
||||||
|
|
||||||
self.histogram
|
self.histogram
|
||||||
.iter_linear(step)
|
.iter_linear(step)
|
||||||
.map(|val| val.count_since_last_iteration())
|
.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()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SPARKS: &[char] = &[' ', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
|
||||||
|
|
|
||||||
|
|
@ -17,28 +17,8 @@ impl Widget for UiHistogram<'_> {
|
||||||
where
|
where
|
||||||
Self: Sized,
|
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);
|
let sparkline = Sparkline::default().data(&values);
|
||||||
sparkline.render(area, buf)
|
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]));
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::app::{App, LogMatch};
|
use crate::app::{App, LogMatch};
|
||||||
use crate::ui::histogram::sparkline;
|
|
||||||
use crate::ui::style::TABLE_HEADER_STYLE;
|
use crate::ui::style::TABLE_HEADER_STYLE;
|
||||||
use crate::ui::table::ScrollbarTable;
|
use crate::ui::table::ScrollbarTable;
|
||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
|
|
@ -46,7 +45,7 @@ pub fn match_list(app: &App) -> ScrollbarTable {
|
||||||
.header(header)
|
.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 {
|
if let Some(match_result) = &result.result {
|
||||||
let mut message = String::new();
|
let mut message = String::new();
|
||||||
let mut paths = 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(message),
|
||||||
Text::from(paths),
|
Text::from(paths),
|
||||||
Text::from(lines).alignment(Alignment::Right),
|
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()),
|
Text::from(result.count().to_string()),
|
||||||
])
|
])
|
||||||
.height(match_result.len() as u16)
|
.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(name),
|
||||||
Text::from(""),
|
Text::from(""),
|
||||||
Text::from(""),
|
Text::from(""),
|
||||||
Text::from(sparkline(&result.histogram.counts(10))),
|
Text::from(result.sparkline.as_str()),
|
||||||
Text::from(result.count().to_string()),
|
Text::from(result.count().to_string()),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use ratatui::text::Text;
|
||||||
use ratatui::widgets::{Cell, Row};
|
use ratatui::widgets::{Cell, Row};
|
||||||
use time::format_description::well_known::Iso8601;
|
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 lines = lines.iter().copied().map(|i| &app.lines[i]);
|
||||||
let header = [
|
let header = [
|
||||||
Text::from("Level"),
|
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)
|
ScrollbarTable::new(lines.map(log_row), widths).header(header)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log_row(line: &LogLine) -> Row<'static> {
|
fn log_row(line: &LogLine) -> Row {
|
||||||
Row::new([
|
Row::new([
|
||||||
Text::from(line.level.as_str().to_string()),
|
Text::from(line.level.as_str()),
|
||||||
Text::from(line.app.to_string()),
|
Text::from(line.app.as_str()),
|
||||||
Text::from(line.display()),
|
Text::from(line.display()),
|
||||||
Text::from(line.time.format(&Iso8601::<TIME_FORMAT>).unwrap()).alignment(Alignment::Right),
|
Text::from(line.time.format(&Iso8601::<TIME_FORMAT>).unwrap()).alignment(Alignment::Right),
|
||||||
])
|
])
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ fn exception_trace(exception: &FullException) -> impl Iterator<Item = Row> + '_
|
||||||
let exception_row = Row::new([
|
let exception_row = Row::new([
|
||||||
Text::from(""),
|
Text::from(""),
|
||||||
Text::from(exception.line.to_string()).alignment(Alignment::Right),
|
Text::from(exception.line.to_string()).alignment(Alignment::Right),
|
||||||
Text::from(exception.file.clone()),
|
Text::from(exception.file.as_str()),
|
||||||
])
|
])
|
||||||
.style(TABLE_HEADER_STYLE);
|
.style(TABLE_HEADER_STYLE);
|
||||||
let trace_rows = exception.trace.iter().map(trace_line);
|
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 {
|
fn trace_line(trace: &Trace) -> Row {
|
||||||
Row::new([
|
Row::new([
|
||||||
Text::from(trace.file.clone()),
|
Text::from(trace.file.as_str()),
|
||||||
Text::from(if trace.line > 0 {
|
Text::from(if trace.line > 0 {
|
||||||
trace.line.to_string()
|
trace.line.to_string()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
use crate::app::{App, GroupedLines, LogMatch};
|
use crate::app::{App, GroupedLines, LogMatch};
|
||||||
use crate::ui::histogram::sparkline;
|
|
||||||
use crate::ui::style::TABLE_HEADER_STYLE;
|
use crate::ui::style::TABLE_HEADER_STYLE;
|
||||||
use crate::ui::table::ScrollbarTable;
|
use crate::ui::table::ScrollbarTable;
|
||||||
use ratatui::layout::Constraint;
|
use ratatui::layout::Constraint;
|
||||||
use ratatui::text::Text;
|
use ratatui::text::Text;
|
||||||
use ratatui::widgets::{Cell, Row};
|
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 grouped = &log_match.grouped;
|
||||||
let header = [
|
let header = [
|
||||||
Text::from("Level"),
|
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)
|
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]];
|
let line = &app.lines[group.lines[0]];
|
||||||
|
|
||||||
Row::new([
|
Row::new([
|
||||||
line.level.as_str().to_string(),
|
Text::from(line.level.as_str()),
|
||||||
line.app.to_string(),
|
Text::from(line.app.as_str()),
|
||||||
line.display(),
|
Text::from(line.display()),
|
||||||
sparkline(&group.histogram.counts(10)),
|
Text::from(group.sparkline.as_str()),
|
||||||
group.len().to_string(),
|
Text::from(group.len().to_string()),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue