1
2
3
4
5
6
7
8
9
10
11
12
13
14 package webhook
15
16 import (
17 "bytes"
18 "context"
19 "encoding/json"
20 "fmt"
21 "io"
22 "net/http"
23
24 "github.com/go-kit/log"
25 "github.com/go-kit/log/level"
26 commoncfg "github.com/prometheus/common/config"
27
28 "github.com/prometheus/alertmanager/config"
29 "github.com/prometheus/alertmanager/notify"
30 "github.com/prometheus/alertmanager/template"
31 "github.com/prometheus/alertmanager/types"
32 )
33
34
35 type Notifier struct {
36 conf *config.WebhookConfig
37 tmpl *template.Template
38 logger log.Logger
39 client *http.Client
40 retrier *notify.Retrier
41 }
42
43
44 func New(conf *config.WebhookConfig, t *template.Template, l log.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) {
45 client, err := commoncfg.NewClientFromConfig(*conf.HTTPConfig, "webhook", httpOpts...)
46 if err != nil {
47 return nil, err
48 }
49 return &Notifier{
50 conf: conf,
51 tmpl: t,
52 logger: l,
53 client: client,
54
55
56 retrier: ¬ify.Retrier{
57 CustomDetailsFunc: func(_ int, body io.Reader) string {
58 return errDetails(body, conf.URL.String())
59 },
60 },
61 }, nil
62 }
63
64
65 type Message struct {
66 *template.Data
67
68
69 Version string `json:"version"`
70 GroupKey string `json:"groupKey"`
71 TruncatedAlerts uint64 `json:"truncatedAlerts"`
72 }
73
74 func truncateAlerts(maxAlerts uint64, alerts []*types.Alert) ([]*types.Alert, uint64) {
75 if maxAlerts != 0 && uint64(len(alerts)) > maxAlerts {
76 return alerts[:maxAlerts], uint64(len(alerts)) - maxAlerts
77 }
78
79 return alerts, 0
80 }
81
82
83 func (n *Notifier) Notify(ctx context.Context, alerts ...*types.Alert) (bool, error) {
84 alerts, numTruncated := truncateAlerts(n.conf.MaxAlerts, alerts)
85 data := notify.GetTemplateData(ctx, n.tmpl, alerts, n.logger)
86
87 groupKey, err := notify.ExtractGroupKey(ctx)
88 if err != nil {
89 level.Error(n.logger).Log("err", err)
90 }
91
92 msg := &Message{
93 Version: "4",
94 Data: data,
95 GroupKey: groupKey.String(),
96 TruncatedAlerts: numTruncated,
97 }
98
99 var buf bytes.Buffer
100 if err := json.NewEncoder(&buf).Encode(msg); err != nil {
101 return false, err
102 }
103
104 resp, err := notify.PostJSON(ctx, n.client, n.conf.URL.String(), &buf)
105 if err != nil {
106 return true, err
107 }
108 defer notify.Drain(resp)
109
110 return n.retrier.Check(resp.StatusCode, resp.Body)
111 }
112
113 func errDetails(body io.Reader, url string) string {
114 if body == nil {
115 return url
116 }
117 bs, err := io.ReadAll(body)
118 if err != nil {
119 return url
120 }
121 return fmt.Sprintf("%s: %s", url, string(bs))
122 }
123
View as plain text