2017-08-11 17:45:42 +02:00
|
|
|
package config
|
2017-08-10 20:11:35 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2017-08-11 19:59:19 +02:00
|
|
|
"fmt"
|
2017-08-10 20:11:35 +02:00
|
|
|
"os"
|
|
|
|
"regexp"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/genofire/logmania/log"
|
|
|
|
)
|
|
|
|
|
2017-08-13 12:12:46 +02:00
|
|
|
const AlertMsg = "alert service from logmania, device did not send new message for a while"
|
|
|
|
|
2017-08-10 20:11:35 +02:00
|
|
|
type NotifyState struct {
|
2017-08-13 09:51:13 +02:00
|
|
|
Hostname map[string]string `json:"hostname"`
|
|
|
|
HostTo map[string]map[string]bool `json:"host_to"`
|
|
|
|
MaxPrioIn map[string]log.LogLevel `json:"maxLevel"`
|
|
|
|
RegexIn map[string]map[string]*regexp.Regexp `json:"regexIn"`
|
|
|
|
Lastseen map[string]time.Time `json:"lastseen,omitempty"`
|
2017-08-13 12:12:46 +02:00
|
|
|
LastseenNotify map[string]time.Time `json:"-"`
|
2017-08-10 20:11:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (state *NotifyState) SendTo(e *log.Entry) []string {
|
|
|
|
if to, ok := state.HostTo[e.Hostname]; ok {
|
2017-08-13 12:12:46 +02:00
|
|
|
if e.Text != AlertMsg && e.Hostname != "" {
|
|
|
|
state.Lastseen[e.Hostname] = time.Now()
|
|
|
|
}
|
2017-08-10 20:11:35 +02:00
|
|
|
var toList []string
|
2017-08-11 19:59:19 +02:00
|
|
|
for toEntry, _ := range to {
|
2017-08-11 17:45:42 +02:00
|
|
|
if lvl := state.MaxPrioIn[toEntry]; e.Level < lvl {
|
2017-08-10 20:11:35 +02:00
|
|
|
continue
|
|
|
|
}
|
2017-08-13 09:51:13 +02:00
|
|
|
if regex, ok := state.RegexIn[toEntry]; ok {
|
|
|
|
stopForTo := false
|
|
|
|
for _, expr := range regex {
|
|
|
|
if expr.MatchString(e.Text) {
|
|
|
|
stopForTo = true
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if stopForTo {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
2017-08-10 20:11:35 +02:00
|
|
|
toList = append(toList, toEntry)
|
|
|
|
}
|
2017-08-11 19:59:19 +02:00
|
|
|
if hostname, ok := state.Hostname[e.Hostname]; ok {
|
|
|
|
e.Hostname = hostname
|
|
|
|
}
|
2017-08-10 20:11:35 +02:00
|
|
|
return toList
|
2017-08-11 19:59:19 +02:00
|
|
|
} else {
|
|
|
|
state.HostTo[e.Hostname] = make(map[string]bool)
|
2017-08-10 20:11:35 +02:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-08-13 09:51:13 +02:00
|
|
|
func (state *NotifyState) AddRegex(to, expression string) error {
|
|
|
|
regex, err := regexp.Compile(expression)
|
|
|
|
if err == nil {
|
|
|
|
if _, ok := state.RegexIn[to]; !ok {
|
|
|
|
state.RegexIn[to] = make(map[string]*regexp.Regexp)
|
|
|
|
}
|
|
|
|
state.RegexIn[to][expression] = regex
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-08-10 20:11:35 +02:00
|
|
|
func ReadStateFile(path string) *NotifyState {
|
2017-08-11 19:59:19 +02:00
|
|
|
var state NotifyState
|
2017-08-10 20:11:35 +02:00
|
|
|
if f, err := os.Open(path); err == nil { // transform data to legacy meshviewer
|
2017-08-11 19:59:19 +02:00
|
|
|
if err = json.NewDecoder(f).Decode(&state); err == nil {
|
|
|
|
fmt.Println("loaded", len(state.HostTo), "hosts")
|
2017-08-13 09:51:13 +02:00
|
|
|
if state.Lastseen == nil {
|
|
|
|
state.Lastseen = make(map[string]time.Time)
|
|
|
|
}
|
|
|
|
if state.LastseenNotify == nil {
|
|
|
|
state.LastseenNotify = make(map[string]time.Time)
|
|
|
|
}
|
|
|
|
if state.RegexIn == nil {
|
|
|
|
state.RegexIn = make(map[string]map[string]*regexp.Regexp)
|
|
|
|
} else {
|
|
|
|
for to, regexs := range state.RegexIn {
|
|
|
|
for exp, _ := range regexs {
|
|
|
|
state.AddRegex(to, exp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-08-11 19:59:19 +02:00
|
|
|
return &state
|
2017-08-10 20:11:35 +02:00
|
|
|
} else {
|
2017-08-11 19:59:19 +02:00
|
|
|
fmt.Println("failed to unmarshal nodes:", err)
|
2017-08-10 20:11:35 +02:00
|
|
|
}
|
|
|
|
} else {
|
2017-08-11 19:59:19 +02:00
|
|
|
fmt.Println("failed to open state notify file: ", path, ":", err)
|
2017-08-10 20:11:35 +02:00
|
|
|
}
|
|
|
|
return &NotifyState{
|
2017-08-13 09:51:13 +02:00
|
|
|
Hostname: make(map[string]string),
|
|
|
|
HostTo: make(map[string]map[string]bool),
|
|
|
|
MaxPrioIn: make(map[string]log.LogLevel),
|
|
|
|
RegexIn: make(map[string]map[string]*regexp.Regexp),
|
|
|
|
Lastseen: make(map[string]time.Time),
|
|
|
|
LastseenNotify: make(map[string]time.Time),
|
2017-08-10 20:11:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (state *NotifyState) Saver(path string) {
|
|
|
|
c := time.Tick(time.Minute)
|
|
|
|
|
|
|
|
for range c {
|
|
|
|
state.SaveJSON(path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-13 12:12:46 +02:00
|
|
|
func (state *NotifyState) Alert(expired time.Duration, send func(e *log.Entry)) {
|
|
|
|
c := time.Tick(time.Minute)
|
|
|
|
|
|
|
|
for range c {
|
|
|
|
now := time.Now()
|
|
|
|
for host, time := range state.Lastseen {
|
|
|
|
if time.Before(now.Add(expired * -2)) {
|
|
|
|
if timeNotify, ok := state.LastseenNotify[host]; !ok || !time.Before(timeNotify) {
|
|
|
|
state.LastseenNotify[host] = now
|
|
|
|
send(&log.Entry{
|
|
|
|
Hostname: host,
|
|
|
|
Level: log.ErrorLevel,
|
|
|
|
Text: AlertMsg,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-10 20:11:35 +02:00
|
|
|
// SaveJSON to path
|
|
|
|
func (state *NotifyState) SaveJSON(outputFile string) {
|
|
|
|
tmpFile := outputFile + ".tmp"
|
|
|
|
|
|
|
|
f, err := os.OpenFile(tmpFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.NewEncoder(f).Encode(state)
|
|
|
|
if err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
f.Close()
|
|
|
|
if err := os.Rename(tmpFile, outputFile); err != nil {
|
|
|
|
log.Panic(err)
|
|
|
|
}
|
|
|
|
}
|