mirror of
https://codeberg.org/icewind/logsmash.git
synced 2026-06-03 18:14:11 +02:00
handle multiple "equal" matches
This commit is contained in:
parent
2ba2b82a4a
commit
09f6ac6f47
3 changed files with 80 additions and 27 deletions
|
|
@ -46,6 +46,23 @@ pub struct LoggingStatement {
|
||||||
pub regex: &'static str,
|
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 {
|
impl LoggingStatement {
|
||||||
pub fn message(&self) -> impl Display + '_ {
|
pub fn message(&self) -> impl Display + '_ {
|
||||||
LoggingMessage { message: &self }
|
LoggingMessage { message: &self }
|
||||||
|
|
|
||||||
26
src/main.rs
26
src/main.rs
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::error::LogError;
|
use crate::error::LogError;
|
||||||
use crate::logfile::LogFile;
|
use crate::logfile::LogFile;
|
||||||
use crate::logline::LogLine;
|
use crate::logline::LogLine;
|
||||||
use crate::matcher::Matcher;
|
use crate::matcher::{MatchResult, Matcher};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use cloud_log_analyser_data::{get_statements, MAX_VERSION};
|
use cloud_log_analyser_data::{get_statements, MAX_VERSION};
|
||||||
use main_error::MainResult;
|
use main_error::MainResult;
|
||||||
|
|
@ -28,7 +28,7 @@ fn main() -> MainResult {
|
||||||
})?;
|
})?;
|
||||||
let mut lines = log_file.iter();
|
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 = lines.next().unwrap();
|
||||||
let first_parsed: LogLine = serde_json::from_str(&first).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();
|
let mut counts: Vec<(_, _)> = counts.into_iter().collect();
|
||||||
counts.sort_by_key(|(_, count)| *count);
|
counts.sort_by_key(|(_, count)| *count);
|
||||||
counts.reverse();
|
counts.reverse();
|
||||||
for (index, count) in counts {
|
for (match_result, count) in counts {
|
||||||
let statement = &statements[index];
|
println!("{}: {}", match_result.display(statements), count);
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if unmatched_total > 0 {
|
if unmatched_total > 0 {
|
||||||
eprintln!("\n{unmatched_total} lines couldn't be matched:");
|
eprintln!("\n{unmatched_total} lines couldn't be matched:");
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::logline::LogLine;
|
use crate::logline::LogLine;
|
||||||
use cloud_log_analyser_data::{LogLevel, LoggingStatement};
|
use cloud_log_analyser_data::{LogLevel, LoggingStatement};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
pub struct LogMatch {
|
pub struct LogMatch {
|
||||||
level: LogLevel,
|
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_match = None;
|
||||||
let mut best_length = 0;
|
let mut best_length = 0;
|
||||||
|
|
||||||
|
|
@ -49,7 +50,7 @@ impl Matcher {
|
||||||
&& log_match.exception.as_deref() == Some(exception.exception.as_ref())
|
&& log_match.exception.as_deref() == Some(exception.exception.as_ref())
|
||||||
&& log_match.path == exception.file.as_ref()
|
&& log_match.path == exception.file.as_ref()
|
||||||
{
|
{
|
||||||
return Some(i);
|
return Some(MatchResult::Single(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -58,11 +59,21 @@ impl Matcher {
|
||||||
if log_match.has_meaningful_message {
|
if log_match.has_meaningful_message {
|
||||||
if log.level.matches(log_match.level)
|
if log.level.matches(log_match.level)
|
||||||
&& log_match.pattern.is_match(log.message.as_ref())
|
&& 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);
|
if log_match.pattern_length > best_length {
|
||||||
|
best_match = None;
|
||||||
best_length = log_match.pattern_length;
|
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]
|
#[test]
|
||||||
fn test_matcher() {
|
fn test_matcher() {
|
||||||
use crate::logline::Exception;
|
use crate::logline::Exception;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue