alertmanager/notify/notify.go

294 lines
6.7 KiB
Go
Raw Normal View History

2015-09-29 12:45:38 +00:00
package notify
import (
"fmt"
2015-09-29 13:12:31 +00:00
"sync"
"time"
2015-09-29 12:45:38 +00:00
2015-09-29 13:12:31 +00:00
"github.com/prometheus/common/log"
"github.com/prometheus/common/model"
2015-09-29 12:45:38 +00:00
"golang.org/x/net/context"
"github.com/prometheus/alertmanager/config"
2015-09-29 13:12:31 +00:00
"github.com/prometheus/alertmanager/provider"
2015-09-29 12:45:38 +00:00
"github.com/prometheus/alertmanager/types"
)
// The minimum timeout that is set for the context of a call
// to a notification pipeline.
const MinTimeout = 10 * time.Second
2015-09-29 13:12:31 +00:00
type notifyKey int
const (
keyDestination notifyKey = iota
keyRepeatInterval
keySendResolved
keyGroup
keyNow
2015-09-29 13:12:31 +00:00
)
func WithDestination(ctx context.Context, dest string) context.Context {
return context.WithValue(ctx, keyDestination, dest)
}
func WithRepeatInterval(ctx context.Context, t time.Duration) context.Context {
return context.WithValue(ctx, keyRepeatInterval, t)
}
func WithSendResolved(ctx context.Context, b bool) context.Context {
return context.WithValue(ctx, keySendResolved, b)
}
func WithGroup(ctx context.Context, g string) context.Context {
return context.WithValue(ctx, keyGroup, g)
}
func WithNow(ctx context.Context, t time.Time) context.Context {
return context.WithValue(ctx, keyNow, t)
}
func Destination(ctx context.Context) (string, bool) {
v, ok := ctx.Value(keyDestination).(string)
return v, ok
}
func RepeatInterval(ctx context.Context) (time.Duration, bool) {
v, ok := ctx.Value(keyRepeatInterval).(time.Duration)
return v, ok
}
func SendResolved(ctx context.Context) (bool, bool) {
v, ok := ctx.Value(keySendResolved).(bool)
return v, ok
}
func Group(ctx context.Context) (string, bool) {
v, ok := ctx.Value(keyGroup).(string)
return v, ok
}
func Now(ctx context.Context) (time.Time, bool) {
v, ok := ctx.Value(keyNow).(time.Time)
return v, ok
}
2015-09-29 13:12:31 +00:00
type Notifier interface {
Notify(context.Context, ...*types.Alert) error
}
2015-09-29 13:12:31 +00:00
// Notifiers fans out notifications to all notifiers it holds
// at once.
type Notifiers []Notifier
func (ns Notifiers) Notify(ctx context.Context, alerts ...*types.Alert) error {
var wg sync.WaitGroup
wg.Add(len(ns))
for _, n := range ns {
go func(n Notifier) {
err := n.Notify(ctx, alerts...)
if err != nil {
log.Errorf("Error on notify: %s", err)
}
wg.Done()
}(n)
}
wg.Wait()
return nil
2015-09-29 12:45:38 +00:00
}
2015-09-29 13:12:31 +00:00
type DedupingNotifier struct {
notifies provider.Notifies
notifier Notifier
2015-09-29 12:45:38 +00:00
}
2015-09-29 13:12:31 +00:00
func NewDedupingNotifier(notifies provider.Notifies, n Notifier) *DedupingNotifier {
return &DedupingNotifier{
notifies: notifies,
notifier: n,
}
2015-09-29 12:45:38 +00:00
}
2015-09-29 13:12:31 +00:00
func (n *DedupingNotifier) Notify(ctx context.Context, alerts ...*types.Alert) error {
name, ok := Destination(ctx)
2015-09-29 13:12:31 +00:00
if !ok {
return fmt.Errorf("notifier name missing")
}
repeatInterval, ok := RepeatInterval(ctx)
2015-09-29 13:12:31 +00:00
if !ok {
return fmt.Errorf("repeat interval missing")
2015-09-29 12:45:38 +00:00
}
2015-09-29 13:12:31 +00:00
sendResolved, ok := SendResolved(ctx)
2015-09-29 13:12:31 +00:00
if !ok {
return fmt.Errorf("send resolved missing")
}
now, ok := Now(ctx)
if !ok {
return fmt.Errorf("now time missing")
}
2015-09-29 13:12:31 +00:00
var fps []model.Fingerprint
2015-09-29 12:45:38 +00:00
for _, a := range alerts {
2015-09-29 13:12:31 +00:00
fps = append(fps, a.Fingerprint())
2015-09-29 12:45:38 +00:00
}
2015-09-29 13:12:31 +00:00
notifies, err := n.notifies.Get(name, fps...)
if err != nil {
2015-09-29 12:45:38 +00:00
return err
}
var (
doResend bool
resendQueue []*types.Alert
filtered []*types.Alert
)
2015-09-29 13:12:31 +00:00
for i, a := range alerts {
last := notifies[i]
if last != nil {
if a.Resolved() {
if !sendResolved {
continue
}
// If the initial alert was not delivered successfully,
// there is no point in sending a resolved notification.
if !last.Resolved && !last.Delivered {
continue
}
if last.Resolved && last.Delivered {
continue
}
} else if !last.Resolved {
// Do not send again if last was delivered unless
// the repeat interval has already passed.
if last.Delivered {
if !now.After(last.Timestamp.Add(repeatInterval)) {
// To not repeat initial batch fragmentation after the repeat interval
// has passed, store them and send them anyway if on of the other
// alerts has already passed the repeat interval.
// This way we unify batches again.
resendQueue = append(resendQueue, a)
continue
} else {
doResend = true
}
2015-09-29 13:12:31 +00:00
}
}
2015-10-02 13:58:37 +00:00
} else if a.Resolved() && !sendResolved {
continue
2015-09-29 13:12:31 +00:00
}
filtered = append(filtered, a)
}
// As we are resending an alert anyway, resend all of them even if their
// repeat interval has not yet passed.
if doResend {
filtered = append(filtered, resendQueue...)
}
var newNotifies []*types.Notify
2015-09-29 13:12:31 +00:00
for _, a := range filtered {
2015-09-29 13:12:31 +00:00
newNotifies = append(newNotifies, &types.Notify{
Alert: a.Fingerprint(),
SendTo: name,
Delivered: true,
Resolved: a.Resolved(),
Timestamp: now,
})
}
// The deduping notifier is the last one before actually sending notifications.
// Thus, this is the place where we abort if after all filtering, nothing is left.
if len(filtered) == 0 {
return nil
}
2015-09-29 13:12:31 +00:00
if err := n.notifier.Notify(ctx, filtered...); err != nil {
2015-09-29 12:45:38 +00:00
return err
}
2015-10-06 18:40:52 +00:00
return n.notifies.Set(newNotifies...)
2015-09-29 13:12:31 +00:00
}
// RoutedNotifier dispatches the alerts to one of a set of
// named notifiers based on the name value provided in the context.
type RoutedNotifier struct {
mtx sync.RWMutex
notifiers map[string]Notifier
2015-09-29 13:12:31 +00:00
// build creates a new set of named notifiers based on a config.
build func([]*config.NotificationConfig) map[string]Notifier
}
func NewRoutedNotifier(build func([]*config.NotificationConfig) map[string]Notifier) *RoutedNotifier {
return &RoutedNotifier{build: build}
}
func (n *RoutedNotifier) Notify(ctx context.Context, alerts ...*types.Alert) error {
name, ok := Destination(ctx)
2015-09-29 13:12:31 +00:00
if !ok {
return fmt.Errorf("notifier name missing")
}
n.mtx.RLock()
defer n.mtx.RUnlock()
notifier, ok := n.notifiers[name]
if !ok {
return fmt.Errorf("notifier %q does not exist", name)
}
return notifier.Notify(ctx, alerts...)
}
func (n *RoutedNotifier) ApplyConfig(conf *config.Config) {
2015-09-29 13:12:31 +00:00
n.mtx.Lock()
defer n.mtx.Unlock()
n.notifiers = n.build(conf.NotificationConfigs)
}
// MutingNotifier wraps a notifier and applies a Silencer
// before sending out an alert.
type MutingNotifier struct {
types.Muter
Notifier Notifier
}
func (n *MutingNotifier) Notify(ctx context.Context, alerts ...*types.Alert) error {
var filtered []*types.Alert
for _, a := range alerts {
// TODO(fabxc): increment total alerts counter.
// Do not send the alert if the silencer mutes it.
if !n.Mutes(a.Labels) {
// TODO(fabxc): increment muted alerts counter.
filtered = append(filtered, a)
}
}
return n.Notifier.Notify(ctx, filtered...)
}
type LogNotifier struct {
2015-09-30 12:54:13 +00:00
Log log.Logger
Notifier Notifier
2015-09-29 13:12:31 +00:00
}
func (ln *LogNotifier) Notify(ctx context.Context, alerts ...*types.Alert) error {
2015-09-30 12:54:13 +00:00
ln.Log.Debugf("notify %v", alerts)
2015-09-29 13:12:31 +00:00
2015-09-30 12:54:13 +00:00
if ln.Notifier != nil {
return ln.Notifier.Notify(ctx, alerts...)
2015-09-29 13:12:31 +00:00
}
2015-09-29 12:45:38 +00:00
return nil
}