Spaces:
Runtime error
Runtime error
File size: 4,699 Bytes
2196bfe |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
package services
import (
"crypto/tls"
"log"
"net/http"
"strings"
"time"
"uptime/backend/database"
"uptime/backend/models"
)
var DeletedMonitorIDs = make(chan uint, 100)
func StartMonitoring() {
log.Printf("[INFO] Monitoring service started at %v", time.Now())
ticker := time.NewTicker(10 * time.Second) // Check for new monitors every 10 seconds
monitorTimers := make(map[uint]*time.Timer)
for {
select {
case <-ticker.C:
var monitors []models.Monitor
database.DB.Find(&monitors)
for i := range monitors {
monitor := &monitors[i]
// Skip if timer already exists for this monitor
if _, exists := monitorTimers[monitor.ID]; exists {
continue
}
// Create individual timer for each monitor based on its interval
interval := time.Duration(monitor.Interval) * time.Second
log.Printf("[INFO] Creating timer for monitor ID=%d Name=%s Interval=%v", monitor.ID, monitor.Name, interval)
monitorTimers[monitor.ID] = time.AfterFunc(0, func() {
scheduleMonitorCheck(monitor, monitorTimers, interval)
})
}
case id := <-DeletedMonitorIDs:
if timer, exists := monitorTimers[id]; exists {
timer.Stop()
delete(monitorTimers, id)
log.Printf("[INFO] Stopped and removed timer for monitor ID=%d", id)
}
}
}
}
func scheduleMonitorCheck(monitor *models.Monitor, timers map[uint]*time.Timer, interval time.Duration) {
// Add a delay before the check if specified
if monitor.Delay > 0 {
delay := time.Duration(monitor.Delay) * time.Second
log.Printf("[INFO] Monitor ID=%d Name=%s: Delaying check by %v", monitor.ID, monitor.Name, delay)
time.Sleep(delay)
}
checkMonitorWithRetries(monitor)
// Schedule next check
timers[monitor.ID] = time.AfterFunc(interval, func() {
scheduleMonitorCheck(monitor, timers, interval)
})
}
func checkMonitorWithRetries(monitor *models.Monitor) {
maxRetries := int(monitor.Retries)
retryInterval := time.Duration(monitor.RetryInterval) * time.Second
for attempt := 0; attempt <= maxRetries; attempt++ {
success := checkMonitor(monitor)
if success || attempt == maxRetries {
break
}
if attempt < maxRetries {
log.Printf("[WARN] Monitor ID=%d Name=%s failed (attempt %d/%d), retrying in %v",
monitor.ID, monitor.Name, attempt+1, maxRetries+1, retryInterval)
time.Sleep(retryInterval)
}
}
}
func checkMonitor(monitor *models.Monitor) bool {
client := &http.Client{
Timeout: time.Duration(monitor.Timeout) * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: monitor.IgnoreTLS},
},
}
req, err := http.NewRequest(monitor.HttpMethod, monitor.URL, strings.NewReader(monitor.HttpBody))
if err != nil {
log.Printf("[ERROR] Monitor ID=%d Name=%s: Error creating request: %s", monitor.ID, monitor.Name, err.Error())
updateMonitorStatus(monitor, "down")
return false
}
// Add headers
// For simplicity, assuming headers are in "Key:Value\nKey2:Value2" format
if monitor.HttpHeaders != "" {
headers := strings.Split(monitor.HttpHeaders, "\n")
for _, header := range headers {
parts := strings.SplitN(header, ":", 2)
if len(parts) == 2 {
req.Header.Set(strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]))
}
}
}
// Add basic auth
if monitor.AuthMethod == "basic" && monitor.AuthUsername != "" {
req.SetBasicAuth(monitor.AuthUsername, monitor.AuthPassword)
}
start := time.Now()
resp, err := client.Do(req)
latency := time.Since(start).Milliseconds()
if err != nil {
log.Printf("[ERROR] Monitor ID=%d Name=%s: HTTP request failed: %s", monitor.ID, monitor.Name, err.Error())
updateMonitorStatus(monitor, "down")
return false
}
defer resp.Body.Close()
// Store latency
latencyRecord := models.Latency{MonitorID: monitor.ID, Latency: uint(latency)}
database.DB.Create(&latencyRecord)
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
log.Printf("[INFO] Monitor ID=%d Name=%s: Status UP (HTTP %d), Latency: %dms", monitor.ID, monitor.Name, resp.StatusCode, latency)
updateMonitorStatus(monitor, "up")
return true
} else {
log.Printf("[WARN] Monitor ID=%d Name=%s: Status DOWN (HTTP %d), Latency: %dms", monitor.ID, monitor.Name, resp.StatusCode, latency)
updateMonitorStatus(monitor, "down")
return false
}
}
func updateMonitorStatus(monitor *models.Monitor, status string) {
oldStatus := monitor.Status
monitor.Status = status
database.DB.Save(monitor)
log.Printf("[INFO] Monitor ID=%d Name=%s: Status changed from %s to %s", monitor.ID, monitor.Name, oldStatus, status)
} |