diff --git a/Cargo.lock b/Cargo.lock index b1920c3..ca51d08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -79,6 +88,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.112" @@ -140,6 +155,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" +[[package]] +name = "once_cell" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" + [[package]] name = "parking_lot" version = "0.11.2" @@ -165,6 +186,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + [[package]] name = "proc-macro2" version = "1.0.34" @@ -234,9 +261,20 @@ dependencies = [ "crossterm 0.22.1", "serde", "toml", + "tracing", + "tracing-subscriber", "tui", ] +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook" version = "0.3.12" @@ -296,6 +334,15 @@ dependencies = [ "redox_termios", ] +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + [[package]] name = "toml" version = "0.5.8" @@ -305,6 +352,63 @@ dependencies = [ "serde", ] +[[package]] +name = "tracing" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-log" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245da694cc7fc4729f3f418b304cb57789f1bed2a78c575407ab8a23f53cb4d3" +dependencies = [ + "ansi_term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + [[package]] name = "tui" version = "0.16.0" diff --git a/Cargo.toml b/Cargo.toml index e87e634..6e62fb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,6 @@ edition = "2021" crossterm = "0.22.1" serde = { version = "1.0.132", features = ["derive"] } toml = "0.5.8" +tracing = "0.1.29" +tracing-subscriber = "0.3.3" tui = { version = "0.16.0", features = ["crossterm"] } diff --git a/service-manager.log b/service-manager.log new file mode 100644 index 0000000..a5b6c82 --- /dev/null +++ b/service-manager.log @@ -0,0 +1,6 @@ + 0.000298909s INFO service_manager: Starting service-manager... + at src/main.rs:94 + + 0.002233976s INFO service_manager::controller: Entering main loop + at src/controller.rs:23 + diff --git a/src/controller.rs b/src/controller.rs index f472eeb..e700529 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -10,6 +10,7 @@ use std::process::{Child, Command, Stdio}; use std::sync::mpsc::TryRecvError; use std::sync::{mpsc, Arc, Mutex}; use std::time::Duration; +use tracing::{error, info, trace_span}; use tui::backend::Backend; use tui::widgets::TableState; use tui::Terminal; @@ -19,6 +20,8 @@ const STDIO_SEND_BUF_SIZE: usize = 512; pub type StdioSendBuf = ([u8; STDIO_SEND_BUF_SIZE], usize); pub fn run_app(terminal: &mut Terminal, mut app: App) -> SmResult { + info!("Entering main loop"); + loop { terminal.draw(|f| view::render_ui(f, &mut app))?; @@ -183,6 +186,8 @@ impl App { fn start_service(&mut self, index: usize) -> SmResult { let service = &mut self.table.services[index]; + trace_span!("Starting service", name = %service.name); + *service.status.lock()? = ServiceStatus::Running; let mut cmd = Command::new("sh"); @@ -219,14 +224,20 @@ impl App { let service_status = service.status.clone(); - std::thread::spawn(move || { - match child_process_thread(child, stdout_send, service_status, terminate_recv) { - Ok(_) => {} - Err(e) => { - let _ = std::fs::write("error.txt", e.to_string()); + let spawn_result = std::thread::Builder::new() + .name(format!("worker-{}", service.name)) + .spawn(move || { + match child_process_thread(child, stdout_send, service_status, terminate_recv) { + Ok(_) => {} + Err(err) => { + error!(error = %err, "Error processing service"); + } } - } - }); + }); + + if let Err(err) = spawn_result { + error!(error = %err, "Error spawning thread"); + } Ok(()) } @@ -244,6 +255,30 @@ fn child_process_thread( .take() .ok_or(SmError::Bug("Stdout of child could not be taken"))?; + let mut stderr = child + .stderr + .take() + .ok_or(SmError::Bug("Stderr of child could not be taken"))?; + + let stdout_send_2 = stdout_send.clone(); + std::thread::spawn(move || { + let mut stderr_buf = [0; STDIO_SEND_BUF_SIZE]; + match stderr.read(&mut stderr_buf) { + Ok(0) => {} + Ok(n) => { + let result = stdout_send_2 + .send((stderr_buf, n)) + .map_err(|_| SmError::Bug("Failed to send stderr to main thread")); + + if let Err(err) = result { + error!(error = %err); + } + } + Err(err) if err.kind() == io::ErrorKind::Interrupted => {} + Err(err) => error!(error = %err, "Error reading from stderr"), + }; + }); + let result = loop { match terminate_channel.try_recv() { Ok(_) | Err(TryRecvError::Disconnected) => break Ok(()), @@ -251,9 +286,8 @@ fn child_process_thread( } let mut stdout_buf = [0; STDIO_SEND_BUF_SIZE]; - match stdout.read(&mut stdout_buf) { - Ok(0) => continue, + Ok(0) => {} Ok(n) => { stdout_send .send((stdout_buf, n)) @@ -262,6 +296,22 @@ fn child_process_thread( Err(e) if e.kind() == io::ErrorKind::Interrupted => {} Err(e) => break Err(e.into()), }; + + match child.try_wait() { + Ok(None) => {} + Ok(Some(status)) => { + let mut status_lock = service_status.lock().map_err(|_| SmError::MutexPoisoned)?; + + *status_lock = match status.code() { + Some(0) => ServiceStatus::Exited, + Some(code) => ServiceStatus::Failed(code), + None => ServiceStatus::Killed, + }; + + return Ok(()); + } + Err(e) => break Err(e.into()), + } }; match child.kill() { diff --git a/src/main.rs b/src/main.rs index cd9e03a..a0cb9ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,8 +7,10 @@ use crossterm::execute; use crossterm::terminal::{ disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, }; +use std::fs::File; use std::io::StdoutLock; use std::{env, fs, io}; +use tracing::info; use tui::backend::CrosstermBackend; use tui::Terminal; @@ -16,6 +18,8 @@ use crate::model::config::Config; use crate::model::App; fn main() { + setup_logging(); + let file_path = env::args() .nth(1) .or_else(|| env::var("SERVICE_MANAGER_CONFIG_PATH").ok()) @@ -77,6 +81,19 @@ or use the environment variable SERVICE_MANAGER_CONFIG_PATH" } } +fn setup_logging() { + let log_file = File::create("service-manager.log").unwrap(); + + tracing_subscriber::fmt() + .with_timer(tracing_subscriber::fmt::time::uptime()) + .with_ansi(false) + .pretty() + .with_writer(log_file) + .init(); + + info!("Starting service-manager..."); +} + fn setup_terminal(mut stdout: StdoutLock) -> io::Result>> { enable_raw_mode()?; execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; diff --git a/src/model.rs b/src/model.rs index 48495c6..9787479 100644 --- a/src/model.rs +++ b/src/model.rs @@ -41,7 +41,7 @@ pub enum ServiceStatus { NotStarted, Running, Exited, - Failed(u8), + Failed(i32), Killed, }