mirror of
https://github.com/Astatin3/unshell.git
synced 2026-06-08 22:38:01 -06:00
Improve log viewer
This commit is contained in:
@@ -1,42 +1,205 @@
|
||||
use chrono::DateTime;
|
||||
use chrono::Utc;
|
||||
use egui::Color32;
|
||||
use egui::TextStyle;
|
||||
use egui_extras::Column;
|
||||
use egui_extras::TableBuilder;
|
||||
|
||||
use crate::auth::Auth;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::time::SystemTime;
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize)]
|
||||
pub struct LogViewer {
|
||||
enable_location: bool,
|
||||
#[serde(skip)]
|
||||
state: Arc<Mutex<LogState>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct LogState {
|
||||
logs: Vec<String>,
|
||||
logs: Vec<Record>,
|
||||
// trees: Option<Vec<String>>,
|
||||
// tree_keys: Option<HashMap<String, String>>,
|
||||
// is_requesting: bool,
|
||||
is_requesting: bool,
|
||||
requested_data: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum LogLevel {
|
||||
Debug,
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Record {
|
||||
log_level: LogLevel,
|
||||
location: Option<String>,
|
||||
time: SystemTime,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl Record {
|
||||
pub fn display_level(&self, ui: &mut egui::Ui) {
|
||||
match self.log_level {
|
||||
LogLevel::Debug => ui.colored_label(Color32::LIGHT_BLUE, "DBUG"),
|
||||
LogLevel::Info => ui.colored_label(Color32::DARK_GREEN, "INFO"),
|
||||
LogLevel::Warn => ui.colored_label(Color32::YELLOW, "WARN"),
|
||||
LogLevel::Error => ui.colored_label(Color32::RED, "ERR!"),
|
||||
};
|
||||
}
|
||||
pub fn display_location(&self, ui: &mut egui::Ui) {
|
||||
if let Some(ref location) = self.location {
|
||||
ui.label(location);
|
||||
}
|
||||
}
|
||||
pub fn display_time(&self, ui: &mut egui::Ui) {
|
||||
let date: DateTime<Utc> = self.time.into();
|
||||
let date = date.to_rfc2822().to_string();
|
||||
ui.label(date);
|
||||
}
|
||||
pub fn display_message(&self, ui: &mut egui::Ui) {
|
||||
ui.strong(&self.message);
|
||||
}
|
||||
}
|
||||
|
||||
impl LogViewer {
|
||||
pub fn update(&mut self, auth: &mut Auth, ui: &mut egui::Ui) {
|
||||
ui.heading("Log Viewer");
|
||||
for log in &self.state.lock().unwrap().logs {
|
||||
ui.label(log);
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Refresh").clicked() {
|
||||
self.refresh_logs(auth);
|
||||
}
|
||||
if ui.button("Poll").clicked() {
|
||||
let state_clone = self.state.clone();
|
||||
auth.get(&format!("/api/log/{}", 0), move |e: Vec<String>| {
|
||||
(*state_clone.lock().unwrap()).logs = e;
|
||||
// crate::log(&format!("{e:?}"));
|
||||
|
||||
ui.checkbox(&mut self.enable_location, "Enable Location");
|
||||
});
|
||||
|
||||
// let logs = ;
|
||||
|
||||
let body_text_size = TextStyle::Body.resolve(ui.style()).size;
|
||||
use egui_extras::{Size, StripBuilder};
|
||||
StripBuilder::new(ui)
|
||||
.size(Size::remainder().at_least(100.0)) // for the table
|
||||
.size(Size::exact(body_text_size))
|
||||
.vertical(|mut strip| {
|
||||
strip.cell(|ui| {
|
||||
egui::ScrollArea::both()
|
||||
.stick_to_bottom(true)
|
||||
.show(ui, |ui| {
|
||||
let table = TableBuilder::new(ui)
|
||||
.striped(true)
|
||||
.resizable(true)
|
||||
.stick_to_bottom(true)
|
||||
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
|
||||
.column(Column::auto())
|
||||
.column(Column::auto())
|
||||
.column(Column::auto())
|
||||
.min_scrolled_height(0.0)
|
||||
.sense(egui::Sense::click());
|
||||
|
||||
let table = if self.enable_location {
|
||||
table.column(Column::auto())
|
||||
} else {
|
||||
table
|
||||
};
|
||||
|
||||
table
|
||||
.header(20., |mut header| {
|
||||
header.col(|ui| {
|
||||
ui.strong("Time");
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("Level");
|
||||
});
|
||||
header.col(|ui| {
|
||||
ui.strong("Message");
|
||||
});
|
||||
if self.enable_location {
|
||||
header.col(|ui| {
|
||||
ui.strong("Location");
|
||||
});
|
||||
}
|
||||
})
|
||||
.body(|mut body| {
|
||||
let state_lock = self.state.lock().unwrap();
|
||||
// let logs = state_lock.logs.as_ref();
|
||||
|
||||
for log in state_lock.logs.iter() {
|
||||
// // let runtime = self.current_runtimes
|
||||
|
||||
body.row(18., |mut row| {
|
||||
row.col(|ui| log.display_time(ui));
|
||||
row.col(|ui| log.display_level(ui));
|
||||
row.col(|ui| log.display_message(ui));
|
||||
if self.enable_location {
|
||||
row.col(|ui| log.display_location(ui));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
{
|
||||
let state_lock = self.state.lock().unwrap();
|
||||
|
||||
match (
|
||||
state_lock.is_requesting,
|
||||
state_lock.logs.len() == 0,
|
||||
state_lock.requested_data,
|
||||
) {
|
||||
(true, _, _) => {
|
||||
drop(state_lock);
|
||||
ui.spinner();
|
||||
}
|
||||
(false, true, true) => {
|
||||
drop(state_lock);
|
||||
ui.label("There are no logs");
|
||||
}
|
||||
(false, true, false) => {
|
||||
drop(state_lock);
|
||||
self.refresh_logs(auth);
|
||||
}
|
||||
_ => {
|
||||
drop(state_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh_logs(&self, auth: &mut Auth) {
|
||||
let state_clone = self.state.clone();
|
||||
{
|
||||
let mut state_lock = self.state.lock().unwrap();
|
||||
state_lock.logs.clear();
|
||||
state_lock.is_requesting = true;
|
||||
}
|
||||
auth.get(&format!("/api/log/{}", 0), move |e: Vec<String>| {
|
||||
let mut state_lock = state_clone.lock().unwrap();
|
||||
state_lock.logs.append(
|
||||
&mut e
|
||||
.iter()
|
||||
.map(|log| serde_json::from_str(log).unwrap())
|
||||
.collect(),
|
||||
);
|
||||
state_lock.is_requesting = false;
|
||||
state_lock.requested_data = true;
|
||||
|
||||
// crate::log(&format!("{e:?}"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LogViewer {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
// logs: Vec::new(),
|
||||
enable_location: false,
|
||||
state: Arc::new(Mutex::new(LogState::default())),
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+146
-6
@@ -57,6 +57,18 @@ dependencies = [
|
||||
"virtue",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@@ -115,6 +127,18 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.42"
|
||||
@@ -303,6 +327,15 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.28"
|
||||
@@ -330,6 +363,29 @@ version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
@@ -392,6 +448,15 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.2"
|
||||
@@ -433,6 +498,12 @@ version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
@@ -460,7 +531,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -493,6 +564,51 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "static_init"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bae1df58c5fea7502e8e352ec26b5579f6178e1fdb311e088580c980dee25ed"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cfg_aliases 0.2.1",
|
||||
"libc",
|
||||
"parking_lot",
|
||||
"parking_lot_core",
|
||||
"static_init_macro",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_init_macro"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1389c88ddd739ec6d3f8f83343764a0e944cd23cfbf126a9796a714b0b6edd6f"
|
||||
dependencies = [
|
||||
"cfg_aliases 0.1.1",
|
||||
"memchr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.109"
|
||||
@@ -551,7 +667,9 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"rand",
|
||||
"static_init",
|
||||
"syn 2.0.109",
|
||||
"unshell-crypt",
|
||||
]
|
||||
|
||||
@@ -614,7 +732,7 @@ dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.109",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -627,6 +745,28 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.62.2"
|
||||
@@ -648,7 +788,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -659,7 +799,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -709,5 +849,5 @@ checksum = "c640b22cd9817fae95be82f0d2f90b11f7605f6c319d16705c459b27ac2cbc26"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.109",
|
||||
]
|
||||
|
||||
@@ -3,7 +3,7 @@ name = "unshell-lib"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
default = ["client", "server", "log_debug"]
|
||||
default = ["client", "server", "log"]
|
||||
|
||||
# Components
|
||||
client = []
|
||||
|
||||
@@ -13,7 +13,7 @@ pub use pretty_logger::PrettyLogger;
|
||||
|
||||
static mut LOGGER: &dyn Logger = &DefaultLogger;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub enum LogLevel {
|
||||
Debug,
|
||||
Info,
|
||||
@@ -21,7 +21,7 @@ pub enum LogLevel {
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Record {
|
||||
log_level: LogLevel,
|
||||
location: Option<String>,
|
||||
|
||||
Generated
+2
@@ -1549,6 +1549,8 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rand",
|
||||
"static_init",
|
||||
"syn 2.0.111",
|
||||
"unshell-crypt",
|
||||
]
|
||||
|
||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
log_debug = []
|
||||
log_debug = ["unshell-lib/log_debug"]
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
mod api;
|
||||
pub mod logger;
|
||||
mod modules;
|
||||
mod server;
|
||||
pub use api::app::start_api;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::path::PathBuf;
|
||||
const LOG_DIR: &str = "logs";
|
||||
|
||||
/// The maximum number of logs to return in one call to `poll_logs`.
|
||||
const LOG_COUNT: usize = 10;
|
||||
const LOG_COUNT: usize = 100;
|
||||
|
||||
/// The full path to the log file.
|
||||
/// Initialized once based on the startup time.
|
||||
@@ -47,8 +47,7 @@ impl Logger {
|
||||
/// * `message` - The string content of the log entry.
|
||||
pub fn log(message: String) {
|
||||
// 1. Format the complete log line with timestamp
|
||||
let now = Local::now();
|
||||
let log_line = format!("[{}] {}\n", now.format("%Y-%m-%d %H:%M:%S%.3f"), message);
|
||||
let log_line = format!("{}\n", message);
|
||||
|
||||
// 2. Open the file in append mode, creating it if it doesn't exist.
|
||||
// The file path is guaranteed to be valid due to the `lazy_static` initialization.
|
||||
@@ -117,9 +116,7 @@ impl Logger {
|
||||
// Get the slice of the lines
|
||||
let slice = &lines[start_index..end_index];
|
||||
|
||||
// The logs are currently in historical order (oldest to newest within the slice).
|
||||
// We must reverse them to return the "most recent" logs in descending order.
|
||||
slice.iter().rev().cloned().collect()
|
||||
slice.iter().cloned().collect()
|
||||
}
|
||||
Err(e) => {
|
||||
// Return an empty vector and print an error message if the file cannot be read
|
||||
|
||||
@@ -26,7 +26,9 @@ async fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
unshell_lib::logger::PrettyLogger::init_output(|message| {
|
||||
unshell_server::logger::Logger::log(format!("{message:?}"));
|
||||
if let Ok(json) = serde_json::to_string(message) {
|
||||
unshell_server::logger::Logger::log(json);
|
||||
}
|
||||
});
|
||||
|
||||
let database = Server::new(args.database_name);
|
||||
|
||||
Reference in New Issue
Block a user