From cae3e18b495fc3deba60074d0d4d4807456cb246 Mon Sep 17 00:00:00 2001 From: goose Date: Fri, 20 Feb 2026 16:57:37 -0300 Subject: [PATCH] debug: Add panic hook and numbered steps to diagnose startup issue --- backend/src/main.rs | 61 ++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/backend/src/main.rs b/backend/src/main.rs index ba88d4f..c3c3b91 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -17,23 +17,35 @@ use tower_http::{ }; use config::Config; +// Set up panic hook to catch panics before main +fn setup_panic_hook() { + std::panic::set_hook(Box::new(|panic_info| { + let msg = panic_info.to_string(); + println!("[PANIC] {}", msg); + let location = panic_info.location().unwrap_or_else(|| std::panic::Location::caller()); + println!("[PANIC] Location: {}:{}:{}", location.file(), location.line(), location.column()); + })); +} + #[tokio::main] async fn main() -> anyhow::Result<()> { - // Use println! which is more reliable in Docker - println!("=== NORMOGEN BACKEND STARTING ==="); - println!("[STARTUP] Loading environment variables..."); + // Install panic hook FIRST + setup_panic_hook(); + + // IMMEDIATE output - this MUST show up + println!("=== NORMOGEN STARTING ==="); + println!("[1/7] Loading environment variables..."); match dotenv::dotenv() { Ok(path) => { - println!("[STARTUP] Loaded .env from: {:?}", path); + println!("[1/7] Loaded .env from: {:?}", path); } - Err(e) => { - println!("[STARTUP] No .env file found (this is OK in Docker): {}", e); + Err(_) => { + println!("[1/7] No .env file found (OK in Docker)"); } } - println!("[STARTUP] Initializing logging..."); - + println!("[2/7] Initializing logging..."); tracing_subscriber::fmt() .with_env_filter( tracing_subscriber::EnvFilter::try_from_default_env() @@ -41,11 +53,10 @@ async fn main() -> anyhow::Result<()> { ) .init(); - println!("[STARTUP] Loading configuration..."); - + println!("[3/7] Loading configuration..."); let config = match Config::from_env() { Ok(cfg) => { - println!("[STARTUP] Config loaded: DB={}, Port={}", cfg.database.database, cfg.server.port); + println!("[3/7] Config loaded: DB={}, Port={}", cfg.database.database, cfg.server.port); cfg } Err(e) => { @@ -54,11 +65,10 @@ async fn main() -> anyhow::Result<()> { } }; - println!("[STARTUP] Connecting to MongoDB at {}...", config.database.uri); - + println!("[4/7] Connecting to MongoDB at {}...", config.database.uri); let db = match db::MongoDb::new(&config.database.uri, &config.database.database).await { Ok(db) => { - println!("[STARTUP] MongoDB connection successful!"); + println!("[4/7] MongoDB connection successful!"); db } Err(e) => { @@ -67,11 +77,10 @@ async fn main() -> anyhow::Result<()> { } }; - println!("[STARTUP] Performing health check..."); - + println!("[5/7] Performing health check..."); match db.health_check().await { - Ok(health) => { - println!("[STARTUP] MongoDB health check: {}", health); + Ok(_) => { + println!("[5/7] MongoDB health check: OK"); } Err(e) => { println!("[FATAL] MongoDB health check failed: {}", e); @@ -79,6 +88,7 @@ async fn main() -> anyhow::Result<()> { } } + println!("[6/7] Building application..."); let jwt_service = auth::JwtService::new(config.jwt.clone()); let app_state = config::AppState { @@ -87,8 +97,6 @@ async fn main() -> anyhow::Result<()> { config: config.clone(), }; - println!("[STARTUP] Building router..."); - let public_routes = Router::new() .route("/health", get(handlers::health_check)) .route("/ready", get(handlers::ready_check)) @@ -102,21 +110,17 @@ async fn main() -> anyhow::Result<()> { ); let protected_routes = Router::new() - // Profile management .route("/api/users/me", get(handlers::get_profile)) .route("/api/users/me", put(handlers::update_profile)) .route("/api/users/me", delete(handlers::delete_account)) - // Account settings .route("/api/users/me/settings", get(handlers::get_settings)) - .route("/api/users/me/settings", put(handlers::update_settings)) + .route("/api/users/me/settings", put(handers::update_settings)) .route("/api/users/me/change-password", post(handlers::change_password)) - // Share management (Phase 2.5) .route("/api/shares", post(handlers::create_share)) .route("/api/shares", get(handlers::list_shares)) .route("/api/shares/:id", get(handlers::get_share)) .route("/api/shares/:id", put(handlers::update_share)) .route("/api/shares/:id", delete(handlers::delete_share)) - // Permissions (Phase 2.5) .route("/api/permissions/check", post(handlers::check_permission)) .layer( ServiceBuilder::new() @@ -130,13 +134,12 @@ async fn main() -> anyhow::Result<()> { let app = public_routes.merge(protected_routes).with_state(app_state); - println!("[STARTUP] Binding to {}:{}...", config.server.host, config.server.port); - + println!("[7/7] Starting server on {}:{}...", config.server.host, config.server.port); let listener = tokio::net::TcpListener::bind(&format!("{}:{}", config.server.host, config.server.port)) .await?; - println!("[STARTUP] Server listening on http://{}:{}", config.server.host, config.server.port); - println!("[STARTUP] Server ready to accept connections!"); + println!("=== SERVER READY ==="); + println!("Listening on http://{}:{}", config.server.host, config.server.port); tracing::info!("Server listening on {}:{}", config.server.host, config.server.port);