mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting template functions (#39261)
* Alerting: (wip) add template funcs * Alerting: (wip) numeric template functions * Alerting: (wip) template functions * Test for the "args" function * Alerting: (wip) Documentation for template functions * Alerting: template functions - refactor * code review changes * disable linter error * Use Prometheus implementation of TemplateExpander * Update docs/sources/alerting/unified-alerting/alerting-rules/create-grafana-managed-rule.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * change templateCaptureValue to support using template functions * Update pkg/services/ngalert/state/template.go Co-authored-by: gotjosh <josue.abreu@gmail.com> * Test and documentation added for reReplaceAll template function * complete missing functions, documentation and tests * Use the alert instance's evaluation time for expanding the template * strvalue graphlink and tablelink functions * delete duplicate test * make strvalue return an empty string Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> Co-authored-by: gotjosh <josue.abreu@gmail.com>
This commit is contained in:
@@ -1,13 +1,10 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
text_template "text/template"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
|
||||
@@ -19,17 +16,19 @@ import (
|
||||
)
|
||||
|
||||
type cache struct {
|
||||
states map[int64]map[string]map[string]*State // orgID > alertRuleUID > stateID > state
|
||||
mtxStates sync.RWMutex
|
||||
log log.Logger
|
||||
metrics *metrics.State
|
||||
states map[int64]map[string]map[string]*State // orgID > alertRuleUID > stateID > state
|
||||
mtxStates sync.RWMutex
|
||||
log log.Logger
|
||||
metrics *metrics.State
|
||||
externalURL *url.URL
|
||||
}
|
||||
|
||||
func newCache(logger log.Logger, metrics *metrics.State) *cache {
|
||||
func newCache(logger log.Logger, metrics *metrics.State, externalURL *url.URL) *cache {
|
||||
return &cache{
|
||||
states: make(map[int64]map[string]map[string]*State),
|
||||
log: logger,
|
||||
metrics: metrics,
|
||||
states: make(map[int64]map[string]map[string]*State),
|
||||
log: logger,
|
||||
metrics: metrics,
|
||||
externalURL: externalURL,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +92,7 @@ func (c *cache) expandRuleLabelsAndAnnotations(alertRule *ngModels.AlertRule, la
|
||||
expand := func(original map[string]string) map[string]string {
|
||||
expanded := make(map[string]string, len(original))
|
||||
for k, v := range original {
|
||||
ev, err := expandTemplate(alertRule.Title, v, labels, alertInstance)
|
||||
ev, err := expandTemplate(alertRule.Title, v, labels, alertInstance, c.externalURL)
|
||||
expanded[k] = ev
|
||||
if err != nil {
|
||||
c.log.Error("error in expanding template", "name", k, "value", v, "err", err.Error())
|
||||
@@ -107,70 +106,6 @@ func (c *cache) expandRuleLabelsAndAnnotations(alertRule *ngModels.AlertRule, la
|
||||
return expand(alertRule.Labels), expand(alertRule.Annotations)
|
||||
}
|
||||
|
||||
// templateCaptureValue represents each value in .Values in the annotations
|
||||
// and labels template.
|
||||
type templateCaptureValue struct {
|
||||
Labels map[string]string
|
||||
Value float64
|
||||
}
|
||||
|
||||
// String implements the Stringer interface to print the value of each RefID
|
||||
// in the template via {{ $values.A }} rather than {{ $values.A.Value }}.
|
||||
func (v templateCaptureValue) String() string {
|
||||
return strconv.FormatFloat(v.Value, 'f', -1, 64)
|
||||
}
|
||||
|
||||
func expandTemplate(name, text string, labels map[string]string, alertInstance eval.Result) (result string, resultErr error) {
|
||||
name = "__alert_" + name
|
||||
text = "{{- $labels := .Labels -}}{{- $values := .Values -}}{{- $value := .Value -}}" + text
|
||||
// It'd better to have no alert description than to kill the whole process
|
||||
// if there's a bug in the template.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
var ok bool
|
||||
resultErr, ok = r.(error)
|
||||
if !ok {
|
||||
resultErr = fmt.Errorf("panic expanding template %v: %v", name, r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
tmpl, err := text_template.New(name).Option("missingkey=error").Parse(text)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error parsing template %v: %s", name, err.Error())
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
if err := tmpl.Execute(&buffer, struct {
|
||||
Labels map[string]string
|
||||
Values map[string]templateCaptureValue
|
||||
Value string
|
||||
}{
|
||||
Labels: labels,
|
||||
Values: newTemplateCaptureValues(alertInstance.Values),
|
||||
Value: alertInstance.EvaluationString,
|
||||
}); err != nil {
|
||||
return "", fmt.Errorf("error executing template %v: %s", name, err.Error())
|
||||
}
|
||||
return buffer.String(), nil
|
||||
}
|
||||
|
||||
func newTemplateCaptureValues(values map[string]eval.NumberValueCapture) map[string]templateCaptureValue {
|
||||
m := make(map[string]templateCaptureValue)
|
||||
for k, v := range values {
|
||||
var f float64
|
||||
if v.Value != nil {
|
||||
f = *v.Value
|
||||
} else {
|
||||
f = math.NaN()
|
||||
}
|
||||
m[k] = templateCaptureValue{
|
||||
Labels: v.Labels,
|
||||
Value: f,
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (c *cache) set(entry *State) {
|
||||
c.mtxStates.Lock()
|
||||
defer c.mtxStates.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user