use crate::{
event::Event,
records::{EventRecord, SessionRecord},
Cli,
};
use chrono::{DateTime, Duration, Utc};
use std::{collections::VecDeque, fmt::Debug, net::IpAddr};
use uuid::Uuid;
#[derive(Debug)]
pub struct Session {
pub id: Uuid,
pub client: IpAddr,
pub command: String,
pub coordinator: IpAddr,
pub duration: Duration,
pub parameters: String,
pub request: String,
pub started_at: DateTime<Utc>,
pub request_size: Option<u32>,
pub response_size: Option<u32>,
pub username: Option<String>,
root_events: Vec<Event>,
}
impl Session
{
pub(crate) fn new(session_record: SessionRecord, event_records: Vec<EventRecord>) -> Self {
let (mut root_events, mut child_events): (VecDeque<Event>, VecDeque<Event>) = event_records
.into_iter()
.map(Event::from)
.partition(|event| event.parent_span_id.is_root());
'child_events: while let Some(child_event) = child_events.pop_front() {
let mut opt = Some(child_event);
'_root_search: for root_event in &mut root_events {
match root_event.try_add_child(opt.take().unwrap()) {
Ok(_) => continue 'child_events,
Err(child_event) => opt = Some(child_event),
}
}
child_events.push_back(opt.take().unwrap());
}
Self {
id: session_record.session_id,
client: session_record.client,
command: session_record.command,
coordinator: session_record.coordinator,
duration: Duration::microseconds(session_record.duration.into()),
parameters: session_record.parameters,
request: session_record.request,
request_size: session_record.request_size,
response_size: session_record.response_size,
started_at: session_record.started_at,
username: session_record.username,
root_events: root_events.into(),
}
}
pub fn event_count(&self) -> usize {
self.root_events
.iter()
.map(|e| e.count_including_children())
.sum::<usize>()
}
pub fn events(&self) -> Vec<(&Event, usize)> {
let count = self.event_count();
let mut events = Vec::with_capacity(count);
for root_event in &self.root_events {
root_event.recurse_events(&mut events, 0);
}
events
}
pub fn total_duration(&self) -> i64 {
self.root_events.iter().map(|e| e.durations().0).sum()
}
pub fn display(&self, cli: Cli, w: &mut dyn std::io::Write) -> std::io::Result<()> {
writeln!(w, "Session ID: {}", &self.id)?;
writeln!(w, "{}", &self.started_at.to_rfc3339())?;
writeln!(
w,
"{:15} ({}) -> {:15}",
&self.client,
&self.username.clone().unwrap_or_else(|| String::from("N/A")),
&self.coordinator
)?;
writeln!(
w,
"Request Size: {}",
&self
.request_size
.map(|rs| rs.to_string())
.unwrap_or_else(|| String::from("N/A"))
)?;
writeln!(
w,
"Response Size: {}",
&self
.response_size
.map(|rs| rs.to_string())
.unwrap_or_else(|| String::from("N/A"))
)?;
writeln!(w, "{}", &self.request)?;
writeln!(w, "{:?}", &self.parameters)?;
let s_end = self.total_duration();
let mut offset = 0i64;
let events = self.events();
let a_max_width = events
.iter()
.map(|(e, _)| e.activity_length())
.max()
.unwrap_or(0);
let max_depth = events.iter().map(|(_, depth)| *depth).max().unwrap_or(1);
let i_max_width = self.event_count().to_string().len();
writeln!(w)?;
writeln!(
w,
"{:i_max_width$} {:w_width$} {}",
"",
"waterfall chart",
crate::event_display_str(
&cli,
a_max_width,
"dur",
"node",
&format!("{:tree_width$}", "", tree_width = max_depth + 2),
"activity",
"event id",
"span id",
"parent span id",
"thread name",
),
w_width = *cli.waterfall_width + 2
)?;
for (i, (e, depth)) in events.iter().enumerate() {
writeln!(
w,
"{:i_max_width$} {} {}",
i + 1,
e.waterfall(&cli, offset, s_end),
e.display(&cli, a_max_width, *depth, max_depth)
)?;
offset += e.durations().1;
}
Ok(())
}
}