mirror of
https://codeberg.org/icewind/logsmash.git
synced 2026-06-03 10:04:12 +02:00
stack sparkline
This commit is contained in:
parent
ea695e8460
commit
36bdc23080
3 changed files with 38 additions and 16 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
name = "logsmash"
|
name = "logsmash"
|
||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.85.0"
|
rust-version = "1.87.0"
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
||||||
16
src/app.rs
16
src/app.rs
|
|
@ -2,7 +2,7 @@ use crate::grouping::group_lines_by;
|
||||||
use crate::logfile::{LogFile, LogLine, LogLineNumber};
|
use crate::logfile::{LogFile, LogLine, LogLineNumber};
|
||||||
use crate::logs::ParsedLogs;
|
use crate::logs::ParsedLogs;
|
||||||
use crate::matcher::MatchResult;
|
use crate::matcher::MatchResult;
|
||||||
use crate::timegraph::TimeGraph;
|
use crate::timegraph::{SparkLine, TimeGraph};
|
||||||
use logsmash_data::{LoggingStatementWithPathPrefix, StatementList};
|
use logsmash_data::{LoggingStatementWithPathPrefix, StatementList};
|
||||||
use regex::{escape, Regex, RegexBuilder};
|
use regex::{escape, Regex, RegexBuilder};
|
||||||
use serde_json::Error as JsonError;
|
use serde_json::Error as JsonError;
|
||||||
|
|
@ -56,7 +56,7 @@ pub struct LogMatch<'logs> {
|
||||||
pub result: Option<MatchResult>,
|
pub result: Option<MatchResult>,
|
||||||
pub count: usize,
|
pub count: usize,
|
||||||
pub histogram: OnceCell<TimeGraph>,
|
pub histogram: OnceCell<TimeGraph>,
|
||||||
pub sparkline: OnceCell<String>,
|
pub sparkline: OnceCell<SparkLine>,
|
||||||
pub all: LineSet<'logs>,
|
pub all: LineSet<'logs>,
|
||||||
pub grouped: Vec<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
|
self.sparkline
|
||||||
.get_or_init(|| self.histogram(app).sparkline::<10>())
|
.get_or_init(|| self.histogram(app).sparkline())
|
||||||
.as_str()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn histogram(&self, app: &App) -> &TimeGraph {
|
pub fn histogram(&self, app: &App) -> &TimeGraph {
|
||||||
|
|
@ -136,7 +135,7 @@ impl<'logs> LogMatch<'logs> {
|
||||||
pub struct LineSet<'logs> {
|
pub struct LineSet<'logs> {
|
||||||
pub lines: Vec<&'logs LogLine<'logs>>,
|
pub lines: Vec<&'logs LogLine<'logs>>,
|
||||||
pub histogram: OnceCell<TimeGraph>,
|
pub histogram: OnceCell<TimeGraph>,
|
||||||
pub sparkline: OnceCell<String>,
|
pub sparkline: OnceCell<SparkLine>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'logs> LineSet<'logs> {
|
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
|
self.sparkline
|
||||||
.get_or_init(|| self.histogram(app).sparkline::<10>())
|
.get_or_init(|| self.histogram(app).sparkline())
|
||||||
.as_str()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn histogram(&self, app: &App) -> &TimeGraph {
|
pub fn histogram(&self, app: &App) -> &TimeGraph {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use hdrhistogram::Histogram;
|
use hdrhistogram::Histogram;
|
||||||
|
use ratatui::text::Text;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
|
|
@ -41,21 +42,44 @@ impl TimeGraph {
|
||||||
.map(|val| val.count_since_last_iteration())
|
.map(|val| val.count_since_last_iteration())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sparkline<const N: usize>(&self) -> String {
|
pub fn sparkline(&self) -> SparkLine {
|
||||||
let mut values = [0; N];
|
let mut values = [0; 10];
|
||||||
for (value, count) in values.iter_mut().zip(self.counts(N)) {
|
for (value, count) in values.iter_mut().zip(self.counts(10)) {
|
||||||
*value = count;
|
*value = count;
|
||||||
}
|
}
|
||||||
let max = values.iter().copied().max().unwrap_or_default() as f64;
|
let max = values.iter().copied().max().unwrap_or_default() as f64;
|
||||||
let len = SPARKS.len() as f64 - 1.0;
|
let len = SPARKS.len() as f64 - 1.0;
|
||||||
values
|
values
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.map(|val| {
|
.map(|val| {
|
||||||
let rel = val as f64 / max;
|
let rel = val as f64 / max;
|
||||||
SPARKS[(rel * len) as usize]
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue