From af2f37ccb110eb1a5e20e831e6fa1ab915e9abc1 Mon Sep 17 00:00:00 2001 From: Jamie Pine Date: Sun, 25 Jan 2026 20:55:13 -0800 Subject: [PATCH] Enhance server startup logging and error handling - Implemented detailed logging for server startup in server.py, including Python version, executable path, and parsed arguments. - Added error handling for module imports and server initialization to improve robustness. - Introduced an Entitlements.plist file for macOS to manage security settings. - Updated tauri.conf.json to reference the new Entitlements.plist. - Enhanced error reporting in main.rs for better debugging during server process management. --- backend/server.py | 107 +++++++++++++++++++---------- tauri/src-tauri/Entitlements.plist | 12 ++++ tauri/src-tauri/gen/Assets.car | Bin 3847048 -> 3847048 bytes tauri/src-tauri/src/main.rs | 47 +++++++++++-- tauri/src-tauri/tauri.conf.json | 3 +- 5 files changed, 128 insertions(+), 41 deletions(-) create mode 100644 tauri/src-tauri/Entitlements.plist diff --git a/backend/server.py b/backend/server.py index 2dfb0cd9..15f94a67 100644 --- a/backend/server.py +++ b/backend/server.py @@ -5,45 +5,80 @@ This module provides an entry point that works with PyInstaller by using absolute imports instead of relative imports. """ -import argparse -import uvicorn +import sys +import logging -# Import the FastAPI app from the backend package -from backend.main import app -from backend import config, database +# Set up logging FIRST, before any imports that might fail +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + stream=sys.stderr, # Log to stderr so it's captured by Tauri +) +logger = logging.getLogger(__name__) + +# Log startup immediately to confirm binary execution +logger.info("=" * 60) +logger.info("voicebox-server starting up...") +logger.info(f"Python version: {sys.version}") +logger.info(f"Executable: {sys.executable}") +logger.info(f"Arguments: {sys.argv}") +logger.info("=" * 60) + +try: + import argparse + import uvicorn + logger.info("Standard library imports successful") + + # Import the FastAPI app from the backend package + logger.info("Importing backend modules...") + from backend.main import app + from backend import config, database + logger.info("Backend imports successful") +except Exception as e: + logger.error(f"Failed to import required modules: {e}", exc_info=True) + sys.exit(1) if __name__ == "__main__": - parser = argparse.ArgumentParser(description="voicebox backend server") - parser.add_argument( - "--host", - type=str, - default="127.0.0.1", - help="Host to bind to (use 0.0.0.0 for remote access)", - ) - parser.add_argument( - "--port", - type=int, - default=8000, - help="Port to bind to", - ) - parser.add_argument( - "--data-dir", - type=str, - default=None, - help="Data directory for database, profiles, and generated audio", - ) - args = parser.parse_args() + try: + parser = argparse.ArgumentParser(description="voicebox backend server") + parser.add_argument( + "--host", + type=str, + default="127.0.0.1", + help="Host to bind to (use 0.0.0.0 for remote access)", + ) + parser.add_argument( + "--port", + type=int, + default=8000, + help="Port to bind to", + ) + parser.add_argument( + "--data-dir", + type=str, + default=None, + help="Data directory for database, profiles, and generated audio", + ) + args = parser.parse_args() + logger.info(f"Parsed arguments: host={args.host}, port={args.port}, data_dir={args.data_dir}") - # Set data directory if provided - if args.data_dir: - config.set_data_dir(args.data_dir) + # Set data directory if provided + if args.data_dir: + logger.info(f"Setting data directory to: {args.data_dir}") + config.set_data_dir(args.data_dir) - # Initialize database after data directory is set - database.init_db() + # Initialize database after data directory is set + logger.info("Initializing database...") + database.init_db() + logger.info("Database initialized successfully") - uvicorn.run( - app, - host=args.host, - port=args.port, - log_level="info", - ) + logger.info(f"Starting uvicorn server on {args.host}:{args.port}...") + uvicorn.run( + app, + host=args.host, + port=args.port, + log_level="info", + ) + except Exception as e: + logger.error(f"Server startup failed: {e}", exc_info=True) + sys.exit(1) diff --git a/tauri/src-tauri/Entitlements.plist b/tauri/src-tauri/Entitlements.plist new file mode 100644 index 00000000..46f67566 --- /dev/null +++ b/tauri/src-tauri/Entitlements.plist @@ -0,0 +1,12 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + + diff --git a/tauri/src-tauri/gen/Assets.car b/tauri/src-tauri/gen/Assets.car index 0795abd4efed96fc65d18a761a220c016d9b6ada..ce0088a7316c616cdd18293ec40a0cc3daa62f17 100644 GIT binary patch delta 820 zcmZwFyG~S56b4|15gBgc1r!hjRJ<={-!H`E%)XuQ2$Xhq#?r#hVrY>VD|-?wOehY8 zg>S$M&^xg((T3Ka5E|hmU$b}eueH}Y+dqG`w&B~++S1t2&`<}a;TlZABuv2g!JoCG z=^Zw*yJC7D&C2c%-P?U-Ubc=8zL>wml{QHg5jd3~3n{4*MVydep5 zBxO$8mP_g+EuA{6v)F7`olotGr5FUuifqmd4M&HDogrmHh`2K`^NS5?PB9lnbzR#~GhJBLmjr3W?0L{2cjns_=c2Y&Y7#a^Qb{WE!X>g= z)xkR^q{hjABRm~?E@we={jJRojKMfez$8q;HJFC$a070_Etr8>xD9vUG-DnX;4Un} z65NAjSbEf0$ui4{ zot+|blT^-%rOlTwp6{=<-}G&_ZyPWQO}GGKa1qAqzHOzkN~_?rvyrsSie$=~a4RXW z5R%F*s)*8-Xn~CJo@CAic@s0G$ctENrJ4IryV}KYp7ScS z78(_kk}1XUFf&F{yFlJEMNApvq;!@EKHN{vygbk8$JSD_icu?9YEDr&)oA4r>B)N)+WycWl;o@o=Nh_L^7N+l?o>lFNS84 zWy*hd)9xO09s|K9Yykrf0t%3z>VuB1e^dpt_y&nnaspqFJ4Y^SO|~@F$7nc{YN#G( kF8IL?+uh@>5)9b-cx$`)y0Q5F^WpYNW4``+xczbb4=wHD5dZ)H diff --git a/tauri/src-tauri/src/main.rs b/tauri/src-tauri/src/main.rs index 68a4c29e..13f0894c 100644 --- a/tauri/src-tauri/src/main.rs +++ b/tauri/src-tauri/src/main.rs @@ -31,10 +31,21 @@ async fn start_server( std::fs::create_dir_all(&data_dir) .map_err(|e| format!("Failed to create data dir: {}", e))?; + println!("================================================================="); + println!("Starting voicebox-server sidecar"); + println!("Data directory: {:?}", data_dir); + println!("Remote mode: {}", remote.unwrap_or(false)); + let mut sidecar = app .shell() .sidecar("voicebox-server") - .map_err(|e| format!("Failed to get sidecar: {}", e))?; + .map_err(|e| { + eprintln!("Failed to get sidecar: {}", e); + eprintln!("This usually means the binary is not bundled correctly or doesn't have execute permissions"); + format!("Failed to get sidecar: {}", e) + })?; + + println!("Sidecar command created successfully"); // Pass data directory to Python server sidecar = sidecar.args([ @@ -48,9 +59,21 @@ async fn start_server( sidecar = sidecar.args(["--host", "0.0.0.0"]); } + println!("Spawning server process..."); let (mut rx, child) = sidecar .spawn() - .map_err(|e| format!("Failed to spawn: {}", e))?; + .map_err(|e| { + eprintln!("Failed to spawn server process: {}", e); + eprintln!("This could be due to:"); + eprintln!(" - Missing or corrupted binary"); + eprintln!(" - Missing execute permissions"); + eprintln!(" - Code signing issues on macOS"); + eprintln!(" - Missing dependencies"); + format!("Failed to spawn: {}", e) + })?; + + println!("Server process spawned, waiting for ready signal..."); + println!("================================================================="); // Store child process *state.child.lock().unwrap() = Some(child); @@ -58,10 +81,18 @@ async fn start_server( // Wait for server to be ready by listening for startup log let timeout = tokio::time::Duration::from_secs(30); let start_time = tokio::time::Instant::now(); + let mut error_output = Vec::new(); loop { if start_time.elapsed() > timeout { - return Err("Server startup timeout".to_string()); + eprintln!("Server startup timeout after 30 seconds"); + if !error_output.is_empty() { + eprintln!("Collected error output:"); + for line in &error_output { + eprintln!(" {}", line); + } + } + return Err("Server startup timeout - check Console.app for detailed logs".to_string()); } match tokio::time::timeout(tokio::time::Duration::from_millis(100), rx.recv()).await { @@ -77,9 +108,14 @@ async fn start_server( } } tauri_plugin_shell::process::CommandEvent::Stderr(line) => { - let line_str = String::from_utf8_lossy(&line); + let line_str = String::from_utf8_lossy(&line).to_string(); eprintln!("Server: {}", line_str); + // Collect error lines for debugging + if line_str.contains("ERROR") || line_str.contains("Error") || line_str.contains("Failed") { + error_output.push(line_str.clone()); + } + // Uvicorn logs to stderr, so check there too if line_str.contains("Uvicorn running") || line_str.contains("Application startup complete") { println!("Server is ready!"); @@ -90,6 +126,9 @@ async fn start_server( } } Ok(None) => { + eprintln!("Server process ended unexpectedly during startup!"); + eprintln!("The server binary may have crashed or exited with an error."); + eprintln!("Check Console.app logs for more details (search for 'voicebox')"); return Err("Server process ended unexpectedly".to_string()); } Err(_) => { diff --git a/tauri/src-tauri/tauri.conf.json b/tauri/src-tauri/tauri.conf.json index 96cd02a2..a02c5f5f 100644 --- a/tauri/src-tauri/tauri.conf.json +++ b/tauri/src-tauri/tauri.conf.json @@ -24,7 +24,8 @@ "macOS": { "frameworks": [], "minimumSystemVersion": "11.0", - "infoPlist": "Info.plist" + "infoPlist": "Info.plist", + "entitlements": "Entitlements.plist" }, "resources": { "gen/Assets.car": "./",