diff --git a/examples/events.md b/examples/events.md index e3d65b5..12d0a6e 100644 --- a/examples/events.md +++ b/examples/events.md @@ -27,4 +27,10 @@ sre/provider/grafana.go:127 Annotation 1502808. Annotation added ## DataDog UO ![DataDog](/examples/datadog-events.png) -![DataDog](/examples/datadog-events2.png) \ No newline at end of file +![DataDog](/examples/datadog-events2.png) + +## Slack UI + +Send simple messages from `name` field or structured slack BlockKit json from `attributes["payload"]`(if present) + +![Slack](/examples/slack-events.png) \ No newline at end of file diff --git a/examples/events_slack.go b/examples/events_slack.go new file mode 100644 index 0000000..5ba8400 --- /dev/null +++ b/examples/events_slack.go @@ -0,0 +1,57 @@ +package main + +import ( + _ "embed" + "time" + + "github.com/devopsext/sre/common" + "github.com/devopsext/sre/provider" +) + +//go:embed slack.json +var payload []byte + +var logs = common.NewLogs() +var events = common.NewEvents() + +func test() { + + events.Now(":exclamation: First", nil) + + m := make(map[string]string) + m["payload"] = string(payload) + + events.Now(":grey_exclamation:Second", m) + events.Now(":grey_question: *Third*", nil) +} + +func main() { + + defer logs.Stop() // finalize logs delivery + defer events.Stop() // finalize events delivery + + // initialize Stdout logger + stdout := provider.NewStdout(provider.StdoutOptions{ + Format: "template", + Level: "debug", + Template: "{{.file}} {{.msg}}", + TimestampFormat: time.RFC3339Nano, + TextColors: true, + }) + // set caller offset for file:line proper usage + stdout.SetCallerOffset(2) + + // add Stdout logger + logs.Register(stdout) + + slack := provider.NewSlackEventer(provider.SlackOptions{ + WebHook: "", + Tags: "", + Timeout: 2, + }, logs, stdout) + + // add events + events.Register(slack) + + test() +} diff --git a/examples/slack-events.png b/examples/slack-events.png new file mode 100644 index 0000000..80d8e3e Binary files /dev/null and b/examples/slack-events.png differ diff --git a/examples/slack.json b/examples/slack.json new file mode 100644 index 0000000..eeb2026 --- /dev/null +++ b/examples/slack.json @@ -0,0 +1,35 @@ +{ + "blocks": [ + { + "type": "header", + "text": { + "type": "plain_text", + "text": "Daily News", + "emoji": true + } + }, + { + "type": "divider" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "`06:45 UTC (08:45 ETT)`:\t:grey_exclamation:\t| Germany_Industrial Orders MM" + } + }, + { + "type": "section", + "fields": [ + { + "type": "mrkdwn", + "text": "*Symbol*: EUR / EUR,CHF,XAG,XAU" + }, + { + "type": "mrkdwn", + "text": "*Type*: Industry Sector" + } + ] + } + ] +} \ No newline at end of file diff --git a/provider/slack.go b/provider/slack.go new file mode 100644 index 0000000..3eb44a2 --- /dev/null +++ b/provider/slack.go @@ -0,0 +1,84 @@ +package provider + +import ( + "bytes" + "context" + "fmt" + "github.com/devopsext/sre/common" + "github.com/devopsext/utils" + "io/ioutil" + "net/http" + "time" +) + +type SlackOptions struct { + WebHook string + Tags string + Timeout int +} + +type SlackEventer struct { + options SlackOptions + logger common.Logger + tags []string + client *http.Client + ctx context.Context +} + +func (se *SlackEventer) Now(name string, attributes map[string]string) { + se.At(name, attributes, time.Now()) +} + +func (se *SlackEventer) At(name string, attributes map[string]string, when time.Time) { + se.Interval(name, attributes, when, when) +} + +func (se *SlackEventer) Interval(name string, attributes map[string]string, begin, end time.Time) { + var body string + + body = fmt.Sprintf("{\"text\": \"%s\"}", name) + if payload, ok := attributes["payload"]; ok { + body = payload + } + + resp, err := http.Post(se.options.WebHook, "application/json", bytes.NewBuffer([]byte(body))) + if err != nil { + se.logger.Error("slack post:", err) + return + } + + defer resp.Body.Close() + + rBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + se.logger.Error("slack post response:", err) + return + } + se.logger.Debug(string(rBody)) +} + +func (se *SlackEventer) Stop() { + se.client.CloseIdleConnections() + se.logger.Info("Slack Eventer stopped.") +} + +func NewSlackEventer(options SlackOptions, logger common.Logger, stdout *Stdout) *SlackEventer { + if logger == nil { + logger = stdout + } + + if utils.IsEmpty(options.WebHook) { + logger.Debug("Slack Eventer is disabled") + return nil + } + + logger.Info("Slack Eventer is up…") + + return &SlackEventer{ + options: options, + logger: logger, + tags: common.MapToArray(common.GetKeyValues(options.Tags)), + client: common.MakeHttpClient(options.Timeout), + ctx: context.Background(), + } +}