add alert to logmania
This commit is contained in:
parent
b4487cdde4
commit
d7ed44e809
|
@ -59,6 +59,8 @@ func main() {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
go notifyState.Alert(config.Notify.AlertCheck.Duration, log.Save)
|
||||||
|
|
||||||
log.Info("starting logmania")
|
log.Info("starting logmania")
|
||||||
|
|
||||||
receiver = allReceiver.Init(&config.Receive, logChannel)
|
receiver = allReceiver.Init(&config.Receive, logChannel)
|
||||||
|
|
|
@ -17,6 +17,7 @@ type Config struct {
|
||||||
|
|
||||||
type NotifyConfig struct {
|
type NotifyConfig struct {
|
||||||
StateFile string `toml:"state_file"`
|
StateFile string `toml:"state_file"`
|
||||||
|
AlertCheck Duration `toml:"alert_check"`
|
||||||
XMPP struct {
|
XMPP struct {
|
||||||
Host string `toml:"host"`
|
Host string `toml:"host"`
|
||||||
Username string `toml:"username"`
|
Username string `toml:"username"`
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Duration is a TOML datatype
|
||||||
|
// A duration string is a possibly signed sequence of
|
||||||
|
// decimal numbers and a unit suffix,
|
||||||
|
// such as "300s", "1.5h" or "5d".
|
||||||
|
// Valid time units are "s", "m", "h", "d", "w".
|
||||||
|
type Duration struct {
|
||||||
|
time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalTOML parses a duration string.
|
||||||
|
func (d *Duration) UnmarshalTOML(dataInterface interface{}) error {
|
||||||
|
var data string
|
||||||
|
switch dataInterface.(type) {
|
||||||
|
case string:
|
||||||
|
data = dataInterface.(string)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid duration: \"%s\"", dataInterface)
|
||||||
|
}
|
||||||
|
// " + int + unit + "
|
||||||
|
if len(data) < 2 {
|
||||||
|
return fmt.Errorf("invalid duration: \"%s\"", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
unit := data[len(data)-1]
|
||||||
|
value, err := strconv.Atoi(string(data[:len(data)-1]))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to parse duration %s: %s", data, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch unit {
|
||||||
|
case 's':
|
||||||
|
d.Duration = time.Duration(value) * time.Second
|
||||||
|
case 'm':
|
||||||
|
d.Duration = time.Duration(value) * time.Minute
|
||||||
|
case 'h':
|
||||||
|
d.Duration = time.Duration(value) * time.Hour
|
||||||
|
case 'd':
|
||||||
|
d.Duration = time.Duration(value) * time.Hour * 24
|
||||||
|
case 'w':
|
||||||
|
d.Duration = time.Duration(value) * time.Hour * 24 * 7
|
||||||
|
case 'y':
|
||||||
|
d.Duration = time.Duration(value) * time.Hour * 24 * 365
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid duration unit: %s", string(unit))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package lib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDuration(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
var tests = []struct {
|
||||||
|
input string
|
||||||
|
err string
|
||||||
|
duration time.Duration
|
||||||
|
}{
|
||||||
|
{"", "invalid duration: \"\"", 0},
|
||||||
|
{"1x", "invalid duration unit: x", 0},
|
||||||
|
{"1s", "", time.Second},
|
||||||
|
{"73s", "", time.Second * 73},
|
||||||
|
{"1m", "", time.Minute},
|
||||||
|
{"73m", "", time.Minute * 73},
|
||||||
|
{"1h", "", time.Hour},
|
||||||
|
{"43h", "", time.Hour * 43},
|
||||||
|
{"1d", "", time.Hour * 24},
|
||||||
|
{"8d", "", time.Hour * 24 * 8},
|
||||||
|
{"1w", "", time.Hour * 24 * 7},
|
||||||
|
{"52w", "", time.Hour * 24 * 7 * 52},
|
||||||
|
{"1y", "", time.Hour * 24 * 365},
|
||||||
|
{"3y", "", time.Hour * 24 * 365 * 3},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
|
||||||
|
d := Duration{}
|
||||||
|
err := d.UnmarshalTOML(test.input)
|
||||||
|
duration := d.Duration
|
||||||
|
|
||||||
|
if test.err == "" {
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(test.duration, duration)
|
||||||
|
} else {
|
||||||
|
assert.EqualError(err, test.err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,18 +10,22 @@ import (
|
||||||
"github.com/genofire/logmania/log"
|
"github.com/genofire/logmania/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const AlertMsg = "alert service from logmania, device did not send new message for a while"
|
||||||
|
|
||||||
type NotifyState struct {
|
type NotifyState struct {
|
||||||
Hostname map[string]string `json:"hostname"`
|
Hostname map[string]string `json:"hostname"`
|
||||||
HostTo map[string]map[string]bool `json:"host_to"`
|
HostTo map[string]map[string]bool `json:"host_to"`
|
||||||
MaxPrioIn map[string]log.LogLevel `json:"maxLevel"`
|
MaxPrioIn map[string]log.LogLevel `json:"maxLevel"`
|
||||||
RegexIn map[string]map[string]*regexp.Regexp `json:"regexIn"`
|
RegexIn map[string]map[string]*regexp.Regexp `json:"regexIn"`
|
||||||
Lastseen map[string]time.Time `json:"lastseen,omitempty"`
|
Lastseen map[string]time.Time `json:"lastseen,omitempty"`
|
||||||
LastseenNotify map[string]time.Time `json:"lastseen_notify,omitempty"`
|
LastseenNotify map[string]time.Time `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (state *NotifyState) SendTo(e *log.Entry) []string {
|
func (state *NotifyState) SendTo(e *log.Entry) []string {
|
||||||
if to, ok := state.HostTo[e.Hostname]; ok {
|
if to, ok := state.HostTo[e.Hostname]; ok {
|
||||||
|
if e.Text != AlertMsg && e.Hostname != "" {
|
||||||
state.Lastseen[e.Hostname] = time.Now()
|
state.Lastseen[e.Hostname] = time.Now()
|
||||||
|
}
|
||||||
var toList []string
|
var toList []string
|
||||||
for toEntry, _ := range to {
|
for toEntry, _ := range to {
|
||||||
if lvl := state.MaxPrioIn[toEntry]; e.Level < lvl {
|
if lvl := state.MaxPrioIn[toEntry]; e.Level < lvl {
|
||||||
|
@ -108,6 +112,26 @@ func (state *NotifyState) Saver(path string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SaveJSON to path
|
// SaveJSON to path
|
||||||
func (state *NotifyState) SaveJSON(outputFile string) {
|
func (state *NotifyState) SaveJSON(outputFile string) {
|
||||||
tmpFile := outputFile + ".tmp"
|
tmpFile := outputFile + ".tmp"
|
||||||
|
|
Loading…
Reference in New Issue