VictoriaMetrics/app/vmalert/provider/common.go
kreedom b22da547a2
[vmalert] - parse template annotaions (#387)
* [vmalert] - parse template annotations
2020-03-27 18:31:16 +02:00

101 lines
2.7 KiB
Go

package provider
import (
"bytes"
"strings"
"text/template"
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/config"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/datasource"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
)
// AlertProvider is common interface for alert manager provider
type AlertProvider interface {
Send(alerts []Alert) error
}
// Alert the triggered alert
type Alert struct {
Group string
Name string
Labels []datasource.Label
Annotations map[string]string
Start time.Time
End time.Time
Value float64
}
type alertTplData struct {
Labels map[string]string
ExternalLabels map[string]string
Value float64
}
const tplHeader = `{{ $value := .Value }}{{ $labels := .Labels }}{{ $externalLabels := .ExternalLabels }}`
// AlertsFromMetrics converts metrics to alerts by alert Rule
func AlertsFromMetrics(metrics []datasource.Metric, group string, rule config.Rule, start, end time.Time) []Alert {
alerts := make([]Alert, 0, len(metrics))
for i, m := range metrics {
a := Alert{
Group: group,
Name: rule.Name,
Start: start,
End: end,
Value: m.Value,
}
tplData := alertTplData{Value: m.Value, ExternalLabels: make(map[string]string)}
tplData.Labels, a.Labels = mergeLabels(metrics[i].Labels, rule.Labels)
a.Annotations = templateAnnotations(rule.Annotations, tplHeader, tplData)
alerts = append(alerts, a)
}
return alerts
}
func mergeLabels(ml []datasource.Label, rl map[string]string) (map[string]string, []datasource.Label) {
set := make(map[string]string, len(ml)+len(rl))
sl := append([]datasource.Label(nil), ml...)
for _, i := range ml {
set[i.Name] = i.Value
}
for name, value := range rl {
if _, ok := set[name]; ok {
continue
}
set[name] = value
sl = append(sl, datasource.Label{
Name: name,
Value: value,
})
}
return set, sl
}
func templateAnnotations(annotations map[string]string, header string, data alertTplData) map[string]string {
var builder strings.Builder
var buf bytes.Buffer
r := make(map[string]string, len(annotations))
for key, text := range annotations {
r[key] = text
buf.Reset()
builder.Reset()
builder.Grow(len(header) + len(text))
builder.WriteString(header)
builder.WriteString(text)
// todo add template helper func from Prometheus
tpl, err := template.New("").Option("missingkey=zero").Parse(builder.String())
if err != nil {
logger.Errorf("error parsing annotation template %q for %q:%s", text, key, err)
continue
}
if err = tpl.Execute(&buf, data); err != nil {
logger.Errorf("error evaluating annotation template %s for %s:%s", text, key, err)
continue
}
r[key] = buf.String()
}
return r
}