package routewatch import ( "context" "os" "os/signal" "runtime" "strings" "syscall" "time" "git.eeqj.de/sneak/routewatch/internal/logger" "go.uber.org/fx" ) const ( // shutdownTimeout is the maximum time allowed for graceful shutdown shutdownTimeout = 60 * time.Second // debugInterval is how often to log debug stats debugInterval = 60 * time.Second // bytesPerMB is bytes per megabyte bytesPerMB = 1024 * 1024 ) // logDebugStats logs goroutine count and memory usage func logDebugStats(logger *logger.Logger) { // Only run if DEBUG env var contains "routewatch" debugEnv := os.Getenv("DEBUG") if !strings.Contains(debugEnv, "routewatch") { return } ticker := time.NewTicker(debugInterval) defer ticker.Stop() for range ticker.C { var m runtime.MemStats runtime.ReadMemStats(&m) logger.Debug("System stats", "goroutines", runtime.NumGoroutine(), "alloc_mb", m.Alloc/bytesPerMB, "total_alloc_mb", m.TotalAlloc/bytesPerMB, "sys_mb", m.Sys/bytesPerMB, "num_gc", m.NumGC, "heap_alloc_mb", m.HeapAlloc/bytesPerMB, "heap_sys_mb", m.HeapSys/bytesPerMB, "heap_idle_mb", m.HeapIdle/bytesPerMB, "heap_inuse_mb", m.HeapInuse/bytesPerMB, "heap_released_mb", m.HeapReleased/bytesPerMB, "stack_inuse_mb", m.StackInuse/bytesPerMB, ) } } // CLIEntry is the main entry point for the CLI func CLIEntry() { app := fx.New( getModule(), fx.StopTimeout(shutdownTimeout), // Allow 60 seconds for graceful shutdown fx.Invoke(func(lc fx.Lifecycle, rw *RouteWatch, logger *logger.Logger, shutdowner fx.Shutdowner) { lc.Append(fx.Hook{ OnStart: func(ctx context.Context) error { // Start debug stats logging go logDebugStats(logger) // Handle shutdown signals sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) go func() { <-sigCh logger.Info("Received shutdown signal") if err := shutdowner.Shutdown(); err != nil { logger.Error("Failed to shutdown gracefully", "error", err) } }() go func() { if err := rw.Run(ctx); err != nil { logger.Error("RouteWatch error", "error", err) } }() return nil }, OnStop: func(_ context.Context) error { logger.Info("Shutting down RouteWatch") rw.Shutdown() return nil }, }) }), ) app.Run() }