diff --git a/bot/command.go b/bot/command.go index 788c26d..119b9a8 100644 --- a/bot/command.go +++ b/bot/command.go @@ -166,14 +166,14 @@ func (b *Bot) listMaxfilter(answer func(string), from string, params []string) { if len(params) > 0 { of = params[0] } - if filter, ok := b.db.NotifiesByAddress[of]; ok { - msg = fmt.Sprintf("%s of %s is %s", msg, of, filter) + if notify, ok := b.db.NotifiesByAddress[of]; ok { + msg = fmt.Sprintf("%s %s is %s", msg, of, notify.MaxPrioIn.String()) } } answer(msg) } -// set a filter to a mix +// set a filter to a max func (b *Bot) setMaxfilter(answer func(string), from string, params []string) { if len(params) < 1 { answer("invalid: CMD Priority\n or\n CMD Channel Priority") diff --git a/cmd/server.go b/cmd/server.go index 29aea96..be134ed 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -66,7 +66,7 @@ var serverCmd = &cobra.Command{ go db.Alert(config.AlertCheck.Duration, out.Send) } - log.Info("starting logmania") + log.WithField("defaults", len(db.DefaultNotify)).Info("starting logmania") if config.HTTPAddress != "" { if config.Webroot != "" { diff --git a/database/host.go b/database/host.go index 6696bdd..71be71b 100644 --- a/database/host.go +++ b/database/host.go @@ -89,6 +89,7 @@ func (db *DB) NewHost(addr string) *Host { h := &Host{ Address: addr, NotifiesByAddress: make(map[string]*Notify), + Lastseen: time.Now(), } db.AddHost(h) return h diff --git a/input/all/internal.go b/input/all/internal.go index f880dc4..552e3ce 100644 --- a/input/all/internal.go +++ b/input/all/internal.go @@ -18,15 +18,15 @@ func Init(configInterface interface{}, exportChannel chan *log.Entry) input.Inpu for inputType, init := range input.Register { configForItem := config[inputType] if configForItem == nil { - log.Warnf("the input type '%s' has no configuration\n", inputType) + log.Warnf("the input type '%s' has no configuration", inputType) continue } - input := init(configForItem, exportChannel) + in := init(configForItem, exportChannel) - if input == nil { + if in == nil { continue } - list = append(list, input) + list = append(list, in) } return &Input{ list: list, diff --git a/input/all/main.go b/input/all/main.go index 293a20f..e72bdb3 100644 --- a/input/all/main.go +++ b/input/all/main.go @@ -4,4 +4,8 @@ import ( _ "dev.sum7.eu/genofire/logmania/input/journald_json" _ "dev.sum7.eu/genofire/logmania/input/logrus" _ "dev.sum7.eu/genofire/logmania/input/syslog" + _ "dev.sum7.eu/genofire/logmania/input/webhook" + _ "dev.sum7.eu/genofire/logmania/input/webhook/circleci" + _ "dev.sum7.eu/genofire/logmania/input/webhook/git" + _ "dev.sum7.eu/genofire/logmania/input/webhook/grafana" ) diff --git a/input/logrus/main.go b/input/logrus/main.go index 13f8e28..1630e83 100644 --- a/input/logrus/main.go +++ b/input/logrus/main.go @@ -27,7 +27,7 @@ func Init(config interface{}, exportChannel chan *log.Entry) input.Input { http.HandleFunc("/input/"+inputType, ws.Handler) - input := &Input{ + in := &Input{ input: inputMsg, serverSocket: ws, exportChannel: exportChannel, @@ -35,7 +35,7 @@ func Init(config interface{}, exportChannel chan *log.Entry) input.Input { logger.Info("init") - return input + return in } func (in *Input) Listen() { diff --git a/input/syslog/main.go b/input/syslog/main.go index 79b1f17..8805a7f 100644 --- a/input/syslog/main.go +++ b/input/syslog/main.go @@ -37,14 +37,14 @@ func Init(configInterface interface{}, exportChannel chan *log.Entry) input.Inpu logger.Error("init ", err) return nil } - input := &Input{ + in := &Input{ serverSocket: ln, exportChannel: exportChannel, } logger.Info("init") - return input + return in } const maxDataGramSize = 8192 diff --git a/input/webhook/circleci/main.go b/input/webhook/circleci/main.go new file mode 100644 index 0000000..1a477d1 --- /dev/null +++ b/input/webhook/circleci/main.go @@ -0,0 +1,55 @@ +package circleci + +import ( + "fmt" + "net/http" + "time" + + "github.com/mitchellh/mapstructure" + log "github.com/sirupsen/logrus" + + "dev.sum7.eu/genofire/logmania/input/webhook" +) + +type requestBody struct { + Payload struct { + VCSURL string `mapstructure:"vcs_url"` + Status string `mapstructure:"status"` + BuildNum float64 `mapstructure:"build_num"` + BuildURL string `mapstructure:"build_url"` + BuildTime float64 `mapstructure:"build_time_millis"` + Subject string `mapstructure:"subject"` + } `mapstructure:"payload"` +} + +const webhookType = "circleci" + +var HookstatusMap = map[string]log.Level{ + "failed": log.ErrorLevel, + "success": log.InfoLevel, +} + +var logger = log.WithField("input", webhook.InputType).WithField("hook", webhookType) + +func handler(_ http.Header, body interface{}) *log.Entry { + var request requestBody + if err := mapstructure.Decode(body, &request); err != nil { + logger.Warnf("not able to decode data: %s", err) + return nil + } + + if request.Payload.VCSURL == "" { + return nil + } + + entry := log.NewEntry(nil) + entry = entry.WithField("hostname", request.Payload.VCSURL) + entry.Time = time.Now() + entry.Level = HookstatusMap[request.Payload.Status] + entry.Message = fmt.Sprintf("#%0.f (%0.fs): %s - %s", request.Payload.BuildNum, request.Payload.BuildTime/1000, request.Payload.Subject, request.Payload.BuildURL) + return entry +} + +func init() { + webhook.AddHandler(webhookType, handler) +} diff --git a/input/webhook/git/main.go b/input/webhook/git/main.go new file mode 100644 index 0000000..497813a --- /dev/null +++ b/input/webhook/git/main.go @@ -0,0 +1,81 @@ +package git + +import ( + "net/http" + "time" + + "github.com/mitchellh/mapstructure" + log "github.com/sirupsen/logrus" + + "dev.sum7.eu/genofire/logmania/input/webhook" +) + +type requestBody struct { + Repository struct { + HTMLURL string `mapstructure:"html_url"` + FullName string `mapstructure:"full_name"` + } `mapstructure:"repository"` + //push + Pusher struct { + Name string `mapstructure:"name"` + } `mapstructure:"pusher"` + Commits []struct { + Added []interface{} `mapstructure:"added"` + Removed []interface{} `mapstructure:"removed"` + Modified []interface{} `mapstructure:"modified"` + } `mapstructure:"commits"` + Compare string `mapstructure:"compare"` + Ref string `mapstructure:"ref"` + // issue + fallback + Sender struct { + Login string `mapstructure:"login"` + } `mapstructure:"sender"` + // issue + Action string `mapstructure:"action"` + Issue struct { + HTMLURL string `mapstructure:"html_url"` + Number float64 `mapstructure:"number"` + Title string `mapstructure:"title"` + } `mapstructure:"issue"` +} + +const webhookType = "git" + +var eventHeader = []string{"X-GitHub-Event", "X-Gogs-Event"} + +var logger = log.WithField("input", webhook.InputType).WithField("hook", webhookType) + +func handler(header http.Header, body interface{}) *log.Entry { + event := "" + for _, head := range eventHeader { + event = header.Get(head) + + if event != "" { + break + } + } + + if event == "status" { + return nil + } + var request requestBody + if err := mapstructure.Decode(body, &request); err != nil { + logger.Warnf("not able to decode data: %s", err) + return nil + } + + if request.Repository.HTMLURL == "" { + return nil + } + + entry := log.NewEntry(nil) + entry = entry.WithField("hostname", request.Repository.HTMLURL) + entry.Time = time.Now() + entry.Level = log.InfoLevel + entry.Message = RequestToString(event, request) + return entry +} + +func init() { + webhook.AddHandler(webhookType, handler) +} diff --git a/input/webhook/git/msg.go b/input/webhook/git/msg.go new file mode 100644 index 0000000..0ad48e7 --- /dev/null +++ b/input/webhook/git/msg.go @@ -0,0 +1,75 @@ +package git + +import ( + "fmt" + "strings" +) + +var eventMsg = map[string]string{ + "commit_comment_created": "Commit comment", + "status_error": "Commit status: error", + "status_failure": "Commit status: failure", + "status_pending": "Commit status: pending", + "status_success": "Commit status: success", + "create_branch": "Create branch", + "create_tag": "Create tag", + "delete_branch": "Delete branch", + "delete_tag": "Delete tag", + "issue_comment_created": "Issue comment", + "issue_comment_deleted": "Issue comment: deleted", + "issue_comment_edited": "Issue comment: edited", + "issue_assigned": "Issue: assigned", + "issue_closed": "Issue: closed", + "issue_edited": "Issue: edited", + "issue_labeled": "Issue: labeled", + "issue_opened": "Issue: opened", + "issue_reopened": "Issue: reopened", + "issue_unassigned": "Issue: unassigned", + "issue_unlabeled": "Issue: unlabeled", + "pr_review_created": "Pull request review comment", + "pr_review_deleted": "Pull request review comment: deleted", + "pr_review_edited": "Pull request review comment: edited", + "pr_assigned": "Pull request: assigned", + "pr_closed": "Pull request: closed", + "pr_edited": "Pull request: edited", + "pr_labeled": "Pull request: labeled", + "pr_opened": "Pull request: opened", + "pr_reopened": "Pull request: reopened", + "pr_synchronize": "Pull request: synchronize", + "pr_unassigned": "Pull request: unassigned", + "pr_unlabeled": "Pull request: unlabeled", + "push": "Push", + "release_published": "Release published", + "member_added": "Repo: added collaborator", + "team_add": "Repo: added to a team", + "fork": "Repo: forked", + "public": "Repo: made public", + "watch_started": "Repo: starred", + "gollum_created": "Wiki: created page", + "gollum_edited": "Wiki: edited page", +} + +func RequestToString(event string, request requestBody) string { + msg := fmt.Sprintf("[%s]", request.Repository.FullName) + + if event == "push" { + added := 0 + removed := 0 + modified := 0 + for _, commit := range request.Commits { + added += len(commit.Added) + removed += len(commit.Removed) + modified += len(commit.Modified) + } + msg = fmt.Sprintf("%s %s - pushed %d commit(s) to %s [+%d/-%d/\u00B1%d]: %s", msg, request.Pusher.Name, len(request.Commits), strings.TrimLeft(request.Ref, "refs/heads/"), added, removed, modified, request.Compare) + } else if event == "issues" || event == "issue_comment" { + msg = fmt.Sprintf("%s %s - %s action #%.0f: %s - %s", msg, request.Sender.Login, request.Action, request.Issue.Number, request.Issue.Title, request.Issue.HTMLURL) + } else { + text := eventMsg[event] + if text == "" { + text = event + } + msg = fmt.Sprintf("%s %s - %s", msg, request.Sender.Login, text) + } + return msg +} diff --git a/input/webhook/grafana/main.go b/input/webhook/grafana/main.go new file mode 100644 index 0000000..1c6ff16 --- /dev/null +++ b/input/webhook/grafana/main.go @@ -0,0 +1,67 @@ +package grafana + +import ( + "fmt" + "net/http" + "time" + + "github.com/mitchellh/mapstructure" + log "github.com/sirupsen/logrus" + + "dev.sum7.eu/genofire/logmania/input/webhook" +) + +type evalMatch struct { + Tags map[string]string `mapstructure:"tags,omitempty"` + Metric string `mapstructure:"metric"` + Value float64 `mapstructure:"value"` +} + +type requestBody struct { + Title string `mapstructure:"title"` + State string `mapstructure:"state"` + RuleID int64 `mapstructure:"ruleId"` + RuleName string `mapstructure:"ruleName"` + RuleURL string `mapstructure:"ruleUrl"` + EvalMatches []evalMatch `mapstructure:"evalMatches"` + ImageURL string `mapstructure:"imageUrl"` + Message string `mapstructure:"message"` +} + +const webhookType = "grafana" + +var HookstateMap = map[string]log.Level{ + "no_data": log.ErrorLevel, + "paused": log.InfoLevel, + "alerting": log.ErrorLevel, + "ok": log.InfoLevel, + "pending": log.WarnLevel, +} + +var logger = log.WithField("input", webhook.InputType).WithField("hook", webhookType) + +func handler(_ http.Header, body interface{}) *log.Entry { + var request requestBody + if err := mapstructure.Decode(body, &request); err != nil { + logger.Warnf("not able to decode data: %s", err) + return nil + } + if request.RuleURL == "" { + return nil + } + + entry := log.NewEntry(nil) + entry = entry.WithField("hostname", request.RuleURL) + entry.Time = time.Now() + entry.Level = HookstateMap[request.State] + if request.Message == "" { + entry.Message = fmt.Sprintf("%s - %s: %s", request.Title, request.State, request.RuleURL) + } else { + entry.Message = fmt.Sprintf("%s - %s: %s %s", request.Title, request.State, request.Message, request.RuleURL) + } + return entry +} + +func init() { + webhook.AddHandler(webhookType, handler) +} diff --git a/input/webhook/internal.go b/input/webhook/internal.go new file mode 100644 index 0000000..0163341 --- /dev/null +++ b/input/webhook/internal.go @@ -0,0 +1,15 @@ +package webhook + +import ( + "net/http" + + log "github.com/sirupsen/logrus" +) + +type WebhookHandler func(http.Header, interface{}) *log.Entry + +var handlers = make(map[string]WebhookHandler) + +func AddHandler(name string, f WebhookHandler) { + handlers[name] = f +} diff --git a/input/webhook/main.go b/input/webhook/main.go new file mode 100644 index 0000000..e6132bf --- /dev/null +++ b/input/webhook/main.go @@ -0,0 +1,56 @@ +package webhook + +import ( + "fmt" + "net/http" + + libHTTP "github.com/genofire/golang-lib/http" + log "github.com/sirupsen/logrus" + + "dev.sum7.eu/genofire/logmania/input" +) + +const InputType = "webhook" + +var logger = log.WithField("input", InputType) + +type Input struct { + input.Input + exportChannel chan *log.Entry +} + +func Init(config interface{}, exportChannel chan *log.Entry) input.Input { + logger.Info("init") + + return &Input{ + exportChannel: exportChannel, + } +} + +func (in *Input) getHTTPHandler(name string, h WebhookHandler) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var body interface{} + libHTTP.Read(r, &body) + + e := h(r.Header, body) + if e == nil { + http.Error(w, fmt.Sprintf("no able to generate log for handler-request %s", name), http.StatusInternalServerError) + return + } + in.exportChannel <- e + http.Error(w, fmt.Sprintf("handler-request %s - ok", name), http.StatusOK) + } +} + +func (in *Input) Listen() { + for name, h := range handlers { + http.HandleFunc("/input/"+InputType+"/"+name, in.getHTTPHandler(name, h)) + } +} + +func (in *Input) Close() { +} + +func init() { + input.Add(InputType, Init) +} diff --git a/output/all/internal.go b/output/all/internal.go index d9915ef..ced1d69 100644 --- a/output/all/internal.go +++ b/output/all/internal.go @@ -21,7 +21,7 @@ func Init(configInterface interface{}, db *database.DB, bot *bot.Bot) output.Out config := configInterface.(map[string]interface{}) var list []output.Output - var defaults []*database.Notify + for outputType, init := range output.Register { configForItem := config[outputType] if configForItem == nil { @@ -35,14 +35,12 @@ func Init(configInterface interface{}, db *database.DB, bot *bot.Bot) output.Out } list = append(list, notify) def := notify.Default() - if def != nil { + if def == nil { continue } - defaults = append(defaults, def...) + db.DefaultNotify = append(db.DefaultNotify, def...) } - db.DefaultNotify = defaults - out := &Output{ db: db, list: list, diff --git a/output/xmpp/main.go b/output/xmpp/main.go index 3a04be5..b52b553 100644 --- a/output/xmpp/main.go +++ b/output/xmpp/main.go @@ -143,7 +143,13 @@ func Init(configInterface interface{}, db *database.DB, bot *bot.Bot) output.Out logger.WithField("jid", config.JID).Info("startup") - var defaults []*database.Notify + out := &Output{ + channels: channels, + client: client, + formatter: &log.TextFormatter{ + DisableTimestamp: true, + }, + } for to, muc := range config.Defaults { def := &database.Notify{ Protocol: proto, @@ -152,16 +158,9 @@ func Init(configInterface interface{}, db *database.DB, bot *bot.Bot) output.Out if muc { def.Protocol = protoGroup } - defaults = append(defaults, def) - } - return &Output{ - channels: channels, - defaults: defaults, - client: client, - formatter: &log.TextFormatter{ - DisableTimestamp: true, - }, + out.defaults = append(out.defaults, def) } + return out } func (out *Output) Default() []*database.Notify {