package main import ( "log" "os" "os/exec" "strconv" "sync" "syscall" "time" ) // startLogger starts the logger process and returns a function to stop it. // It automatically restarts the logger if it crashes. func startLogger(port int, capacity int) func() { var mu sync.Mutex var cmd *exec.Cmd var stopping bool portStr := strconv.Itoa(port) capacityStr := strconv.Itoa(capacity) start := func() *exec.Cmd { c := exec.Command("../logger/logger", "--port", portStr, "--capacity", capacityStr) c.Stdout = os.Stdout c.Stderr = os.Stderr if err := c.Start(); err != nil { log.Printf("[logger] Failed to start: %v", err) return nil } log.Printf("[logger] Started (pid %d) on port %s", c.Process.Pid, portStr) return c } // Start initial logger cmd = start() // Monitor and restart on crash go func() { for { mu.Lock() currentCmd := cmd mu.Unlock() if currentCmd == nil { time.Sleep(time.Second) mu.Lock() if !stopping { cmd = start() } mu.Unlock() continue } err := currentCmd.Wait() mu.Lock() if stopping { mu.Unlock() return } if err != nil { log.Printf("[logger] Process exited: %v, restarting...", err) } else { log.Printf("[logger] Process exited normally, restarting...") } time.Sleep(time.Second) cmd = start() mu.Unlock() } }() // Return stop function return func() { mu.Lock() defer mu.Unlock() stopping = true if cmd == nil || cmd.Process == nil { return } log.Printf("[logger] Stopping (pid %d)", cmd.Process.Pid) cmd.Process.Signal(syscall.SIGTERM) // Wait briefly for graceful shutdown done := make(chan struct{}) go func() { cmd.Wait() close(done) }() select { case <-done: log.Printf("[logger] Stopped gracefully") case <-time.After(5 * time.Second): log.Printf("[logger] Force killing") cmd.Process.Kill() } } }