diff options
| author | Paul Buetow <paul@buetow.org> | 2023-04-17 23:12:04 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2023-04-17 23:12:04 +0300 |
| commit | 204c0c88104ce130bdeabfd73c735b7ef33e2f57 (patch) | |
| tree | 3ff643babafedee47d40fbe6645fe2ab93d32799 | |
| parent | 6b5ed019b50fdec4ab91922708390cb12c9a814e (diff) | |
only send one report
| -rw-r--r-- | README.md | 10 | ||||
| -rw-r--r-- | check.go | 3 | ||||
| -rw-r--r-- | main.go | 13 | ||||
| -rw-r--r-- | notify.go | 2 | ||||
| -rw-r--r-- | state.go | 94 |
5 files changed, 95 insertions, 27 deletions
@@ -1,3 +1,11 @@ # gogios -Minimalist monitoring system, compatible with the Nagios Check API.
\ No newline at end of file +Minimalist monitoring system, compatible with the Nagios Check API. + +TODO: Only send one email per state (OK, WARNING, CRITICAL, + +TODO: Only send one email per state (OK, WARNING, CRITICAL, + +TODO: Only send one email per state (OK, WARNING, CRITICAL, + +TODO: Only send one email per state (OK, WARNING, CRITICAL, @@ -4,6 +4,7 @@ import ( "bytes" "context" "os/exec" + "strings" ) type check struct { @@ -24,5 +25,5 @@ func (c check) execute(ctx context.Context) (string, int) { } } - return bytes.String(), cmd.ProcessState.ExitCode() + return strings.TrimSuffix(bytes.String(), "\n"), cmd.ProcessState.ExitCode() } @@ -3,7 +3,6 @@ package main import ( "context" "flag" - "fmt" "log" "sync" "time" @@ -54,13 +53,7 @@ func main() { defer cancel() output, status := check.execute(ctx) - stateChanged := state.update(name, status) - - if status != ok || stateChanged { - subject := fmt.Sprintf("GOGIOS %s: %s", codeToString(status), name) - notify(config, subject, output) - } - + state.update(name, output, status) }(entry.name, entry.check) } @@ -70,4 +63,8 @@ func main() { if err := state.persist(); err != nil { notifyError(config, err) } + + if subject, body, changed := state.report(); changed { + notify(config, subject, body) + } } @@ -7,7 +7,7 @@ import ( ) func notify(config config, subject, body string) error { - log.Println("emailNotify", subject, body) + log.Println("notify", subject, body) headers := make(map[string]string) headers["From"] = config.EmailFrom @@ -6,19 +6,27 @@ import ( "io/ioutil" "log" "os" + "strings" "sync" ) +type checkState struct { + status int + prevStatus int + output string +} + type state struct { stateFile string - Checks map[string]int - mutex sync.Mutex + checks map[string]checkState + mutex *sync.Mutex } func newState(config config) (state, error) { s := state{ stateFile: fmt.Sprintf("%s/state.json", config.StateDir), - Checks: make(map[string]int), + checks: make(map[string]checkState), + mutex: &sync.Mutex{}, } if _, err := os.Stat(s.stateFile); err != nil { @@ -39,51 +47,105 @@ func newState(config config) (state, error) { } // Parse the JSON content - if err := json.Unmarshal(bytes, &s.Checks); err != nil { + if err := json.Unmarshal(bytes, &s.checks); err != nil { return s, err } // Clean up obsolete state information var obsolete []string - for name := range s.Checks { + for name := range s.checks { if _, ok := config.Checks[name]; !ok { obsolete = append(obsolete, name) } } for _, name := range obsolete { - delete(s.Checks, name) + delete(s.checks, name) log.Printf("State of %s is obsolete (removed)", name) } return s, nil } -func (s state) update(name string, status int) bool { +func (s state) update(name, output string, status int) { s.mutex.Lock() defer s.mutex.Unlock() - oldStatus, ok := s.Checks[name] + prevState, ok := s.checks[name] if !ok { log.Printf("State of %s: %d (new)", name, status) - s.Checks[name] = status - return true + s.checks[name] = checkState{status, unknown, output} + return } - if oldStatus != status { - log.Printf("State of %s: %d -> %d (changed)", name, oldStatus, status) - s.Checks[name] = status - return true + if prevState.status != status { + log.Printf("State of %s: %d -> %d (changed)", name, prevState, status) + s.checks[name] = checkState{status, prevState.status, output} + return } log.Printf("State of %s: %d (unchanged)", name, status) - return false + s.checks[name] = checkState{status, prevState.status, output} } func (s state) persist() error { - jsonData, err := json.Marshal(s.Checks) + jsonData, err := json.Marshal(s.checks) if err != nil { return err } return ioutil.WriteFile(s.stateFile, jsonData, os.ModePerm) } + +func (s state) report() (string, string, bool) { + var sb strings.Builder + var changed bool + + f := func(filter func(i int) bool) int { + var count int + for name, checkState := range s.checks { + if filter(checkState.status) { + count++ + if checkState.status != checkState.prevStatus { + sb.WriteString(codeToString(checkState.prevStatus)) + sb.WriteString("->") + changed = true + } + sb.WriteString(codeToString(checkState.status)) + sb.WriteString(": ") + sb.WriteString(name) + sb.WriteString(" ==>> ") + sb.WriteString(checkState.output) + sb.WriteString("\n") + } + } + return count + } + + sb.WriteString("This is the recent Googios report!\n\n") + + numCriticals := f(func(i int) bool { return i == 2 }) + if numCriticals > 0 { + sb.WriteString("\n") + } + + numWarnings := f(func(i int) bool { return i == 1 }) + if numWarnings > 0 { + sb.WriteString("\n") + } + + numUnknowns := f(func(i int) bool { return i > 2 }) + if numUnknowns > 0 { + sb.WriteString("\n") + } + + numOks := f(func(i int) bool { return i == 0 }) + if numOks > 0 { + sb.WriteString("\n") + } + + sb.WriteString("Have a nice day!\n") + subject := fmt.Sprintf("GOGIOS Report [C:%d W:%d U:%d OK:%d]", + numCriticals, numWarnings, numUnknowns, numOks) + + return subject, sb.String(), changed +} |
