stack sparkline

This commit is contained in:
Robin Appelman 2025-08-09 17:27:25 +02:00
commit 36bdc23080
3 changed files with 38 additions and 16 deletions

View file

@ -2,7 +2,7 @@
name = "logsmash"
version = "0.1.11"
edition = "2021"
rust-version = "1.85.0"
rust-version = "1.87.0"
license = "GPL-3.0-only"
[dependencies]

View file

@ -2,7 +2,7 @@ use crate::grouping::group_lines_by;
use crate::logfile::{LogFile, LogLine, LogLineNumber};
use crate::logs::ParsedLogs;
use crate::matcher::MatchResult;
use crate::timegraph::TimeGraph;
use crate::timegraph::{SparkLine, TimeGraph};
use logsmash_data::{LoggingStatementWithPathPrefix, StatementList};
use regex::{escape, Regex, RegexBuilder};
use serde_json::Error as JsonError;
@ -56,7 +56,7 @@ pub struct LogMatch<'logs> {
pub result: Option<MatchResult>,
pub count: usize,
pub histogram: OnceCell<TimeGraph>,
pub sparkline: OnceCell<String>,
pub sparkline: OnceCell<SparkLine>,
pub all: LineSet<'logs>,
pub grouped: Vec<LineSet<'logs>>,
}
@ -77,10 +77,9 @@ impl<'logs> LogMatch<'logs> {
}
}
pub fn sparkline(&self, app: &App) -> &str {
pub fn sparkline(&self, app: &App) -> &SparkLine {
self.sparkline
.get_or_init(|| self.histogram(app).sparkline::<10>())
.as_str()
.get_or_init(|| self.histogram(app).sparkline())
}
pub fn histogram(&self, app: &App) -> &TimeGraph {
@ -136,7 +135,7 @@ impl<'logs> LogMatch<'logs> {
pub struct LineSet<'logs> {
pub lines: Vec<&'logs LogLine<'logs>>,
pub histogram: OnceCell<TimeGraph>,
pub sparkline: OnceCell<String>,
pub sparkline: OnceCell<SparkLine>,
}
impl<'logs> LineSet<'logs> {
@ -148,10 +147,9 @@ impl<'logs> LineSet<'logs> {
}
}
pub fn sparkline(&self, app: &App) -> &str {
pub fn sparkline(&self, app: &App) -> &SparkLine {
self.sparkline
.get_or_init(|| self.histogram(app).sparkline::<10>())
.as_str()
.get_or_init(|| self.histogram(app).sparkline())
}
pub fn histogram(&self, app: &App) -> &TimeGraph {

View file

@ -1,4 +1,5 @@
use hdrhistogram::Histogram;
use ratatui::text::Text;
use std::cmp::max;
use time::OffsetDateTime;
@ -41,21 +42,44 @@ impl TimeGraph {
.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)) {
pub fn sparkline(&self) -> SparkLine {
let mut values = [0; 10];
for (value, count) in values.iter_mut().zip(self.counts(10)) {
*value = count;
}
let max = values.iter().copied().max().unwrap_or_default() 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()
.into()
}
}
// the biggest sparkline char is 3 bytes
pub struct SparkLine {
bytes: [u8; 10 * 3],
}
impl From<[char; 10]> for SparkLine {
fn from(value: [char; 10]) -> Self {
let mut buff = [0; 10 * 3];
let mut offset = 0;
for char in value {
char.encode_utf8(&mut buff[offset..]);
offset += char.len_utf8();
}
SparkLine { bytes: buff }
}
}
impl<'a> From<&'a SparkLine> for Text<'a> {
fn from(value: &'a SparkLine) -> Self {
// SAFETY: we only put bytes into the buffer from encode_utf8
let str = unsafe { str::from_utf8_unchecked(&value.bytes).trim_end_matches(char::from(0)) };
Text::raw(str)
}
}