From 204c0c88104ce130bdeabfd73c735b7ef33e2f57 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 17 Apr 2023 23:12:04 +0300 Subject: only send one report --- README.md | 10 ++++++- check.go | 3 +- main.go | 13 ++++----- notify.go | 2 +- state.go | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++----------- 5 files changed, 95 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 166f0d2..4c0ca65 100644 --- a/README.md +++ b/README.md @@ -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, diff --git a/check.go b/check.go index f728e6d..702d46b 100644 --- a/check.go +++ b/check.go @@ -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() } diff --git a/main.go b/main.go index 6d661d7..bd13680 100644 --- a/main.go +++ b/main.go @@ -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) + } } diff --git a/notify.go b/notify.go index 3a295ba..7b423cd 100644 --- a/notify.go +++ b/notify.go @@ -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 diff --git a/state.go b/state.go index d2be1c0..25d0ca3 100644 --- a/state.go +++ b/state.go @@ -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 +} -- cgit v1.2.3