mirror of
https://codeberg.org/icewind/logsmash.git
synced 2026-06-03 18:14:11 +02:00
remove logging statement indirection
This commit is contained in:
parent
7dfbbddde6
commit
ea695e8460
8 changed files with 120 additions and 126 deletions
|
|
@ -24,15 +24,11 @@ impl StatementList {
|
|||
StatementList { statements }
|
||||
}
|
||||
|
||||
pub fn iter(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (LogStatementIndex, &'static LoggingStatement)> + Send + '_ {
|
||||
self.statements
|
||||
.iter()
|
||||
.copied()
|
||||
.flat_map(|(_, list)| list.iter())
|
||||
.enumerate()
|
||||
.map(|(index, statement)| (LogStatementIndex(index), statement))
|
||||
pub fn iter(&self) -> impl Iterator<Item = LoggingStatementWithPathPrefix> + Send + '_ {
|
||||
self.statements.iter().copied().flat_map(|(prefix, list)| {
|
||||
list.iter()
|
||||
.map(|statement| statement.with_path_prefix(prefix))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get(&self, mut index: LogStatementIndex) -> Option<LoggingStatementWithPathPrefix> {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use serde::Deserialize;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Clone, Copy, Deserialize, Hash, PartialOrd, Ord, Eq)]
|
||||
#[serde(from = "i64")]
|
||||
|
|
@ -57,7 +59,7 @@ impl LogLevel {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone, Eq, Ord)]
|
||||
pub struct LoggingStatement {
|
||||
pub level: LogLevel,
|
||||
pub path: &'static str,
|
||||
|
|
@ -68,24 +70,31 @@ pub struct LoggingStatement {
|
|||
pub has_meaningful_message: bool,
|
||||
}
|
||||
|
||||
impl PartialOrd for LoggingStatement {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
(self.path, self.line).partial_cmp(&(other.path, other.line))
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for LoggingStatement {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
(self.path, self.line).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl LoggingStatement {
|
||||
pub fn with_path_prefix(&self, path_prefix: &'static str) -> LoggingStatementWithPathPrefix {
|
||||
pub fn with_path_prefix(
|
||||
&'static self,
|
||||
path_prefix: &'static str,
|
||||
) -> LoggingStatementWithPathPrefix {
|
||||
LoggingStatementWithPathPrefix {
|
||||
level: self.level,
|
||||
path_prefix,
|
||||
path: self.path,
|
||||
line: self.line,
|
||||
placeholders: self.placeholders,
|
||||
exception: self.exception,
|
||||
pattern: self.pattern,
|
||||
has_meaningful_message: self.has_meaningful_message,
|
||||
statement: self,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn message(&self) -> impl Display + '_ {
|
||||
LoggingMessage {
|
||||
message: self.clone(),
|
||||
}
|
||||
LoggingMessage { message: &self }
|
||||
}
|
||||
|
||||
pub fn pattern_len(&self) -> usize {
|
||||
|
|
@ -93,50 +102,34 @@ impl LoggingStatement {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone, Eq, Ord, PartialOrd, Hash)]
|
||||
pub struct LoggingStatementWithPathPrefix {
|
||||
pub level: LogLevel,
|
||||
pub statement: &'static LoggingStatement,
|
||||
pub path_prefix: &'static str,
|
||||
pub path: &'static str,
|
||||
pub line: usize,
|
||||
pub placeholders: &'static [&'static str],
|
||||
pub exception: Option<&'static str>,
|
||||
pub pattern: &'static str,
|
||||
pub has_meaningful_message: bool,
|
||||
}
|
||||
|
||||
impl From<&LoggingStatementWithPathPrefix> for LoggingStatement {
|
||||
fn from(value: &LoggingStatementWithPathPrefix) -> Self {
|
||||
LoggingStatement {
|
||||
level: value.level,
|
||||
path: value.path,
|
||||
line: value.line,
|
||||
placeholders: value.placeholders,
|
||||
exception: value.exception,
|
||||
pattern: value.pattern,
|
||||
has_meaningful_message: value.has_meaningful_message,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LoggingStatementWithPathPrefix {
|
||||
fn raw_message(&self) -> LoggingMessage {
|
||||
fn raw_message(&self) -> LoggingMessage<'static> {
|
||||
LoggingMessage {
|
||||
message: self.into(),
|
||||
message: self.statement,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(&self) -> impl Display {
|
||||
LoggingStatementPath {
|
||||
path_prefix: self.path_prefix,
|
||||
path: self.path,
|
||||
path: self.statement.path,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn line(&self) -> usize {
|
||||
self.statement.line
|
||||
}
|
||||
|
||||
pub fn message(&self) -> impl Display {
|
||||
LoggingStatementMessage {
|
||||
message: self.raw_message(),
|
||||
exception: self.exception,
|
||||
exception: self.statement.exception,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -153,7 +146,7 @@ impl Display for LoggingStatementPath {
|
|||
}
|
||||
|
||||
struct LoggingStatementMessage {
|
||||
pub message: LoggingMessage,
|
||||
pub message: LoggingMessage<'static>,
|
||||
pub exception: Option<&'static str>,
|
||||
}
|
||||
|
||||
|
|
@ -174,16 +167,16 @@ impl Display for LoggingStatementWithPathPrefix {
|
|||
"«{}» {} line {}",
|
||||
self.raw_message(),
|
||||
self.path(),
|
||||
self.line
|
||||
self.statement.line
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct LoggingMessage {
|
||||
message: LoggingStatement,
|
||||
struct LoggingMessage<'a> {
|
||||
message: &'a LoggingStatement,
|
||||
}
|
||||
|
||||
impl Display for LoggingMessage {
|
||||
impl Display for LoggingMessage<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
if self.message.pattern.is_empty() {
|
||||
return Ok(());
|
||||
|
|
|
|||
39
src/app.rs
39
src/app.rs
|
|
@ -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
21
src/grouping.rs
Normal 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
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ use std::sync::RwLock;
|
|||
|
||||
mod app;
|
||||
mod error;
|
||||
pub mod grouping;
|
||||
mod logfile;
|
||||
mod logs;
|
||||
mod matcher;
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue