mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-10-03 17:15:19 -07:00
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.
This commit is contained in:
+71
-36
@@ -5,45 +5,80 @@ This module provides an entry point that works with PyInstaller by using
|
|||||||
absolute imports instead of relative imports.
|
absolute imports instead of relative imports.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import sys
|
||||||
import uvicorn
|
import logging
|
||||||
|
|
||||||
# Import the FastAPI app from the backend package
|
# Set up logging FIRST, before any imports that might fail
|
||||||
from backend.main import app
|
logging.basicConfig(
|
||||||
from backend import config, database
|
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__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="voicebox backend server")
|
try:
|
||||||
parser.add_argument(
|
parser = argparse.ArgumentParser(description="voicebox backend server")
|
||||||
"--host",
|
parser.add_argument(
|
||||||
type=str,
|
"--host",
|
||||||
default="127.0.0.1",
|
type=str,
|
||||||
help="Host to bind to (use 0.0.0.0 for remote access)",
|
default="127.0.0.1",
|
||||||
)
|
help="Host to bind to (use 0.0.0.0 for remote access)",
|
||||||
parser.add_argument(
|
)
|
||||||
"--port",
|
parser.add_argument(
|
||||||
type=int,
|
"--port",
|
||||||
default=8000,
|
type=int,
|
||||||
help="Port to bind to",
|
default=8000,
|
||||||
)
|
help="Port to bind to",
|
||||||
parser.add_argument(
|
)
|
||||||
"--data-dir",
|
parser.add_argument(
|
||||||
type=str,
|
"--data-dir",
|
||||||
default=None,
|
type=str,
|
||||||
help="Data directory for database, profiles, and generated audio",
|
default=None,
|
||||||
)
|
help="Data directory for database, profiles, and generated audio",
|
||||||
args = parser.parse_args()
|
)
|
||||||
|
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
|
# Set data directory if provided
|
||||||
if args.data_dir:
|
if args.data_dir:
|
||||||
config.set_data_dir(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
|
# Initialize database after data directory is set
|
||||||
database.init_db()
|
logger.info("Initializing database...")
|
||||||
|
database.init_db()
|
||||||
|
logger.info("Database initialized successfully")
|
||||||
|
|
||||||
uvicorn.run(
|
logger.info(f"Starting uvicorn server on {args.host}:{args.port}...")
|
||||||
app,
|
uvicorn.run(
|
||||||
host=args.host,
|
app,
|
||||||
port=args.port,
|
host=args.host,
|
||||||
log_level="info",
|
port=args.port,
|
||||||
)
|
log_level="info",
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Server startup failed: {e}", exc_info=True)
|
||||||
|
sys.exit(1)
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.disable-library-validation</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
Binary file not shown.
@@ -31,10 +31,21 @@ async fn start_server(
|
|||||||
std::fs::create_dir_all(&data_dir)
|
std::fs::create_dir_all(&data_dir)
|
||||||
.map_err(|e| format!("Failed to create data dir: {}", e))?;
|
.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
|
let mut sidecar = app
|
||||||
.shell()
|
.shell()
|
||||||
.sidecar("voicebox-server")
|
.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
|
// Pass data directory to Python server
|
||||||
sidecar = sidecar.args([
|
sidecar = sidecar.args([
|
||||||
@@ -48,9 +59,21 @@ async fn start_server(
|
|||||||
sidecar = sidecar.args(["--host", "0.0.0.0"]);
|
sidecar = sidecar.args(["--host", "0.0.0.0"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("Spawning server process...");
|
||||||
let (mut rx, child) = sidecar
|
let (mut rx, child) = sidecar
|
||||||
.spawn()
|
.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
|
// Store child process
|
||||||
*state.child.lock().unwrap() = Some(child);
|
*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
|
// Wait for server to be ready by listening for startup log
|
||||||
let timeout = tokio::time::Duration::from_secs(30);
|
let timeout = tokio::time::Duration::from_secs(30);
|
||||||
let start_time = tokio::time::Instant::now();
|
let start_time = tokio::time::Instant::now();
|
||||||
|
let mut error_output = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if start_time.elapsed() > timeout {
|
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 {
|
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) => {
|
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);
|
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
|
// Uvicorn logs to stderr, so check there too
|
||||||
if line_str.contains("Uvicorn running") || line_str.contains("Application startup complete") {
|
if line_str.contains("Uvicorn running") || line_str.contains("Application startup complete") {
|
||||||
println!("Server is ready!");
|
println!("Server is ready!");
|
||||||
@@ -90,6 +126,9 @@ async fn start_server(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(None) => {
|
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());
|
return Err("Server process ended unexpectedly".to_string());
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
|||||||
@@ -24,7 +24,8 @@
|
|||||||
"macOS": {
|
"macOS": {
|
||||||
"frameworks": [],
|
"frameworks": [],
|
||||||
"minimumSystemVersion": "11.0",
|
"minimumSystemVersion": "11.0",
|
||||||
"infoPlist": "Info.plist"
|
"infoPlist": "Info.plist",
|
||||||
|
"entitlements": "Entitlements.plist"
|
||||||
},
|
},
|
||||||
"resources": {
|
"resources": {
|
||||||
"gen/Assets.car": "./",
|
"gen/Assets.car": "./",
|
||||||
|
|||||||
Reference in New Issue
Block a user