feat: file-based logging to wraith.log for MCP startup diagnostics
Some checks failed
Build & Sign Wraith / Build Windows + Sign (push) Has been cancelled

env_logger was never initialized so log::info/error went nowhere.
Added write_log() that appends to data_dir/wraith.log with timestamps.
Logs MCP server startup success/failure and any panics.

Check: %APPDATA%\Wraith\wraith.log (Windows)
       ~/Library/Application Support/Wraith/wraith.log (macOS)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell 2026-03-25 00:55:31 -04:00
parent 44c79decf3
commit 0e88f9f07c

View File

@ -91,9 +91,29 @@ pub fn data_directory() -> PathBuf {
PathBuf::from(".")
}
fn write_log(path: &std::path::Path, msg: &str) -> std::io::Result<()> {
use std::io::Write;
let mut f = std::fs::OpenOptions::new().create(true).append(true).open(path)?;
let elapsed = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
writeln!(f, "[{}] {}", elapsed, msg)
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let app_state = AppState::new(data_directory()).expect("Failed to init AppState");
// Initialize file-based logging to data_dir/wraith.log
let log_path = data_directory().join("wraith.log");
let _ = write_log(&log_path, "=== Wraith starting ===");
let app_state = match AppState::new(data_directory()) {
Ok(s) => s,
Err(e) => {
let _ = write_log(&log_path, &format!("FATAL: AppState init failed: {}", e));
panic!("Failed to init AppState: {}", e);
}
};
app_state.theme.seed_builtins();
tauri::Builder::default()
@ -109,25 +129,30 @@ pub fn run() {
}
// Start MCP and error watcher — completely non-fatal.
// These are nice-to-have services that must never crash the app.
{
use tauri::Manager;
let log_file = data_directory().join("wraith.log");
let _ = write_log(&log_file, "Setup: starting MCP and error watcher");
if let Ok(state) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
app.state::<AppState>().inner().clone_services()
})) {
let (ssh, rdp, sftp, scrollback, watcher) = state;
let _ = write_log(&log_file, "Setup: cloned services OK");
let app_handle = app.handle().clone();
mcp::error_watcher::start_error_watcher(watcher, scrollback.clone(), app_handle);
let _ = write_log(&log_file, "Setup: error watcher started");
let log_file2 = log_file.clone();
tauri::async_runtime::spawn(async move {
match mcp::server::start_mcp_server(ssh, rdp, sftp, scrollback).await {
Ok(port) => log::info!("MCP server started on localhost:{}", port),
Err(e) => log::error!("Failed to start MCP server: {}", e),
Ok(port) => { let _ = write_log(&log_file2, &format!("MCP server started on localhost:{}", port)); }
Err(e) => { let _ = write_log(&log_file2, &format!("MCP server FAILED: {}", e)); }
}
});
} else {
log::error!("MCP/error watcher startup failed — continuing without MCP");
let _ = write_log(&log_file, "MCP startup panicked — continuing without MCP");
}
}