mirror of
https://codeberg.org/icewind/logsmash.git
synced 2026-06-03 10:04:12 +02:00
borrowed line parsing
This commit is contained in:
parent
b9c7704699
commit
95e09f0e0c
12 changed files with 61 additions and 62 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -876,9 +876,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.5"
|
||||
version = "1.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@
|
|||
releaseMatrix = buildMatrix releaseTargets;
|
||||
|
||||
devShells.default = mkShell {
|
||||
nativeBuildInputs = with pkgs; [msrvToolchain rustc bacon cargo-msrv cargo-insta samply];
|
||||
nativeBuildInputs = with pkgs; [msrvToolchain rustc bacon cargo-msrv cargo-insta samply hyperfine];
|
||||
};
|
||||
}
|
||||
)
|
||||
|
|
|
|||
10
src/app.rs
10
src/app.rs
|
|
@ -6,20 +6,20 @@ use logsmash_data::StatementList;
|
|||
use std::collections::BTreeMap;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
pub struct App {
|
||||
pub struct App<'a> {
|
||||
pub first_date: OffsetDateTime,
|
||||
pub last_date: OffsetDateTime,
|
||||
pub lines: Vec<LogLine>,
|
||||
pub lines: Vec<LogLine<'a>>,
|
||||
pub log_statements: StatementList,
|
||||
pub matches: Vec<LogMatch>,
|
||||
pub error_count: usize,
|
||||
pub all: LogMatch,
|
||||
pub unmatched: LogMatch,
|
||||
pub log_file: LogFile,
|
||||
pub log_file: &'a LogFile,
|
||||
pub error_lines: Vec<(String, serde_json::Error)>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
impl<'a> App<'a> {
|
||||
pub fn match_lines(&self) -> usize {
|
||||
let unmatched_line_count = if self.unmatched.lines.is_empty() {
|
||||
0
|
||||
|
|
@ -29,7 +29,7 @@ impl App {
|
|||
self.matches.len() + 1 + unmatched_line_count
|
||||
}
|
||||
|
||||
pub fn get_line(&self, index: usize) -> Option<&str> {
|
||||
pub fn get_line(&self, index: usize) -> Option<&'a str> {
|
||||
self.log_file.nth(index)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use ahash::AHasher;
|
|||
use logsmash_data::LogLevel;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use time::format_description::well_known::iso8601::{Config, EncodedConfig, TimePrecision};
|
||||
|
|
@ -16,14 +17,14 @@ pub const TIME_FORMAT: EncodedConfig = Config::DEFAULT
|
|||
.encode();
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LogLine {
|
||||
pub struct LogLine<'a> {
|
||||
#[serde(default)]
|
||||
pub index: usize,
|
||||
pub version: TinyAsciiStr<16>,
|
||||
pub version: &'a str,
|
||||
pub level: LogLevel,
|
||||
pub message: String,
|
||||
pub exception: Option<Exception>,
|
||||
pub app: TinyAsciiStr<32>,
|
||||
pub message: Cow<'a, str>,
|
||||
pub exception: Option<Exception<'a>>,
|
||||
pub app: &'a str,
|
||||
#[serde(with = "date")]
|
||||
pub time: OffsetDateTime,
|
||||
}
|
||||
|
|
@ -76,21 +77,10 @@ mod date {
|
|||
return Ok(date);
|
||||
}
|
||||
}
|
||||
return Err(D::Error::custom(format_args!(
|
||||
Err(D::Error::custom(format_args!(
|
||||
"Failed to parse date: {}",
|
||||
str
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
impl LogLine {
|
||||
pub fn identity(&self) -> u64 {
|
||||
let mut hasher = AHasher::default();
|
||||
self.message.hash(&mut hasher);
|
||||
self.level.hash(&mut hasher);
|
||||
self.exception.hash(&mut hasher);
|
||||
self.app.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -99,15 +89,24 @@ pub fn format_time(time: OffsetDateTime) -> String {
|
|||
.unwrap_or_else(|_| "Invalid time".into())
|
||||
}
|
||||
|
||||
impl LogLine {
|
||||
pub fn display(&self) -> String {
|
||||
impl<'a> LogLine<'a> {
|
||||
pub fn identity(&self) -> u64 {
|
||||
let mut hasher = AHasher::default();
|
||||
self.message.hash(&mut hasher);
|
||||
self.level.hash(&mut hasher);
|
||||
self.exception.hash(&mut hasher);
|
||||
self.app.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
pub fn display(&'a self) -> Cow<'a, str> {
|
||||
if let Some(exception) = self.exception.as_ref() {
|
||||
format!(
|
||||
Cow::Owned(format!(
|
||||
"{}{}{}({}) - {} line {}",
|
||||
if self.message.starts_with("Exception thrown:") {
|
||||
""
|
||||
} else {
|
||||
self.message.as_str()
|
||||
&self.message
|
||||
},
|
||||
if self.message.starts_with("Exception thrown:") {
|
||||
""
|
||||
|
|
@ -118,23 +117,23 @@ impl LogLine {
|
|||
exception.message,
|
||||
exception.file,
|
||||
exception.line
|
||||
)
|
||||
))
|
||||
} else {
|
||||
self.message.clone()
|
||||
Cow::Borrowed(&self.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct Exception {
|
||||
pub message: String,
|
||||
pub exception: String,
|
||||
pub file: String,
|
||||
pub struct Exception<'a> {
|
||||
pub message: Cow<'a, str>,
|
||||
pub exception: Cow<'a, str>,
|
||||
pub file: Cow<'a, str>,
|
||||
pub line: usize,
|
||||
}
|
||||
|
||||
impl Hash for Exception {
|
||||
impl Hash for Exception<'_> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.message.hash(state);
|
||||
self.exception.hash(state);
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ fn main() -> MainResult {
|
|||
unmatched,
|
||||
all,
|
||||
error_count,
|
||||
log_file,
|
||||
log_file: &log_file,
|
||||
};
|
||||
|
||||
if args.profile {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ impl Matcher {
|
|||
if let Some(exception) = &log.exception {
|
||||
for log_match in self.matches.iter() {
|
||||
if log_match.line == exception.line
|
||||
&& log_match.exception == Some(exception.exception.as_str())
|
||||
&& log_match.exception == Some(exception.exception.as_ref())
|
||||
&& exception.file.ends_with(log_match.path)
|
||||
{
|
||||
return Some(MatchResult::Single(log_match.index));
|
||||
|
|
@ -68,7 +68,7 @@ impl Matcher {
|
|||
for log_match in self.matches.iter() {
|
||||
if log_match.has_meaningful_message
|
||||
&& log.level.matches(log_match.level)
|
||||
&& log_match.pattern.is_match(log.message.as_str())
|
||||
&& log_match.pattern.is_match(&log.message)
|
||||
&& log_match.pattern_length >= best_length
|
||||
{
|
||||
if log_match.pattern_length > best_length {
|
||||
|
|
@ -147,7 +147,6 @@ impl MatchResult {
|
|||
fn test_matcher() {
|
||||
use crate::logline::Exception;
|
||||
use time::OffsetDateTime;
|
||||
use tinystr::TinyAsciiStr;
|
||||
|
||||
const STATEMENTS: &[LoggingStatement] = &[
|
||||
LoggingStatement {
|
||||
|
|
@ -195,8 +194,8 @@ fn test_matcher() {
|
|||
assert_eq!(
|
||||
Some(MatchResult::Single(0)),
|
||||
matcher.match_log(&LogLine {
|
||||
version: TinyAsciiStr::from_str("29").unwrap(),
|
||||
app: TinyAsciiStr::from_str("core").unwrap(),
|
||||
version: "29",
|
||||
app: "core",
|
||||
level: LogLevel::Error,
|
||||
message: "Not allowed to rename a shared album".into(),
|
||||
exception: None,
|
||||
|
|
@ -207,8 +206,8 @@ fn test_matcher() {
|
|||
assert_eq!(
|
||||
Some(MatchResult::List(vec![3, 4])),
|
||||
matcher.match_log(&LogLine {
|
||||
version: TinyAsciiStr::from_str("29").unwrap(),
|
||||
app: TinyAsciiStr::from_str("core").unwrap(),
|
||||
version: "29",
|
||||
app: "core",
|
||||
level: LogLevel::Error,
|
||||
message: "Not allowed to rename an album".into(),
|
||||
exception: None,
|
||||
|
|
@ -219,8 +218,8 @@ fn test_matcher() {
|
|||
assert_eq!(
|
||||
Some(MatchResult::Single(1)),
|
||||
matcher.match_log(&LogLine {
|
||||
version: TinyAsciiStr::from_str("29").unwrap(),
|
||||
app: TinyAsciiStr::from_str("core").unwrap(),
|
||||
version: "29",
|
||||
app: "core",
|
||||
level: LogLevel::Error,
|
||||
message: "You are not allowed to edit link shares that you don't own".into(),
|
||||
exception: None,
|
||||
|
|
@ -231,8 +230,8 @@ fn test_matcher() {
|
|||
assert_eq!(
|
||||
None,
|
||||
matcher.match_log(&LogLine {
|
||||
version: TinyAsciiStr::from_str("29").unwrap(),
|
||||
app: TinyAsciiStr::from_str("core").unwrap(),
|
||||
version: "29",
|
||||
app: "core",
|
||||
level: LogLevel::Info,
|
||||
message: "You are not allowed to edit link shares that you don't own".into(),
|
||||
exception: None,
|
||||
|
|
@ -244,8 +243,8 @@ fn test_matcher() {
|
|||
Some(MatchResult::Single(2)),
|
||||
matcher.match_log(
|
||||
&LogLine {
|
||||
version: TinyAsciiStr::from_str("29").unwrap(),
|
||||
app: TinyAsciiStr::from_str("core").unwrap(),
|
||||
version: "29",
|
||||
app: "core",
|
||||
level: LogLevel::Error,
|
||||
message: "Unsupported query value for mimetype: %/text, only values in the format \"mime/type\" or \"mime/%\" are supported".into(),
|
||||
exception: None,
|
||||
|
|
@ -258,8 +257,8 @@ fn test_matcher() {
|
|||
Some(MatchResult::Single(4)),
|
||||
matcher.match_log(
|
||||
&LogLine {
|
||||
version: TinyAsciiStr::from_str("29").unwrap(),
|
||||
app: TinyAsciiStr::from_str("core").unwrap(),
|
||||
version: "29",
|
||||
app: "core",
|
||||
level: LogLevel::Error,
|
||||
message: "Unsupported query value for mimetype: %/text, only values in the format \"mime/type\" or \"mime/%\" are supported".into(),
|
||||
exception: Some(Exception {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use ratatui::layout::Constraint;
|
|||
use ratatui::text::Text;
|
||||
use ratatui::widgets::{Cell, Row};
|
||||
|
||||
pub fn error_list(app: &App) -> ScrollbarTable {
|
||||
pub fn error_list<'a>(app: &'a App<'a>) -> ScrollbarTable<'a> {
|
||||
let header = [Text::from("Error"), Text::from("Line")]
|
||||
.into_iter()
|
||||
.map(Cell::from)
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ use crate::ui::state::UiPage;
|
|||
use ratatui::layout::Constraint;
|
||||
use ratatui::prelude::Style;
|
||||
use ratatui::style::palette::tailwind;
|
||||
use ratatui::text::Text;
|
||||
use ratatui::widgets::{Row, Table};
|
||||
|
||||
pub fn footer(app: &App, page: UiPage) -> Table {
|
||||
pub fn footer<'a>(app: &App<'a>, page: UiPage) -> Table<'a> {
|
||||
let footer_style = Style::default()
|
||||
.bg(tailwind::BLACK)
|
||||
.fg(tailwind::GREEN.c600);
|
||||
|
|
@ -18,9 +19,9 @@ pub fn footer(app: &App, page: UiPage) -> Table {
|
|||
|
||||
Table::new(
|
||||
[Row::new([
|
||||
help(page).to_string(),
|
||||
format!("{} unmatched items", app.unmatched.lines.len()),
|
||||
format!("{} parse errors", app.error_count),
|
||||
Text::from(help(page)),
|
||||
Text::from(format!("{} unmatched items", app.unmatched.lines.len())),
|
||||
Text::from(format!("{} parse errors", app.error_count)),
|
||||
])],
|
||||
widths,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use ratatui::widgets::{Cell, Row};
|
|||
use std::fmt::Write;
|
||||
use std::iter::{empty, once};
|
||||
|
||||
pub fn match_list(app: &App) -> ScrollbarTable {
|
||||
pub fn match_list<'a>(app: &'a App<'a>) -> ScrollbarTable<'a> {
|
||||
let header = [
|
||||
Text::from("Statement"),
|
||||
Text::from("File"),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use ratatui::layout::{Alignment, Constraint};
|
|||
use ratatui::text::Text;
|
||||
use ratatui::widgets::{Cell, Row};
|
||||
|
||||
pub fn raw_logs<'a>(app: &'a App, lines: &[usize]) -> ScrollbarTable<'a> {
|
||||
pub fn raw_logs<'a>(app: &'a App<'a>, lines: &[usize]) -> ScrollbarTable<'a> {
|
||||
let lines = lines.iter().copied().map(|i| &app.lines[i]);
|
||||
let header = [
|
||||
Text::from("Level"),
|
||||
|
|
@ -29,10 +29,10 @@ pub fn raw_logs<'a>(app: &'a App, lines: &[usize]) -> ScrollbarTable<'a> {
|
|||
ScrollbarTable::new(lines.map(log_row), widths).header(header)
|
||||
}
|
||||
|
||||
fn log_row(line: &LogLine) -> Row {
|
||||
fn log_row<'a>(line: &'a LogLine<'a>) -> Row<'a> {
|
||||
Row::new([
|
||||
Text::from(line.level.as_str()),
|
||||
Text::from(line.app.as_str()),
|
||||
Text::from(line.app),
|
||||
Text::from(line.display()),
|
||||
Text::from(format_time(line.time)).alignment(Alignment::Right),
|
||||
])
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use ratatui::layout::Constraint;
|
|||
use ratatui::text::Text;
|
||||
use ratatui::widgets::{Cell, Row};
|
||||
|
||||
pub fn grouped_lines<'a>(app: &'a App, log_match: &'a LogMatch) -> ScrollbarTable<'a> {
|
||||
pub fn grouped_lines<'a>(app: &'a App<'a>, log_match: &'a LogMatch) -> ScrollbarTable<'a> {
|
||||
let grouped = &log_match.grouped;
|
||||
let header = [
|
||||
Text::from("Level"),
|
||||
|
|
@ -35,7 +35,7 @@ fn group_row<'a>(app: &'a App, group: &'a GroupedLines) -> Row<'a> {
|
|||
|
||||
Row::new([
|
||||
Text::from(line.level.as_str()),
|
||||
Text::from(line.app.as_str()),
|
||||
Text::from(line.app),
|
||||
Text::from(line.display()),
|
||||
Text::from(group.sparkline.as_str()),
|
||||
Text::from(group.len().to_string()),
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ impl<'a> LogsState<'a> {
|
|||
#[derive(Clone)]
|
||||
pub struct LogState<'a> {
|
||||
pub trace_len: usize,
|
||||
pub log: &'a LogLine,
|
||||
pub log: &'a LogLine<'a>,
|
||||
pub full_line: FullLogLine,
|
||||
pub table_state: ScrollbarTableState,
|
||||
pub previous: Box<UiState<'a>>,
|
||||
|
|
@ -97,7 +97,7 @@ impl<'a> UiState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn process(self, event: UiEvent, app: &'a App) -> (bool, UiState) {
|
||||
pub fn process(self, event: UiEvent, app: &'a App<'a>) -> (bool, UiState) {
|
||||
match (self, event) {
|
||||
(UiState::Quit, _) => (true, UiState::Quit),
|
||||
(_, UiEvent::Quit) => (true, UiState::Quit),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue