// cronPrintCurrentStatus logs the regular status check banner func cronPrintCurrentStatus() { // Grab server status stat := common.GetServerStatus() if stat == (common.ServerStatus{}) { log.Println("Could not print current status") return } // Regular status banner log.Printf("status - [goroutines: %d] [memory: %02.3f MB]", stat.NumGoroutine, stat.MemoryMB) // HTTP stats if common.Static.Config.HTTP { log.Printf(" http - [current: %d] [total: %d]", stat.HTTP.Current, stat.HTTP.Total) // Reset current HTTP counter atomic.StoreInt64(&common.Static.HTTP.Current, 0) } // UDP stats if common.Static.Config.UDP { log.Printf(" udp - [current: %d] [total: %d]", stat.UDP.Current, stat.UDP.Total) // Reset current UDP counter atomic.StoreInt64(&common.Static.UDP.Current, 0) } }
// TestGetStatusJSON verifies that /api/status returns proper JSON output func TestGetStatusJSON(t *testing.T) { log.Println("TestGetStatusJSON()") // Get status directly status := common.GetServerStatus() // Request output JSON from API for this status var status2 common.ServerStatus err := json.Unmarshal(getStatusJSON(), &status2) if err != nil { t.Fatalf("Failed to unmarshal result JSON") } // Verify objects are the same if status.PID != status2.PID { t.Fatalf("PID, expected %d, got %d", status.PID, status2.PID) } if status.Hostname != status2.Hostname { t.Fatalf("Hostname, expected %s, got %s", status.Hostname, status2.Hostname) } if status.Platform != status2.Platform { t.Fatalf("Platform, expected %s, got %s", status.Platform, status2.Platform) } if status.Architecture != status2.Architecture { t.Fatalf("Architecture, expected %s, got %s", status.Architecture, status2.Architecture) } if status.NumCPU != status2.NumCPU { t.Fatalf("NumCPU, expected %d, got %d", status.NumCPU, status2.NumCPU) } }
// cronPrintCurrentStatus logs the regular status check banner func cronPrintCurrentStatus() { // Grab server status stat, err := common.GetServerStatus() if err != nil { log.Println(err.Error()) return } // Regular status banner log.Printf("status - [goroutines: %d] [memory: %02.3f MB]", stat.NumGoroutine, stat.MemoryMB) // API stats if common.Static.Config.API { log.Printf(" api - [1 min: %03d | 30 min: %03d | 60 min: %03d] [total: %03d]", stat.API.Minute, stat.API.HalfHour, stat.API.Hour, stat.API.Total) } // HTTP stats if common.Static.Config.HTTP { log.Printf(" http - [1 min: %03d | 30 min: %03d | 60 min: %03d] [total: %03d]", stat.HTTP.Minute, stat.HTTP.HalfHour, stat.HTTP.Hour, stat.HTTP.Total) } // UDP stats if common.Static.Config.UDP { log.Printf(" udp - [1 min: %03d | 30 min: %03d | 60 min: %03d] [total: %03d]", stat.UDP.Minute, stat.UDP.HalfHour, stat.UDP.Hour, stat.UDP.Total) } }
// getStatusJSON returns a JSON representation of server status func getStatusJSON() []byte { // Marshal into JSON from request res, err := json.Marshal(common.GetServerStatus()) if err != nil { log.Println(err.Error()) return nil } // Return status return res }
// getStatusJSON returns a JSON representation of server status func getStatusJSON() ([]byte, error) { // Retrieve status status, err := common.GetServerStatus() if err != nil { return nil, err } // Marshal into JSON from request res, err := json.Marshal(status) if err != nil { return nil, err } // Return status return res, nil }
// Manager is responsible for coordinating the application func Manager(killChan chan bool, exitChan chan int) { // Capture startup time common.Static.StartTime = time.Now().Unix() // Set up logging flags log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) log.Println("Starting " + App + " " + Version) // Grab initial server status stat := common.GetServerStatus() if stat == (common.ServerStatus{}) { log.Println("Could not print get startup status") } else { log.Printf("%s - %s_%s (%d CPU) [pid: %d]", stat.Hostname, stat.Platform, stat.Architecture, stat.NumCPU, stat.PID) } // Load configuration config := common.LoadConfig() if config == (common.Conf{}) { panic("Cannot load configuration, panicking") } common.Static.Config = config // Check for sane announce interval (10 minutes or more) if common.Static.Config.Interval <= 600 { panic("Announce interval must be at least 600 seconds, panicking") } // Attempt database connection if !data.DBPing() { panic(fmt.Errorf("cannot connect to database %s; panicking", data.DBName())) } log.Println("Database", data.DBName(), ": OK") // If configured, attempt redis connection if common.Static.Config.Redis.Enabled { if !data.RedisPing() { panic("Cannot connect to Redis, panicking") } log.Println("Redis : OK") } // Start cron manager go cronManager() // Set up graceful shutdown channels httpSendChan := make(chan bool) httpRecvChan := make(chan bool) httpsSendChan := make(chan bool) httpsRecvChan := make(chan bool) udpSendChan := make(chan bool) udpRecvChan := make(chan bool) // Set up HTTP(S) route http.HandleFunc("/", parseHTTP) // Launch listeners as configured if common.Static.Config.HTTP { go listenHTTP(httpSendChan, httpRecvChan) log.Println("HTTP listener launched on port " + strconv.Itoa(common.Static.Config.Port)) } if common.Static.Config.SSL.Enabled { go listenHTTPS(httpsSendChan, httpsRecvChan) log.Println("HTTPS listener launched on port " + strconv.Itoa(common.Static.Config.SSL.Port)) } if common.Static.Config.UDP { go listenUDP(udpSendChan, udpRecvChan) log.Println("UDP listener launched on port " + strconv.Itoa(common.Static.Config.Port)) } // Wait for shutdown signal for { select { case <-killChan: // Trigger a graceful shutdown log.Println("Triggering graceful shutdown, press Ctrl+C again to force halt") // If program hangs for more than 10 seconds, trigger a force halt go func() { <-time.After(10 * time.Second) log.Println("Timeout reached, triggering force halt") if err := syscall.Kill(os.Getpid(), syscall.SIGTERM); err != nil { log.Println(err.Error()) } }() // Stop listeners if common.Static.Config.HTTP { log.Println("Stopping HTTP listener") httpSendChan <- true <-httpRecvChan } if common.Static.Config.SSL.Enabled { log.Println("Stopping HTTPS listener") httpsSendChan <- true <-httpsRecvChan } if common.Static.Config.UDP { log.Println("Stopping UDP listener") udpSendChan <- true <-udpRecvChan } log.Println("Closing database connection") data.DBCloseFunc() // Report that program should exit gracefully exitChan <- 0 } } }