handle multiple "equal" matches

This commit is contained in:
Robin Appelman 2024-07-23 17:40:57 +02:00
commit 09f6ac6f47
3 changed files with 80 additions and 27 deletions

View file

@ -46,6 +46,23 @@ pub struct LoggingStatement {
pub regex: &'static str,
}
impl Display for LoggingStatement {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if let Some(exception) = self.exception {
write!(
f,
"{}({}): {} line {}",
exception,
self.message(),
self.path,
self.line
)
} else {
write!(f, "{}: {} line {}", self.message(), self.path, self.line)
}
}
}
impl LoggingStatement {
pub fn message(&self) -> impl Display + '_ {
LoggingMessage { message: &self }

View file

@ -1,7 +1,7 @@
use crate::error::LogError;
use crate::logfile::LogFile;
use crate::logline::LogLine;
use crate::matcher::Matcher;
use crate::matcher::{MatchResult, Matcher};
use clap::Parser;
use cloud_log_analyser_data::{get_statements, MAX_VERSION};
use main_error::MainResult;
@ -28,7 +28,7 @@ fn main() -> MainResult {
})?;
let mut lines = log_file.iter();
let mut counts: HashMap<usize, usize> = HashMap::new();
let mut counts: HashMap<MatchResult, usize> = HashMap::new();
let first = lines.next().unwrap();
let first_parsed: LogLine = serde_json::from_str(&first).unwrap();
@ -66,26 +66,8 @@ fn main() -> MainResult {
let mut counts: Vec<(_, _)> = counts.into_iter().collect();
counts.sort_by_key(|(_, count)| *count);
counts.reverse();
for (index, count) in counts {
let statement = &statements[index];
if let Some(exception) = statement.exception {
println!(
"{}({}): {} line {}: {}",
exception,
statement.message(),
statement.path,
statement.line,
count
);
} else {
println!(
"{}: {} line {}: {}",
statement.message(),
statement.path,
statement.line,
count
);
}
for (match_result, count) in counts {
println!("{}: {}", match_result.display(statements), count);
}
if unmatched_total > 0 {
eprintln!("\n{unmatched_total} lines couldn't be matched:");

View file

@ -1,6 +1,7 @@
use crate::logline::LogLine;
use cloud_log_analyser_data::{LogLevel, LoggingStatement};
use regex::Regex;
use std::fmt::{Display, Formatter};
pub struct LogMatch {
level: LogLevel,
@ -39,7 +40,7 @@ impl Matcher {
}
}
pub fn match_log(&self, log: &LogLine) -> Option<usize> {
pub fn match_log(&self, log: &LogLine) -> Option<MatchResult> {
let mut best_match = None;
let mut best_length = 0;
@ -49,7 +50,7 @@ impl Matcher {
&& log_match.exception.as_deref() == Some(exception.exception.as_ref())
&& log_match.path == exception.file.as_ref()
{
return Some(i);
return Some(MatchResult::Single(i));
}
}
}
@ -58,10 +59,20 @@ impl Matcher {
if log_match.has_meaningful_message {
if log.level.matches(log_match.level)
&& log_match.pattern.is_match(log.message.as_ref())
&& log_match.pattern_length > best_length
&& log_match.pattern_length >= best_length
{
best_match = Some(i);
best_length = log_match.pattern_length;
if log_match.pattern_length > best_length {
best_match = None;
best_length = log_match.pattern_length;
}
best_match = Some(match best_match {
Some(MatchResult::Single(res)) => MatchResult::List(vec![res, i]),
Some(MatchResult::List(mut list)) => {
list.push(i);
MatchResult::List(list)
}
None => MatchResult::Single(i),
});
}
}
}
@ -72,6 +83,49 @@ impl Matcher {
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum MatchResult {
Single(usize),
List(Vec<usize>),
}
impl MatchResult {
pub fn display<'a>(&'a self, log_statements: &'a [LoggingStatement]) -> impl Display + 'a {
MatchResultDisplay {
log_statements,
result: &self,
}
}
}
struct MatchResultDisplay<'a> {
log_statements: &'a [LoggingStatement],
result: &'a MatchResult,
}
impl Display for MatchResultDisplay<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.result {
MatchResult::Single(index) => {
if let Some(statement) = self.log_statements.get(*index) {
write!(f, "{statement}")
} else {
write!(f, "unknown statement")
}
}
MatchResult::List(list) => {
writeln!(f, "{} possible matches:", list.len())?;
for index in list {
if let Some(statement) = self.log_statements.get(*index) {
writeln!(f, " {statement}")?;
}
}
write!(f, " ")
}
}
}
}
#[test]
fn test_matcher() {
use crate::logline::Exception;