mirror of
https://git.alemi.dev/dashboard.git
synced 2024-11-22 23:44:55 +01:00
feat: made plots collapsable, moved log in footer
footer is also collapsing
This commit is contained in:
parent
4c40041048
commit
a75d6b432f
2 changed files with 250 additions and 142 deletions
327
src/app/mod.rs
327
src/app/mod.rs
|
@ -1,17 +1,20 @@
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod worker;
|
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
pub mod worker;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use chrono::{Local, Utc};
|
||||||
use chrono::{Utc, Local};
|
|
||||||
use tracing::error;
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use eframe::egui::plot::GridMark;
|
use eframe::egui::plot::GridMark;
|
||||||
use eframe::egui::{RichText, plot::{Line, Plot}, Color32};
|
use eframe::egui::{
|
||||||
|
plot::{Line, Plot},
|
||||||
|
Color32,
|
||||||
|
};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
use self::data::ApplicationState;
|
use self::data::ApplicationState;
|
||||||
use self::worker::native_save;
|
|
||||||
use self::util::{human_size, timestamp_to_str};
|
use self::util::{human_size, timestamp_to_str};
|
||||||
|
use self::worker::native_save;
|
||||||
|
|
||||||
struct InputBuffer {
|
struct InputBuffer {
|
||||||
panel_name: String,
|
panel_name: String,
|
||||||
|
@ -45,12 +48,15 @@ pub struct App {
|
||||||
data: Arc<ApplicationState>,
|
data: Arc<ApplicationState>,
|
||||||
input: InputBuffer,
|
input: InputBuffer,
|
||||||
edit: bool,
|
edit: bool,
|
||||||
show_log: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new(_cc: &eframe::CreationContext, data: Arc<ApplicationState>) -> Self {
|
pub fn new(_cc: &eframe::CreationContext, data: Arc<ApplicationState>) -> Self {
|
||||||
Self { data, input: InputBuffer::default(), edit: false, show_log: false }
|
Self {
|
||||||
|
data,
|
||||||
|
input: InputBuffer::default(),
|
||||||
|
edit: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +75,10 @@ impl eframe::App for App {
|
||||||
}
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.label("+ panel");
|
ui.label("+ panel");
|
||||||
eframe::egui::TextEdit::singleline(&mut self.input.panel_name).hint_text("name").desired_width(50.0).show(ui);
|
eframe::egui::TextEdit::singleline(&mut self.input.panel_name)
|
||||||
|
.hint_text("name")
|
||||||
|
.desired_width(50.0)
|
||||||
|
.show(ui);
|
||||||
if ui.button("add").clicked() {
|
if ui.button("add").clicked() {
|
||||||
if let Err(e) = self.data.add_panel(self.input.panel_name.as_str()) {
|
if let Err(e) = self.data.add_panel(self.input.panel_name.as_str()) {
|
||||||
error!(target: "ui", "Failed to add panel: {:?}", e);
|
error!(target: "ui", "Failed to add panel: {:?}", e);
|
||||||
|
@ -77,20 +86,35 @@ impl eframe::App for App {
|
||||||
}
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.label("+ source");
|
ui.label("+ source");
|
||||||
eframe::egui::TextEdit::singleline(&mut self.input.name).hint_text("name").desired_width(50.0).show(ui);
|
eframe::egui::TextEdit::singleline(&mut self.input.name)
|
||||||
eframe::egui::TextEdit::singleline(&mut self.input.url).hint_text("url").desired_width(160.0).show(ui);
|
.hint_text("name")
|
||||||
eframe::egui::TextEdit::singleline(&mut self.input.query_x).hint_text("x").desired_width(30.0).show(ui);
|
.desired_width(50.0)
|
||||||
eframe::egui::TextEdit::singleline(&mut self.input.query_y).hint_text("y").desired_width(30.0).show(ui);
|
.show(ui);
|
||||||
|
eframe::egui::TextEdit::singleline(&mut self.input.url)
|
||||||
|
.hint_text("url")
|
||||||
|
.desired_width(160.0)
|
||||||
|
.show(ui);
|
||||||
|
eframe::egui::TextEdit::singleline(&mut self.input.query_x)
|
||||||
|
.hint_text("x")
|
||||||
|
.desired_width(30.0)
|
||||||
|
.show(ui);
|
||||||
|
eframe::egui::TextEdit::singleline(&mut self.input.query_y)
|
||||||
|
.hint_text("y")
|
||||||
|
.desired_width(30.0)
|
||||||
|
.show(ui);
|
||||||
egui::ComboBox::from_id_source("panel")
|
egui::ComboBox::from_id_source("panel")
|
||||||
.selected_text(format!("panel [{}]", self.input.panel_id))
|
.selected_text(format!("panel [{}]", self.input.panel_id))
|
||||||
.width(70.0)
|
.width(70.0)
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
let pnls = self.data.panels.write().expect("Panels RwLock poisoned");
|
let pnls = self.data.panels.write().expect("Panels RwLock poisoned");
|
||||||
for p in &*pnls {
|
for p in &*pnls {
|
||||||
ui.selectable_value(&mut self.input.panel_id, p.id, p.name.as_str());
|
ui.selectable_value(
|
||||||
|
&mut self.input.panel_id,
|
||||||
|
p.id,
|
||||||
|
p.name.as_str(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
ui.checkbox(&mut self.input.visible, "visible");
|
ui.checkbox(&mut self.input.visible, "visible");
|
||||||
ui.add(egui::Slider::new(&mut self.input.interval, 1..=60));
|
ui.add(egui::Slider::new(&mut self.input.interval, 1..=60));
|
||||||
ui.color_edit_button_srgba(&mut self.input.color);
|
ui.color_edit_button_srgba(&mut self.input.color);
|
||||||
|
@ -114,29 +138,53 @@ impl eframe::App for App {
|
||||||
if ui.small_button("×").clicked() {
|
if ui.small_button("×").clicked() {
|
||||||
frame.quit();
|
frame.quit();
|
||||||
}
|
}
|
||||||
ui.checkbox(&mut self.show_log, "log");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
egui::TopBottomPanel::bottom("footer").show(ctx, |ui| {
|
egui::TopBottomPanel::bottom("footer").show(ctx, |ui| {
|
||||||
ui.horizontal(|ui|{
|
egui::collapsing_header::CollapsingState::load_with_default_open(
|
||||||
ui.label(self.data.file_path.to_str().unwrap()); // TODO maybe calculate it just once?
|
ctx,
|
||||||
ui.separator();
|
ui.make_persistent_id("footer-logs"),
|
||||||
ui.label(human_size(*self.data.file_size.read().expect("Filesize RwLock poisoned")));
|
false,
|
||||||
let diags = self.data.diagnostics.read().expect("Diagnostics RwLock poisoned");
|
)
|
||||||
if diags.len() > 0 {
|
.show_header(ui, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(self.data.file_path.to_str().unwrap()); // TODO maybe calculate it just once?
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.label(diags.last().unwrap_or(&"".to_string()));
|
ui.label(human_size(
|
||||||
}
|
*self
|
||||||
ui.with_layout(egui::Layout::top_down(egui::Align::RIGHT), |ui| {
|
.data
|
||||||
ui.horizontal(|ui| {
|
.file_size
|
||||||
ui.label(format!("v{}-{}", env!("CARGO_PKG_VERSION"), git_version::git_version!()));
|
.read()
|
||||||
ui.separator();
|
.expect("Filesize RwLock poisoned"),
|
||||||
ui.hyperlink_to("<me@alemi.dev>", "mailto:me@alemi.dev");
|
));
|
||||||
ui.label("alemi");
|
ui.with_layout(egui::Layout::top_down(egui::Align::RIGHT), |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(format!(
|
||||||
|
"v{}-{}",
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
git_version::git_version!()
|
||||||
|
));
|
||||||
|
ui.separator();
|
||||||
|
ui.hyperlink_to("<me@alemi.dev>", "mailto:me@alemi.dev");
|
||||||
|
ui.label("alemi");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.body(|ui| {
|
||||||
|
ui.set_height(200.0);
|
||||||
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
|
let msgs = self
|
||||||
|
.data
|
||||||
|
.diagnostics
|
||||||
|
.read()
|
||||||
|
.expect("Diagnostics RwLock poisoned");
|
||||||
|
for msg in msgs.iter() {
|
||||||
|
ui.label(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if self.edit {
|
if self.edit {
|
||||||
|
@ -147,20 +195,40 @@ impl eframe::App for App {
|
||||||
ui.group(|ui| {
|
ui.group(|ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.checkbox(&mut source.visible, "");
|
ui.checkbox(&mut source.visible, "");
|
||||||
eframe::egui::TextEdit::singleline(&mut source.name).hint_text("name").desired_width(80.0).show(ui);
|
eframe::egui::TextEdit::singleline(&mut source.name)
|
||||||
eframe::egui::TextEdit::singleline(&mut source.url).hint_text("url").desired_width(300.0).show(ui);
|
.hint_text("name")
|
||||||
|
.desired_width(80.0)
|
||||||
|
.show(ui);
|
||||||
|
eframe::egui::TextEdit::singleline(&mut source.url)
|
||||||
|
.hint_text("url")
|
||||||
|
.desired_width(300.0)
|
||||||
|
.show(ui);
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add(egui::Slider::new(&mut source.interval, 1..=60));
|
ui.add(egui::Slider::new(&mut source.interval, 1..=60));
|
||||||
eframe::egui::TextEdit::singleline(&mut source.query_x).hint_text("x").desired_width(50.0).show(ui);
|
eframe::egui::TextEdit::singleline(&mut source.query_x)
|
||||||
eframe::egui::TextEdit::singleline(&mut source.query_y).hint_text("y").desired_width(50.0).show(ui);
|
.hint_text("x")
|
||||||
|
.desired_width(50.0)
|
||||||
|
.show(ui);
|
||||||
|
eframe::egui::TextEdit::singleline(&mut source.query_y)
|
||||||
|
.hint_text("y")
|
||||||
|
.desired_width(50.0)
|
||||||
|
.show(ui);
|
||||||
egui::ComboBox::from_id_source(format!("panel-{}", source.id))
|
egui::ComboBox::from_id_source(format!("panel-{}", source.id))
|
||||||
.selected_text(format!("panel [{}]", source.panel_id))
|
.selected_text(format!("panel [{}]", source.panel_id))
|
||||||
.width(70.0)
|
.width(70.0)
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
let pnls = self.data.panels.read().expect("Panels RwLock poisoned");
|
let pnls = self
|
||||||
|
.data
|
||||||
|
.panels
|
||||||
|
.read()
|
||||||
|
.expect("Panels RwLock poisoned");
|
||||||
for p in &*pnls {
|
for p in &*pnls {
|
||||||
ui.selectable_value(&mut source.panel_id, p.id, p.name.as_str());
|
ui.selectable_value(
|
||||||
|
&mut source.panel_id,
|
||||||
|
p.id,
|
||||||
|
p.name.as_str(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.color_edit_button_srgba(&mut source.color);
|
ui.color_edit_button_srgba(&mut source.color);
|
||||||
|
@ -174,120 +242,143 @@ impl eframe::App for App {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if self.show_log {
|
let mut to_swap: Vec<usize> = Vec::new();
|
||||||
egui::SidePanel::right("logs-panel").show(ctx, |ui| {
|
|
||||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
|
||||||
ui.heading("logs");
|
|
||||||
ui.separator();
|
|
||||||
let msgs = self.data.diagnostics.read().expect("Diagnostics RwLock poisoned");
|
|
||||||
ui.group(|ui| {
|
|
||||||
for msg in msgs.iter() {
|
|
||||||
ui.label(msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let mut to_swap : Vec<usize> = Vec::new();
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
egui::ScrollArea::vertical().show(ui, |ui| {
|
egui::ScrollArea::vertical().show(ui, |ui| {
|
||||||
let mut panels = self.data.panels.write().expect("Panels RwLock poisoned"); // TODO only lock as write when editing
|
let mut panels = self.data.panels.write().expect("Panels RwLock poisoned"); // TODO only lock as write when editing
|
||||||
let sources = self.data.sources.read().expect("Sources RwLock poisoned"); // TODO only lock as write when editing
|
let sources = self.data.sources.read().expect("Sources RwLock poisoned"); // TODO only lock as write when editing
|
||||||
for (index, panel) in panels.iter_mut().enumerate() {
|
for (index, panel) in panels.iter_mut().enumerate() {
|
||||||
ui.group(|ui| {
|
if index > 0 {
|
||||||
ui.vertical(|ui| {
|
ui.separator();
|
||||||
ui.horizontal(|ui| {
|
}
|
||||||
ui.heading(panel.name.as_str());
|
egui::collapsing_header::CollapsingState::load_with_default_open(
|
||||||
ui.separator();
|
ctx,
|
||||||
if self.edit && index > 0 {
|
ui.make_persistent_id(format!("panel-{}-compressable", panel.id)),
|
||||||
if ui.small_button("up").clicked() {
|
true,
|
||||||
|
)
|
||||||
|
.show_header(ui, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if self.edit {
|
||||||
|
if ui.small_button(" ^ ").clicked() {
|
||||||
|
if index > 0 {
|
||||||
to_swap.push(index); // TODO kinda jank but is there a better way?
|
to_swap.push(index); // TODO kinda jank but is there a better way?
|
||||||
}
|
}
|
||||||
ui.separator();
|
|
||||||
}
|
}
|
||||||
for source in &*sources {
|
|
||||||
if source.panel_id == panel.id {
|
|
||||||
if source.visible {
|
|
||||||
ui.label(
|
|
||||||
RichText::new(source.name.as_str())
|
|
||||||
.color(if source.color == Color32::TRANSPARENT { Color32::GRAY } else { source.color })
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
ui.label(RichText::new(source.name.as_str()).color(Color32::BLACK));
|
|
||||||
}
|
|
||||||
ui.separator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ui.add(egui::Slider::new(&mut panel.height, 0..=500).text("height"));
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.checkbox(&mut panel.limit, "limit");
|
}
|
||||||
ui.add(egui::DragValue::new(&mut panel.view_size).speed(10).clamp_range(0..=2147483647));
|
ui.heading(panel.name.as_str());
|
||||||
ui.label("mins");
|
if self.edit {
|
||||||
|
ui.separator();
|
||||||
|
ui.add(
|
||||||
|
egui::Slider::new(&mut panel.height, 0..=500).text("height"),
|
||||||
|
);
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.checkbox(&mut panel.timeserie, "timeserie");
|
ui.checkbox(&mut panel.timeserie, "timeserie");
|
||||||
ui.checkbox(&mut panel.view_scroll, "autoscroll");
|
|
||||||
ui.separator();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut p = Plot::new(format!("plot-{}", panel.name))
|
|
||||||
.height(panel.height as f32)
|
|
||||||
.allow_scroll(false);
|
|
||||||
|
|
||||||
if panel.view_scroll {
|
|
||||||
p = p.include_x(Utc::now().timestamp() as f64);
|
|
||||||
if panel.limit {
|
|
||||||
p = p.include_x((Utc::now().timestamp() - (panel.view_size as i64 * 60)) as f64);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
ui.with_layout(egui::Layout::top_down(egui::Align::RIGHT), |ui| {
|
||||||
if panel.timeserie {
|
ui.horizontal(|ui| {
|
||||||
p = p.x_axis_formatter(|x, _range| timestamp_to_str(x as i64, true, false));
|
ui.toggle_value(&mut panel.view_scroll, " • ");
|
||||||
p = p.label_formatter(|name, value| {
|
ui.separator();
|
||||||
if !name.is_empty() {
|
ui.label("m");
|
||||||
return format!("{}\nx = {}\ny = {:.1}", name, timestamp_to_str(value.x as i64, false, true), value.y)
|
ui.add(
|
||||||
} else {
|
egui::DragValue::new(&mut panel.view_size)
|
||||||
return format!("x = {}\ny = {:.1}", timestamp_to_str(value.x as i64, false, true), value.y);
|
.speed(10)
|
||||||
}
|
.clamp_range(0..=2147483647i32),
|
||||||
|
);
|
||||||
|
ui.checkbox(&mut panel.limit, "limit");
|
||||||
});
|
});
|
||||||
p = p.x_grid_spacer(|grid| {
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.body(|ui| {
|
||||||
|
let mut p = Plot::new(format!("plot-{}", panel.name))
|
||||||
|
.height(panel.height as f32)
|
||||||
|
.allow_scroll(false)
|
||||||
|
.legend(egui::plot::Legend::default().position(egui::plot::Corner::LeftTop));
|
||||||
|
|
||||||
|
if panel.view_scroll {
|
||||||
|
p = p
|
||||||
|
.include_x(Utc::now().timestamp() as f64);
|
||||||
|
if panel.limit {
|
||||||
|
p = p.include_x(
|
||||||
|
(Utc::now().timestamp() - (panel.view_size as i64 * 60))
|
||||||
|
as f64,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if panel.timeserie {
|
||||||
|
p = p
|
||||||
|
.x_axis_formatter(|x, _range| {
|
||||||
|
timestamp_to_str(x as i64, true, false)
|
||||||
|
})
|
||||||
|
.label_formatter(|name, value| {
|
||||||
|
if !name.is_empty() {
|
||||||
|
return format!(
|
||||||
|
"{}\nx = {}\ny = {:.1}",
|
||||||
|
name,
|
||||||
|
timestamp_to_str(value.x as i64, false, true),
|
||||||
|
value.y
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return format!(
|
||||||
|
"x = {}\ny = {:.1}",
|
||||||
|
timestamp_to_str(value.x as i64, false, true),
|
||||||
|
value.y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.x_grid_spacer(|grid| {
|
||||||
let offset = Local::now().offset().local_minus_utc() as i64;
|
let offset = Local::now().offset().local_minus_utc() as i64;
|
||||||
let (start, end) = grid.bounds;
|
let (start, end) = grid.bounds;
|
||||||
let mut counter = (start as i64) - ((start as i64) % 3600);
|
let mut counter = (start as i64) - ((start as i64) % 3600);
|
||||||
let mut out : Vec<GridMark> = Vec::new();
|
let mut out: Vec<GridMark> = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
counter += 3600;
|
counter += 3600;
|
||||||
if counter > end as i64 { break; }
|
if counter > end as i64 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (counter + offset) % 86400 == 0 {
|
if (counter + offset) % 86400 == 0 {
|
||||||
out.push(GridMark { value: counter as f64, step_size: 86400 as f64 })
|
out.push(GridMark {
|
||||||
|
value: counter as f64,
|
||||||
|
step_size: 86400 as f64,
|
||||||
|
})
|
||||||
} else if counter % 3600 == 0 {
|
} else if counter % 3600 == 0 {
|
||||||
out.push(GridMark { value: counter as f64, step_size: 3600 as f64 });
|
out.push(GridMark {
|
||||||
|
value: counter as f64,
|
||||||
|
step_size: 3600 as f64,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
p.show(ui, |plot_ui| {
|
p.show(ui, |plot_ui| {
|
||||||
for source in &*sources {
|
for source in &*sources {
|
||||||
if source.visible && source.panel_id == panel.id {
|
if source.visible && source.panel_id == panel.id {
|
||||||
let line = if panel.limit {
|
let line = if panel.limit {
|
||||||
Line::new(source.values_filter((Utc::now().timestamp() - (panel.view_size as i64 * 60)) as f64)).name(source.name.as_str())
|
Line::new(source.values_filter(
|
||||||
} else {
|
(Utc::now().timestamp()
|
||||||
Line::new(source.values()).name(source.name.as_str())
|
- (panel.view_size as i64 * 60)) as f64,
|
||||||
};
|
))
|
||||||
plot_ui.line(line.color(source.color));
|
.name(source.name.as_str())
|
||||||
}
|
} else {
|
||||||
|
Line::new(source.values()).name(source.name.as_str())
|
||||||
|
};
|
||||||
|
plot_ui.line(line.color(source.color));
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if !to_swap.is_empty() { // TODO can this be done in background? idk
|
if !to_swap.is_empty() {
|
||||||
|
// TODO can this be done in background? idk
|
||||||
let mut panels = self.data.panels.write().expect("Panels RwLock poisoned");
|
let mut panels = self.data.panels.write().expect("Panels RwLock poisoned");
|
||||||
for index in to_swap {
|
for index in to_swap {
|
||||||
panels.swap(index-1, index);
|
panels.swap(index - 1, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::sync::Arc;
|
use chrono::{DateTime, Local, NaiveDateTime, Utc};
|
||||||
use chrono::{DateTime, NaiveDateTime, Utc, Local};
|
|
||||||
use tracing_subscriber::Layer;
|
|
||||||
use eframe::egui::Color32;
|
use eframe::egui::Color32;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tracing_subscriber::Layer;
|
||||||
|
|
||||||
use super::data::ApplicationState;
|
use super::data::ApplicationState;
|
||||||
|
|
||||||
|
@ -22,17 +22,19 @@ pub fn human_size(size: u64) -> String {
|
||||||
pub fn timestamp_to_str(t: i64, date: bool, time: bool) -> String {
|
pub fn timestamp_to_str(t: i64, date: bool, time: bool) -> String {
|
||||||
format!(
|
format!(
|
||||||
"{}",
|
"{}",
|
||||||
DateTime::<Local>::from(DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(t, 0), Utc)).format(
|
DateTime::<Local>::from(DateTime::<Utc>::from_utc(
|
||||||
if date && time {
|
NaiveDateTime::from_timestamp(t, 0),
|
||||||
"%Y/%m/%d %H:%M:%S"
|
Utc
|
||||||
} else if date {
|
))
|
||||||
"%Y/%m/%d"
|
.format(if date && time {
|
||||||
} else if time {
|
"%Y/%m/%d %H:%M:%S"
|
||||||
"%H:%M:%S"
|
} else if date {
|
||||||
} else {
|
"%Y/%m/%d"
|
||||||
"%s"
|
} else if time {
|
||||||
}
|
"%H:%M:%S"
|
||||||
)
|
} else {
|
||||||
|
"%s"
|
||||||
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,21 +66,36 @@ impl InternalLogger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> Layer<S> for InternalLogger where S: tracing::Subscriber {
|
impl<S> Layer<S> for InternalLogger
|
||||||
|
where
|
||||||
|
S: tracing::Subscriber,
|
||||||
|
{
|
||||||
fn on_event(
|
fn on_event(
|
||||||
&self,
|
&self,
|
||||||
event: &tracing::Event<'_>,
|
event: &tracing::Event<'_>,
|
||||||
_ctx: tracing_subscriber::layer::Context<'_, S>,
|
_ctx: tracing_subscriber::layer::Context<'_, S>,
|
||||||
) {
|
) {
|
||||||
let mut msg_visitor = LogMessageVisitor { msg: "".to_string() };
|
let mut msg_visitor = LogMessageVisitor {
|
||||||
|
msg: "".to_string(),
|
||||||
|
};
|
||||||
event.record(&mut msg_visitor);
|
event.record(&mut msg_visitor);
|
||||||
let out = format!("{} [{}] {}: {}", Local::now().format("%H:%M:%S"), event.metadata().level(), event.metadata().target(), msg_visitor.msg);
|
let out = format!(
|
||||||
self.state.diagnostics.write().expect("Diagnostics RwLock poisoned").push(out);
|
"{} [{}] {}: {}",
|
||||||
|
Local::now().format("%H:%M:%S"),
|
||||||
|
event.metadata().level(),
|
||||||
|
event.metadata().target(),
|
||||||
|
msg_visitor.msg
|
||||||
|
);
|
||||||
|
self.state
|
||||||
|
.diagnostics
|
||||||
|
.write()
|
||||||
|
.expect("Diagnostics RwLock poisoned")
|
||||||
|
.push(out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LogMessageVisitor {
|
struct LogMessageVisitor {
|
||||||
msg : String,
|
msg: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl tracing::field::Visit for LogMessageVisitor {
|
impl tracing::field::Visit for LogMessageVisitor {
|
||||||
|
|
Loading…
Reference in a new issue