remove logging statement indirection

This commit is contained in:
Robin Appelman 2025-08-09 17:24:54 +02:00
commit ea695e8460
8 changed files with 120 additions and 126 deletions

View file

@ -1,3 +1,4 @@
use crate::grouping::group_lines_by;
use crate::logfile::{LogFile, LogLine, LogLineNumber};
use crate::logs::ParsedLogs;
use crate::matcher::MatchResult;
@ -6,7 +7,6 @@ use logsmash_data::{LoggingStatementWithPathPrefix, StatementList};
use regex::{escape, Regex, RegexBuilder};
use serde_json::Error as JsonError;
use std::cell::OnceCell;
use std::collections::BTreeMap;
use std::fmt::Display;
use time::OffsetDateTime;
@ -64,7 +64,7 @@ pub struct LogMatch<'logs> {
impl<'logs> LogMatch<'logs> {
pub fn new(result: Option<MatchResult>, lines: Vec<&'logs LogLine<'logs>>) -> Self {
let count = lines.len();
let grouped = group_lines(lines.iter().copied());
let grouped = group_lines_by(lines.iter().copied(), LogLine::identity);
let all = LineSet::new(lines);
LogMatch {
@ -96,33 +96,31 @@ impl<'logs> LogMatch<'logs> {
}
pub fn row_count(&self) -> usize {
self.result.as_ref().map(|res| res.len()).unwrap_or(1)
self.result.as_ref().map(|res| res.count()).unwrap_or(1)
}
pub fn statements<'a>(
&'a self,
app: &'a App,
) -> impl Iterator<Item = LoggingStatementWithPathPrefix> + 'a {
self.result
.iter()
.flat_map(|res| res.iter())
.filter_map(|index| app.log_statements.get(index))
) -> impl Iterator<Item = &'a LoggingStatementWithPathPrefix> + use<'a> {
self.result.iter().flat_map(|res| res.iter())
}
pub fn matches(&self, app: &App, filter: &Filter) -> bool {
pub fn matches(&self, filter: &Filter) -> bool {
if filter.is_empty() {
return true;
}
self.statements(app).any(|statement| {
self.statements().any(|statement| {
filter.parts().all(|filter_part| {
filter_part.is_match(statement.pattern)
|| filter_part.is_match(statement.path)
filter_part.is_match(statement.statement.pattern)
|| filter_part.is_match(statement.statement.path)
|| filter_part.is_match(statement.path_prefix)
|| statement
.statement
.placeholders
.iter()
.any(|placeholder| filter_part.is_match(placeholder))
|| statement
.statement
.exception
.filter(|exception| filter_part.is_match(exception))
.is_some()
@ -135,21 +133,6 @@ impl<'logs> LogMatch<'logs> {
}
}
fn group_lines<'logs, I: Iterator<Item = &'logs LogLine<'logs>>>(
indices: I,
) -> Vec<LineSet<'logs>> {
let mut map: BTreeMap<u64, Vec<&'logs LogLine<'logs>>> = BTreeMap::new();
for line in indices {
map.entry(line.identity()).or_default().push(line);
}
let mut list: Vec<_> = map.into_values().map(LineSet::new).collect();
list.sort_by_key(|list| list.len());
list.reverse();
list
}
pub struct LineSet<'logs> {
pub lines: Vec<&'logs LogLine<'logs>>,
pub histogram: OnceCell<TimeGraph>,

21
src/grouping.rs Normal file
View file

@ -0,0 +1,21 @@
use crate::app::LineSet;
use crate::logfile::LogLine;
use std::collections::BTreeMap;
pub fn group_lines_by<'logs, I, F, K>(indices: I, f: F) -> Vec<LineSet<'logs>>
where
I: Iterator<Item = &'logs LogLine<'logs>>,
K: Ord,
F: Fn(&'logs LogLine<'logs>) -> K,
{
let mut map: BTreeMap<K, Vec<&'logs LogLine<'logs>>> = BTreeMap::new();
for line in indices {
map.entry(f(line)).or_default().push(line);
}
let mut list: Vec<_> = map.into_values().map(LineSet::new).collect();
list.sort_by_key(|list| list.len());
list.reverse();
list
}

View file

@ -22,6 +22,7 @@ use std::sync::RwLock;
mod app;
mod error;
pub mod grouping;
mod logfile;
mod logs;
mod matcher;

View file

@ -1,7 +1,7 @@
use crate::logfile::logline::{Exception, LogLine};
use crate::logfile::LineNumber;
use itertools::Either;
use logsmash_data::{LogLevel, LogStatementIndex, LoggingStatement, StatementList};
use logsmash_data::{LogLevel, LoggingStatementWithPathPrefix, StatementList};
use std::hash::{Hash, Hasher};
use std::iter::once;
use std::ops::Range;
@ -13,22 +13,22 @@ pub struct LogMatch {
exception: Option<&'static str>,
path: &'static str,
line: LineNumber,
index: LogStatementIndex,
statement: LoggingStatementWithPathPrefix,
}
impl LogMatch {
pub fn new(index: LogStatementIndex, statement: &LoggingStatement) -> LogMatch {
pub fn new(statement: LoggingStatementWithPathPrefix) -> LogMatch {
LogMatch {
level: statement.level,
pattern: if statement.has_meaningful_message {
statement.pattern
level: statement.statement.level,
pattern: if statement.statement.has_meaningful_message {
statement.statement.pattern
} else {
""
},
exception: statement.exception,
path: statement.path,
line: statement.line.into(),
index,
exception: statement.statement.exception,
path: statement.statement.path,
line: statement.statement.line.into(),
statement,
}
}
@ -44,10 +44,7 @@ pub struct Matcher {
impl Matcher {
pub fn new(statements: &StatementList) -> Matcher {
let mut matches: Vec<_> = statements
.iter()
.map(|(index, statement)| LogMatch::new(index, statement))
.collect();
let mut matches: Vec<_> = statements.iter().map(LogMatch::new).collect();
matches.sort_by(|a, b| {
// sort first by level, then by longest pattern
a.level
@ -106,13 +103,13 @@ impl Matcher {
best_length = log_match.pattern_len();
best_match = Some(match best_match {
Some(MatchResult::Single(res)) => {
MatchResult::List(vec![res, log_match.index])
MatchResult::List(vec![res, log_match.statement.clone()])
}
Some(MatchResult::List(mut list)) => {
list.push(log_match.index);
list.push(log_match.statement.clone());
MatchResult::List(list)
}
None => MatchResult::Single(log_match.index),
None => MatchResult::Single(log_match.statement.clone()),
});
}
}
@ -123,13 +120,13 @@ impl Matcher {
best_match
}
fn match_exception(&self, exception: &Exception) -> Option<LogStatementIndex> {
fn match_exception(&self, exception: &Exception) -> Option<LoggingStatementWithPathPrefix> {
for log_match in self.matches.iter() {
if log_match.line == exception.line
&& log_match.exception == Some(exception.exception.as_ref())
&& exception.file.ends_with(log_match.path)
{
return Some(log_match.index);
return Some(log_match.statement.clone());
}
}
None
@ -148,8 +145,8 @@ pub fn match_single(pattern: &str, text: &str) -> bool {
#[derive(Debug, Clone, Eq)]
pub enum MatchResult {
Single(LogStatementIndex),
List(Vec<LogStatementIndex>),
Single(LoggingStatementWithPathPrefix),
List(Vec<LoggingStatementWithPathPrefix>),
}
impl PartialEq for MatchResult {
@ -182,30 +179,30 @@ impl Hash for MatchResult {
}
impl MatchResult {
pub fn len(&self) -> usize {
pub fn iter(&self) -> impl Iterator<Item = &LoggingStatementWithPathPrefix> + '_ {
match self {
MatchResult::Single(statement) => Either::Left(once(statement)),
MatchResult::List(list) => Either::Right(list.iter()),
}
}
pub fn count(&self) -> usize {
match self {
MatchResult::Single(_) => 1,
MatchResult::List(list) => list.len(),
}
}
}
pub fn iter(&self) -> impl Iterator<Item = LogStatementIndex> + '_ {
match self {
MatchResult::Single(index) => Either::Left(once(*index)),
MatchResult::List(list) => Either::Right(list.iter().copied()),
}
impl From<LoggingStatementWithPathPrefix> for MatchResult {
fn from(value: LoggingStatementWithPathPrefix) -> Self {
MatchResult::Single(value)
}
}
impl From<usize> for MatchResult {
fn from(value: usize) -> Self {
MatchResult::Single(LogStatementIndex::from(value))
}
}
impl From<Vec<usize>> for MatchResult {
fn from(value: Vec<usize>) -> Self {
MatchResult::List(value.into_iter().map(LogStatementIndex::from).collect())
impl From<Vec<LoggingStatementWithPathPrefix>> for MatchResult {
fn from(value: Vec<LoggingStatementWithPathPrefix>) -> Self {
MatchResult::List(value)
}
}
@ -278,6 +275,7 @@ fn test_matcher() {
use crate::logfile::logline::Exception;
use crate::logfile::LogLineNumber;
use crate::logs::LogIndex;
use logsmash_data::LoggingStatement;
use std::str::FromStr;
use time::OffsetDateTime;
use tinystr::TinyAsciiStr;
@ -357,7 +355,7 @@ fn test_matcher() {
let matcher = Matcher::new(&StatementList::new(vec![("", STATEMENTS)]));
assert_eq!(
Some(MatchResult::from(0)),
Some(MatchResult::from(STATEMENTS[0].with_path_prefix(""))),
matcher.match_log(&LogLine {
version: "29",
app: "core".into(),
@ -367,7 +365,10 @@ fn test_matcher() {
})
);
assert_eq!(
Some(MatchResult::from(vec![3, 4])),
Some(MatchResult::from(vec![
STATEMENTS[3].with_path_prefix(""),
STATEMENTS[4].with_path_prefix("")
])),
matcher.match_log(&LogLine {
version: "29",
app: "core".into(),
@ -377,7 +378,7 @@ fn test_matcher() {
})
);
assert_eq!(
Some(MatchResult::from(1)),
Some(MatchResult::from(STATEMENTS[1].with_path_prefix(""))),
matcher.match_log(&LogLine {
version: "29",
app: "core".into(),
@ -397,7 +398,7 @@ fn test_matcher() {
})
);
assert_eq!(
Some(MatchResult::from(2)),
Some(MatchResult::from(STATEMENTS[2].with_path_prefix(""))),
matcher.match_log(
&LogLine {
version: "29",
@ -409,7 +410,7 @@ fn test_matcher() {
)
);
assert_eq!(
Some(MatchResult::from(4)),
Some(MatchResult::from(STATEMENTS[4].with_path_prefix(""))),
matcher.match_log(
&LogLine {
version: "29",
@ -427,7 +428,7 @@ fn test_matcher() {
)
);
assert_eq!(
Some(MatchResult::from(5)),
Some(MatchResult::from(STATEMENTS[5].with_path_prefix(""))),
matcher.match_log(&LogLine {
version: "29",
app: "core".into(),

View file

@ -41,7 +41,7 @@ pub fn match_list<'a>(app: &'a App<'a>, filter: &Filter) -> ScrollbarTable<'a> {
.chain(
app.matches
.iter()
.filter(|result| result.matches(app, filter))
.filter(|result| result.matches(filter))
.map(|result| log_row(result, app, "")),
)
.chain(unmatched),
@ -55,11 +55,10 @@ fn log_row<'a>(result: &'a LogMatch, app: &'a App, name: &'static str) -> Row<'a
let mut message = String::new();
let mut paths = String::new();
let mut lines = String::new();
for index in match_result.iter() {
let statement = app.log_statements.get(index).expect("invalid match index");
for statement in match_result.iter() {
writeln!(&mut message, "{}", statement.message()).ok();
writeln!(&mut paths, "{}", statement.path()).ok();
writeln!(&mut lines, "{}", statement.line).ok();
writeln!(&mut lines, "{}", statement.line()).ok();
}
Row::new([
Text::from(message),
@ -68,7 +67,7 @@ fn log_row<'a>(result: &'a LogMatch, app: &'a App, name: &'static str) -> Row<'a
Text::from(result.sparkline(app)),
Text::from(result.count().to_string()),
])
.height(match_result.len() as u16)
.height(match_result.count() as u16)
} else {
Row::new([
Text::from(name),

View file

@ -51,7 +51,7 @@ impl<'a> MatchListState<'a> {
} else {
app.matches
.iter()
.filter(|log_match| log_match.matches(app, &self.filter))
.filter(|log_match| log_match.matches(&self.filter))
.nth(selected - 1)
.unwrap_or(&app.unmatched)
}
@ -356,7 +356,7 @@ impl<'a> UiState<'a> {
let match_row_counts = app
.matches
.iter()
.filter(|m| m.matches(app, filter))
.filter(|m| m.matches(filter))
.map(|m| m.row_count());
for (index, row_count) in once(1)
.chain(match_row_counts)