log trace

This commit is contained in:
Robin Appelman 2024-07-28 18:32:22 +02:00
commit aff67c43dd
4 changed files with 229 additions and 11 deletions

View file

@ -170,8 +170,14 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
);
frame.render_widget(footer(app, page), layout[2]);
}
UiState::Log(LogState { log, .. }) => {
frame.render_widget(single_log(app, log), layout[0].union(layout[1]));
UiState::Log(LogState {
log, table_state, ..
}) => {
frame.render_stateful_widget(
single_log(app, log),
layout[0].union(layout[1]),
table_state,
);
frame.render_widget(footer(app, page), layout[2]);
}
}

View file

@ -1,7 +1,98 @@
use crate::app::App;
use crate::logline::LogLine;
use ratatui::widgets::{Paragraph, Wrap};
use crate::logline::{FullException, FullLogLine, LogLine, Trace};
use crate::ui::style::{TABLE_HEADER_STYLE, TABLE_SELECTED_STYLE, TIME_FORMAT};
use ratatui::prelude::*;
use ratatui::widgets::{Cell, HighlightSpacing, Paragraph, Row, Table, TableState, Wrap};
use std::iter::once;
use time::format_description::well_known::Iso8601;
pub fn single_log<'a>(_app: &App, line: &'a LogLine) -> Paragraph<'a> {
Paragraph::new(line.display()).wrap(Wrap::default())
pub fn single_log(app: &App, line: &LogLine) -> SingleLog {
let raw_line = app.get_line(line.index).unwrap();
let line: FullLogLine = serde_json::from_str(raw_line).unwrap();
SingleLog { line }
}
pub struct SingleLog {
line: FullLogLine,
}
impl StatefulWidget for SingleLog {
type State = TableState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State)
where
Self: Sized,
{
let par = Paragraph::new(format!(
"{}\n\n {} {}\n {}\n\n from {} by {} at {}",
self.line.message,
self.line.method,
self.line.url,
self.line.user_agent,
self.line.remote_address,
self.line.user,
self.line.time.format(&Iso8601::<TIME_FORMAT>).unwrap()
))
.wrap(Wrap::default());
let layout = Layout::default()
.direction(Direction::Vertical)
.constraints(vec![Constraint::Min(7), Constraint::Percentage(100)])
.split(area);
par.render(layout[0], buf);
if let Some(exception) = &self.line.exception {
StatefulWidget::render(render_exception(exception), layout[1], buf, state);
}
}
}
pub fn render_exception(exception: &FullException) -> Table {
let header = [
Text::from("File"),
Text::from("Line").alignment(Alignment::Right),
Text::from("Function"),
]
.into_iter()
.map(Cell::from)
.collect::<Row>()
.style(TABLE_HEADER_STYLE)
.height(1);
let widths = [
Constraint::Percentage(40),
Constraint::Min(10),
Constraint::Percentage(60),
];
let rows = exception.stack().flat_map(exception_trace);
let table = Table::new(rows, widths)
.header(header)
.highlight_style(TABLE_SELECTED_STYLE)
.highlight_spacing(HighlightSpacing::Always);
table
}
fn exception_trace(exception: &FullException) -> impl Iterator<Item = Row> + '_ {
let exception_row = Row::new([
Text::from(""),
Text::from(exception.line.to_string()).alignment(Alignment::Right),
Text::from(exception.file.clone()),
])
.style(TABLE_HEADER_STYLE);
let trace_rows = exception.trace.iter().map(trace_line);
once(exception_row).chain(trace_rows)
}
fn trace_line(trace: &Trace) -> Row {
Row::new([
Text::from(trace.file.clone()),
Text::from(if trace.line > 0 {
trace.line.to_string()
} else {
String::new()
})
.alignment(Alignment::Right),
Text::from(trace.function().to_string()),
])
}

View file

@ -1,6 +1,6 @@
use crate::app::{App, LogMatch};
use crate::copy_osc;
use crate::logline::LogLine;
use crate::logline::{FullLogLine, LogLine};
use derive_more::From;
use ratatui::widgets::{ScrollbarState, TableState};
use table_state::TableStateExt;
@ -56,7 +56,10 @@ impl<'a> LogsState<'a> {
#[derive(Clone)]
pub struct LogState<'a> {
pub trace_len: usize,
pub log: &'a LogLine,
pub full_line: FullLogLine,
pub table_state: TableState,
pub previous: Box<UiState<'a>>,
}
@ -84,6 +87,7 @@ impl<'a> UiState<'a> {
UiState::MatchList(state) => Some(&mut state.table_state),
UiState::Match(state) => Some(&mut state.table_state),
UiState::Logs(state) => Some(&mut state.table_state),
UiState::Log(state) => Some(&mut state.table_state),
_ => None,
}
}
@ -102,6 +106,7 @@ impl<'a> UiState<'a> {
UiState::MatchList(_) => app.match_lines(),
UiState::Match(state) => state.result.grouped.len(),
UiState::Logs(state) => state.lines.len(),
UiState::Log(state) => state.trace_len,
_ => 0,
}
}
@ -115,8 +120,9 @@ impl<'a> UiState<'a> {
let count = state.row_count(app);
if let Some(table_state) = state.table_state() {
let pos = table_state.down(count, step);
let scroll_state = state.scroll_state().unwrap();
*scroll_state = scroll_state.position(pos);
if let Some(scroll_state) = state.scroll_state() {
*scroll_state = scroll_state.position(pos);
}
}
state
}
@ -124,8 +130,9 @@ impl<'a> UiState<'a> {
let count = state.row_count(app);
if let Some(table_state) = state.table_state() {
let pos = table_state.up(count, step);
let scroll_state = state.scroll_state().unwrap();
*scroll_state = scroll_state.position(pos);
if let Some(scroll_state) = state.scroll_state() {
*scroll_state = scroll_state.position(pos);
}
}
state
}
@ -168,8 +175,18 @@ impl<'a> UiState<'a> {
let line = state.lines[selected];
let log = &app.lines[line];
let raw_line = app.get_line(log.index).unwrap();
let full_line: FullLogLine = serde_json::from_str(raw_line).unwrap();
let trace_len = if let Some(exception) = &full_line.exception {
exception.stack().map(|e| 1 + e.trace.len()).sum()
} else {
0
};
UiState::Log(LogState {
log,
full_line,
trace_len,
table_state,
previous: Box::new(state.into()),
})
}