add option to show parse errors

This commit is contained in:
Robin Appelman 2024-08-09 23:08:48 +02:00
commit d061e7b606
6 changed files with 97 additions and 19 deletions

View file

@ -16,6 +16,7 @@ pub struct App {
pub all: LogMatch,
pub unmatched: LogMatch,
pub log_file: LogFile,
pub error_lines: Vec<(String, serde_json::Error)>,
}
impl App {

View file

@ -72,35 +72,48 @@ fn main() -> MainResult {
let mut results: Vec<_> = lines
.enumerate()
.par_bridge()
.flat_map(|(index, line)| {
let mut parsed = parse_line(line).ok()?;
.map(|(index, line)| {
let mut parsed = parse_line(line);
if let Ok(parsed) = parsed.as_mut() {
parsed.index = index;
Some(parsed)
};
parsed.map_err(|err| (index, line, err))
})
.map(|parsed| {
parsed.map(|parsed| {
let log_match = matcher.match_log(&parsed);
(parsed, log_match)
})
})
.collect();
results.sort_by_key(|(line, _)| line.index);
results.sort_by_key(|res| match res {
Ok((line, _)) => line.index,
Err((index, _, _)) => *index,
});
let mut error_lines = Vec::with_capacity(32);
let mut parsed_lines = Vec::with_capacity(1024);
let mut unmatched_lines = Vec::with_capacity(256);
for (parsed_index, result) in results.into_iter().enumerate() {
let parsed = match result {
(parsed, Some(match_result)) => {
counts.entry(match_result).or_default().push(parsed_index);
parsed
}
(parsed, None) => {
unmatched_lines.push(parsed_index);
parsed
}
};
let mut parsed_index = 0;
for result in results.into_iter() {
match result {
Ok((parsed, Some(match_result))) => {
counts.entry(match_result).or_default().push(parsed_index);
parsed_lines.push(parsed);
parsed_index += 1;
}
Ok((parsed, None)) => {
unmatched_lines.push(parsed_index);
parsed_lines.push(parsed);
parsed_index += 1;
}
Err((_index, line, e)) => {
error_lines.push((line.to_string(), e));
}
}
}
let error_count = log_file.iter().count() - parsed_lines.len();
@ -125,6 +138,7 @@ fn main() -> MainResult {
last_date: parsed_lines.last().unwrap().time,
lines: parsed_lines,
log_statements: statements,
error_lines,
matches,
unmatched,
all,

22
src/ui/error_list.rs Normal file
View file

@ -0,0 +1,22 @@
use crate::app::App;
use crate::ui::style::TABLE_HEADER_STYLE;
use crate::ui::table::ScrollbarTable;
use ratatui::layout::Constraint;
use ratatui::text::Text;
use ratatui::widgets::{Cell, Row};
pub fn error_list(app: &App) -> ScrollbarTable {
let header = [Text::from("Error"), Text::from("Line")]
.into_iter()
.map(Cell::from)
.collect::<Row>()
.style(TABLE_HEADER_STYLE)
.height(1);
let widths = [Constraint::Percentage(50), Constraint::Percentage(50)];
ScrollbarTable::new(app.error_lines.iter().map(error_row), widths).header(header)
}
fn error_row((line, err): &(String, serde_json::Error)) -> Row {
Row::new([Text::from(format!("{err}")), Text::from(line.as_str())])
}

View file

@ -29,9 +29,10 @@ pub fn footer(app: &App, page: UiPage) -> Table {
fn help(page: UiPage) -> &'static str {
match page {
UiPage::MatchList => "«Q» Exit - «Enter» Select",
UiPage::MatchList => "«Q» Exit - «Enter» Select - «E» Show parse errors",
UiPage::Match => "«Q» Exit - «Enter» Select - «Esc» Back",
UiPage::Logs => "«Q» Exit - «Esc» Back - «C» Copy log line",
UiPage::Log => "«Q» Exit - «Esc» Back - «R» Toggle raw - «C» Copy log line",
UiPage::Errors => "«Q» Exit - «Esc» Back - «C» Copy log line",
}
}

View file

@ -1,12 +1,15 @@
use crate::app::App;
use crate::error::UiError;
use crate::ui::error_list::error_list;
use crate::ui::footer::footer;
use crate::ui::histogram::UiHistogram;
use crate::ui::match_list::match_list;
use crate::ui::raw_logs::raw_logs;
use crate::ui::single_log::single_log;
use crate::ui::single_match::grouped_lines;
use crate::ui::state::{LogState, LogsState, MatchListState, MatchState, UiEvent, UiPage, UiState};
use crate::ui::state::{
ErrorState, LogState, LogsState, MatchListState, MatchState, UiEvent, UiPage, UiState,
};
use ratatui::crossterm::event::{Event, KeyCode, KeyModifiers};
use ratatui::crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
@ -17,6 +20,7 @@ use ratatui::Terminal;
use std::io;
use std::io::stdout;
mod error_list;
mod footer;
mod histogram;
mod match_list;
@ -61,6 +65,7 @@ fn handle_events(page: UiPage) -> io::Result<Option<UiEvent>> {
}
KeyCode::Char('q') => Some(UiEvent::Quit),
KeyCode::Esc => Some(UiEvent::Back),
KeyCode::Char('e') if page == UiPage::MatchList => Some(UiEvent::Errors),
KeyCode::Left if page != UiPage::MatchList => Some(UiEvent::Back),
KeyCode::Down => Some(UiEvent::Down(1)),
KeyCode::Up => Some(UiEvent::Up(1)),
@ -137,5 +142,9 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
);
frame.render_widget(footer(app, page), layout[2]);
}
UiState::Errors(ErrorState { table_state, .. }) => {
frame.render_stateful_widget(error_list(app), layout[0].union(layout[1]), table_state);
frame.render_widget(footer(app, page), layout[2]);
}
}
}

View file

@ -11,6 +11,7 @@ pub enum UiState<'a> {
Match(MatchState<'a>),
Logs(LogsState<'a>),
Log(LogState<'a>),
Errors(ErrorState<'a>),
Quit,
}
@ -45,6 +46,12 @@ pub struct LogsState<'a> {
pub previous: Box<UiState<'a>>,
}
#[derive(Clone)]
pub struct ErrorState<'a> {
pub table_state: ScrollbarTableState,
pub previous: Box<UiState<'a>>,
}
impl<'a> LogsState<'a> {
fn selected(&self) -> usize {
self.table_state.selected()
@ -75,6 +82,7 @@ impl<'a> UiState<'a> {
UiState::Match(_) => UiPage::Match,
UiState::Logs(_) => UiPage::Logs,
UiState::Log(_) => UiPage::Log,
UiState::Errors(_) => UiPage::Errors,
}
}
@ -84,6 +92,7 @@ impl<'a> UiState<'a> {
UiState::Match(state) => Some(&mut state.table_state),
UiState::Logs(state) => Some(&mut state.table_state),
UiState::Log(state) => Some(&mut state.table_state),
UiState::Errors(state) => Some(&mut state.table_state),
_ => None,
}
}
@ -125,6 +134,16 @@ impl<'a> UiState<'a> {
}),
)
}
(UiState::MatchList(state), UiEvent::Errors) => {
let table_state = ScrollbarTableState::new(app.error_lines.len());
(
true,
UiState::Errors(ErrorState {
table_state,
previous: Box::new(state.into()),
}),
)
}
(UiState::Match(state), UiEvent::Select) => {
let selected = state.selected();
let mut table_state = TableState::default();
@ -181,10 +200,20 @@ impl<'a> UiState<'a> {
copy_osc(raw);
(false, UiState::Log(state))
}
(UiState::Errors(state), UiEvent::Copy) => {
let raw = app
.error_lines
.get(state.table_state.selected())
.map(|(line, _)| line.as_str())
.unwrap_or_default();
copy_osc(raw);
(false, UiState::Errors(state))
}
(
UiState::Match(MatchState { previous, .. })
| UiState::Logs(LogsState { previous, .. })
| UiState::Log(LogState { previous, .. }),
| UiState::Log(LogState { previous, .. })
| UiState::Errors(ErrorState { previous, .. }),
UiEvent::Back,
) => (true, *previous),
(state, _) => (false, state),
@ -197,6 +226,7 @@ pub enum UiEvent {
Back,
Up(usize),
Down(usize),
Errors,
Select,
Copy,
}
@ -207,4 +237,5 @@ pub enum UiPage {
Match,
Logs,
Log,
Errors,
}