Alerting: Support values in notification templates (#56457)

We have received a lot of feedback regarding the ValueString in alert notifications. Perhaps one of the most frequent complaints about ValueString is that it is difficult to read because it contains a lot of information, and the information is shown as a JSON-like string. Users have often asked how it can be templated and the answer is that it can't.

Until now users have been able to add custom annotations to their alert rules which contains values via the $values variable added in previous versions of Grafana. However, these custom annotations must be added for each of the user's alert rule, instead of once in a template that all of their alerts can be notified via.

This commit adds then the much requested feature to support values in notification templates. Users can then create a single template that prints the annotations, labels and values of their alerts in a format of their choice!
pull/56646/head
George Robinson 3 years ago committed by GitHub
parent 62674604b4
commit 802d67eeca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      emails/templates/ng_alert_notification.html
  2. 3
      pkg/services/ngalert/models/alert_rule.go
  3. 14
      pkg/services/ngalert/notifier/channels/default_template.go
  4. 24
      pkg/services/ngalert/notifier/channels/default_template_test.go
  5. 4
      pkg/services/ngalert/notifier/channels/dingding_test.go
  6. 36
      pkg/services/ngalert/notifier/channels/template_data.go
  7. 10
      pkg/services/ngalert/schedule/compat.go
  8. 1
      pkg/services/ngalert/schedule/compat_test.go
  9. 12
      pkg/services/ngalert/state/cache.go
  10. 79
      pkg/services/ngalert/state/manager_test.go
  11. 1
      pkg/services/ngalert/state/state.go
  12. 39
      pkg/tests/api/alerting/api_notification_channel_test.go
  13. 6
      public/app/features/alerting/unified/components/receivers/TemplateData.ts
  14. 6
      public/emails/ng_alert_notification.html

@ -3,6 +3,10 @@
[[Subject .Subject "[[.Title]]"]] [[Subject .Subject "[[.Title]]"]]
[[ define "__text_values_list" ]][[ $len := len .Values ]][[ if $len ]][[ $first := gt $len 1 ]][[ range $refID, $value := .Values -]]
[[ $refID ]]=[[ $value ]][[ if $first ]], [[ end ]][[ $first = false ]][[ end -]]
[[ else ]][no value][[ end ]][[ end ]]
[[ define "alert" ]] [[ define "alert" ]]
[[ if ne .ImageURL "" ]] [[ if ne .ImageURL "" ]]
@ -21,7 +25,7 @@
[[ end ]] [[ end ]]
<tr> <tr>
<td colspan="2" class="value"> <td colspan="2" class="value">
<span class="value-heading">Value:</span> <span class="value-value">[[ .ValueString ]]</span> <span class="value-heading">Value:</span> <span class="value-value">[[ template "__text_values_list" . ]]</span>
</td> </td>
</tr> </tr>
[[ if gt (len .Annotations.SortedPairs) 0 ]] [[ if gt (len .Annotations.SortedPairs) 0 ]]

@ -101,6 +101,9 @@ const (
// StateReasonAnnotation is the name of the annotation that explains the difference between evaluation state and alert state (i.e. changing state when NoData or Error). // StateReasonAnnotation is the name of the annotation that explains the difference between evaluation state and alert state (i.e. changing state when NoData or Error).
StateReasonAnnotation = GrafanaReservedLabelPrefix + "state_reason" StateReasonAnnotation = GrafanaReservedLabelPrefix + "state_reason"
ValuesAnnotation = "__values__"
ValueStringAnnotation = "__value_string__"
) )
var ( var (

@ -16,8 +16,12 @@ const (
var DefaultTemplateString = ` var DefaultTemplateString = `
{{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ if gt (.Alerts.Resolved | len) 0 }}, RESOLVED:{{ .Alerts.Resolved | len }}{{ end }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }} {{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ if gt (.Alerts.Resolved | len) 0 }}, RESOLVED:{{ .Alerts.Resolved | len }}{{ end }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }}
{{ define "__text_values_list" }}{{ $len := len .Values }}{{ if $len }}{{ $first := gt $len 1 }}{{ range $refID, $value := .Values -}}
{{ $refID }}={{ $value }}{{ if $first }}, {{ end }}{{ $first = false }}{{ end -}}
{{ else }}[no value]{{ end }}{{ end }}
{{ define "__text_alert_list" }}{{ range . }} {{ define "__text_alert_list" }}{{ range . }}
Value: {{ or .ValueString "[no value]" }} Value: {{ template "__text_values_list" . }}
Labels: Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}Annotations: {{ end }}Annotations:
@ -38,7 +42,7 @@ Labels:
{{ define "__teams_text_alert_list" }}{{ range . }} {{ define "__teams_text_alert_list" }}{{ range . }}
Value: {{ or .ValueString "[no value]" }} Value: {{ template "__text_values_list" . }}
Labels: Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }} {{ end }}
@ -70,8 +74,12 @@ Annotations:
const TemplateForTestsString = ` const TemplateForTestsString = `
{{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }} {{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }}
{{ define "__text_values_list" }}{{ $len := len .Values }}{{ if $len }}{{ $first := gt $len 1 }}{{ range $refID, $value := .Values -}}
{{ $refID }}={{ $value }}{{ if $first }}, {{ end }}{{ $first = false }}{{ end -}}
{{ else }}[no value]{{ end }}{{ end }}
{{ define "__text_alert_list" }}{{ range . }} {{ define "__text_alert_list" }}{{ range . }}
Value: {{ or .ValueString "[no value]" }} Value: {{ template "__text_values_list" . }}
Labels: Labels:
{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} {{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }}
{{ end }}Annotations: {{ end }}Annotations:

@ -20,7 +20,7 @@ func TestDefaultTemplateString(t *testing.T) {
Alert: model.Alert{ Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"}, Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
Annotations: model.LabelSet{ Annotations: model.LabelSet{
"ann1": "annv1", "__dashboardUid__": "dbuid123", "__panelId__": "puid123", "__value_string__": "1234", "ann1": "annv1", "__dashboardUid__": "dbuid123", "__panelId__": "puid123", "__values__": "{\"A\": 1234}", "__value_string__": "1234",
}, },
StartsAt: time.Now(), StartsAt: time.Now(),
EndsAt: time.Now().Add(1 * time.Hour), EndsAt: time.Now().Add(1 * time.Hour),
@ -29,7 +29,7 @@ func TestDefaultTemplateString(t *testing.T) {
}, { // Firing without dashboard and panel ID. }, { // Firing without dashboard and panel ID.
Alert: model.Alert{ Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val2"}, Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val2"},
Annotations: model.LabelSet{"ann1": "annv2", "__value_string__": "1234"}, Annotations: model.LabelSet{"ann1": "annv2", "__values__": "{\"A\": 1234}", "__value_string__": "1234"},
StartsAt: time.Now(), StartsAt: time.Now(),
EndsAt: time.Now().Add(2 * time.Hour), EndsAt: time.Now().Add(2 * time.Hour),
GeneratorURL: "http://localhost/alert2", GeneratorURL: "http://localhost/alert2",
@ -38,7 +38,7 @@ func TestDefaultTemplateString(t *testing.T) {
Alert: model.Alert{ Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val3"}, Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val3"},
Annotations: model.LabelSet{ Annotations: model.LabelSet{
"ann1": "annv3", "__dashboardUid__": "dbuid456", "__panelId__": "puid456", "__value_string__": "1234", "ann1": "annv3", "__dashboardUid__": "dbuid456", "__panelId__": "puid456", "__values__": "{\"A\": 1234}", "__value_string__": "1234",
}, },
StartsAt: time.Now().Add(-1 * time.Hour), StartsAt: time.Now().Add(-1 * time.Hour),
EndsAt: time.Now().Add(-30 * time.Minute), EndsAt: time.Now().Add(-30 * time.Minute),
@ -47,7 +47,7 @@ func TestDefaultTemplateString(t *testing.T) {
}, { // Resolved without dashboard and panel ID. }, { // Resolved without dashboard and panel ID.
Alert: model.Alert{ Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val4"}, Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val4"},
Annotations: model.LabelSet{"ann1": "annv4", "__value_string__": "1234"}, Annotations: model.LabelSet{"ann1": "annv4", "__values__": "{\"A\": 1234}", "__value_string__": "1234"},
StartsAt: time.Now().Add(-2 * time.Hour), StartsAt: time.Now().Add(-2 * time.Hour),
EndsAt: time.Now().Add(-3 * time.Hour), EndsAt: time.Now().Add(-3 * time.Hour),
GeneratorURL: "http://localhost/alert4", GeneratorURL: "http://localhost/alert4",
@ -91,7 +91,7 @@ func TestDefaultTemplateString(t *testing.T) {
templateString: DefaultMessageEmbed, templateString: DefaultMessageEmbed,
expected: `**Firing** expected: `**Firing**
Value: 1234 Value: A=1234
Labels: Labels:
- alertname = alert1 - alertname = alert1
- lbl1 = val1 - lbl1 = val1
@ -102,7 +102,7 @@ Silence: http://localhost/grafana/alerting/silence/new?alertmanager=grafana&matc
Dashboard: http://localhost/grafana/d/dbuid123 Dashboard: http://localhost/grafana/d/dbuid123
Panel: http://localhost/grafana/d/dbuid123?viewPanel=puid123 Panel: http://localhost/grafana/d/dbuid123?viewPanel=puid123
Value: 1234 Value: A=1234
Labels: Labels:
- alertname = alert1 - alertname = alert1
- lbl1 = val2 - lbl1 = val2
@ -114,7 +114,7 @@ Silence: http://localhost/grafana/alerting/silence/new?alertmanager=grafana&matc
**Resolved** **Resolved**
Value: 1234 Value: A=1234
Labels: Labels:
- alertname = alert1 - alertname = alert1
- lbl1 = val3 - lbl1 = val3
@ -125,7 +125,7 @@ Silence: http://localhost/grafana/alerting/silence/new?alertmanager=grafana&matc
Dashboard: http://localhost/grafana/d/dbuid456 Dashboard: http://localhost/grafana/d/dbuid456
Panel: http://localhost/grafana/d/dbuid456?viewPanel=puid456 Panel: http://localhost/grafana/d/dbuid456?viewPanel=puid456
Value: 1234 Value: A=1234
Labels: Labels:
- alertname = alert1 - alertname = alert1
- lbl1 = val4 - lbl1 = val4
@ -139,7 +139,7 @@ Silence: http://localhost/grafana/alerting/silence/new?alertmanager=grafana&matc
templateString: `{{ template "teams.default.message" .}}`, templateString: `{{ template "teams.default.message" .}}`,
expected: `**Firing** expected: `**Firing**
Value: 1234 Value: A=1234
Labels: Labels:
- alertname = alert1 - alertname = alert1
- lbl1 = val1 - lbl1 = val1
@ -157,7 +157,7 @@ Panel: [http://localhost/grafana/d/dbuid123?viewPanel=puid123](http://localhost/
Value: 1234 Value: A=1234
Labels: Labels:
- alertname = alert1 - alertname = alert1
- lbl1 = val2 - lbl1 = val2
@ -174,7 +174,7 @@ Silence: [http://localhost/grafana/alerting/silence/new?alertmanager=grafana&mat
**Resolved** **Resolved**
Value: 1234 Value: A=1234
Labels: Labels:
- alertname = alert1 - alertname = alert1
- lbl1 = val3 - lbl1 = val3
@ -192,7 +192,7 @@ Panel: [http://localhost/grafana/d/dbuid456?viewPanel=puid456](http://localhost/
Value: 1234 Value: A=1234
Labels: Labels:
- alertname = alert1 - alertname = alert1
- lbl1 = val4 - lbl1 = val4

@ -36,7 +36,7 @@ func TestDingdingNotifier(t *testing.T) {
{ {
Alert: model.Alert{ Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"}, Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh", "__value_string__": "1234"}, Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh", "__values__": "{\"A\": 1234}", "__value_string__": "1234"},
}, },
}, },
}, },
@ -44,7 +44,7 @@ func TestDingdingNotifier(t *testing.T) {
"msgtype": "link", "msgtype": "link",
"link": map[string]interface{}{ "link": map[string]interface{}{
"messageUrl": "dingtalk://dingtalkclient/page/link?pc_slide=false&url=http%3A%2F%2Flocalhost%2Falerting%2Flist", "messageUrl": "dingtalk://dingtalkclient/page/link?pc_slide=false&url=http%3A%2F%2Flocalhost%2Falerting%2Flist",
"text": "**Firing**\n\nValue: 1234\nLabels:\n - alertname = alert1\n - lbl1 = val1\nAnnotations:\n - ann1 = annv1\nSilence: http://localhost/alerting/silence/new?alertmanager=grafana&matcher=alertname%3Dalert1&matcher=lbl1%3Dval1\nDashboard: http://localhost/d/abcd\nPanel: http://localhost/d/abcd?viewPanel=efgh\n", "text": "**Firing**\n\nValue: A=1234\nLabels:\n - alertname = alert1\n - lbl1 = val1\nAnnotations:\n - ann1 = annv1\nSilence: http://localhost/alerting/silence/new?alertmanager=grafana&matcher=alertname%3Dalert1&matcher=lbl1%3Dval1\nDashboard: http://localhost/d/abcd\nPanel: http://localhost/d/abcd?viewPanel=efgh\n",
"title": "[FIRING:1] (val1)", "title": "[FIRING:1] (val1)",
}, },
}, },

@ -2,6 +2,7 @@ package channels
import ( import (
"context" "context"
"encoding/json"
"net/url" "net/url"
"path" "path"
"sort" "sort"
@ -18,19 +19,20 @@ import (
) )
type ExtendedAlert struct { type ExtendedAlert struct {
Status string `json:"status"` Status string `json:"status"`
Labels template.KV `json:"labels"` Labels template.KV `json:"labels"`
Annotations template.KV `json:"annotations"` Annotations template.KV `json:"annotations"`
StartsAt time.Time `json:"startsAt"` StartsAt time.Time `json:"startsAt"`
EndsAt time.Time `json:"endsAt"` EndsAt time.Time `json:"endsAt"`
GeneratorURL string `json:"generatorURL"` GeneratorURL string `json:"generatorURL"`
Fingerprint string `json:"fingerprint"` Fingerprint string `json:"fingerprint"`
SilenceURL string `json:"silenceURL"` SilenceURL string `json:"silenceURL"`
DashboardURL string `json:"dashboardURL"` DashboardURL string `json:"dashboardURL"`
PanelURL string `json:"panelURL"` PanelURL string `json:"panelURL"`
ValueString string `json:"valueString"` Values map[string]float64 `json:"values"`
ImageURL string `json:"imageURL,omitempty"` ValueString string `json:"valueString"` // TODO: Remove in Grafana 10
EmbeddedImage string `json:"embeddedImage,omitempty"` ImageURL string `json:"imageURL,omitempty"`
EmbeddedImage string `json:"embeddedImage,omitempty"`
} }
type ExtendedAlerts []ExtendedAlert type ExtendedAlerts []ExtendedAlert
@ -90,7 +92,13 @@ func extendAlert(alert template.Alert, externalURL string, logger log.Logger) *E
} }
if alert.Annotations != nil { if alert.Annotations != nil {
extended.ValueString = alert.Annotations[`__value_string__`] if s, ok := alert.Annotations[ngmodels.ValuesAnnotation]; ok {
if err := json.Unmarshal([]byte(s), &extended.Values); err != nil {
logger.Warn("failed to unmarshal values annotation", "err", err)
}
}
// TODO: Remove in Grafana 10
extended.ValueString = alert.Annotations[ngmodels.ValueStringAnnotation]
} }
matchers := make([]string, 0) matchers := make([]string, 0)

@ -1,6 +1,7 @@
package schedule package schedule
import ( import (
"encoding/json"
"fmt" "fmt"
"net/url" "net/url"
"path" "path"
@ -35,8 +36,15 @@ func stateToPostableAlert(alertState *state.State, appURL *url.URL) *models.Post
nL := alertState.Labels.Copy() nL := alertState.Labels.Copy()
nA := data.Labels(alertState.Annotations).Copy() nA := data.Labels(alertState.Annotations).Copy()
// encode the values as JSON where it will be expanded later
if len(alertState.Values) > 0 {
if b, err := json.Marshal(alertState.Values); err == nil {
nA[ngModels.ValuesAnnotation] = string(b)
}
}
if alertState.LastEvaluationString != "" { if alertState.LastEvaluationString != "" {
nA["__value_string__"] = alertState.LastEvaluationString nA[ngModels.ValueStringAnnotation] = alertState.LastEvaluationString
} }
if alertState.Image != nil { if alertState.Image != nil {

@ -276,5 +276,6 @@ func randomState(evalState eval.State) *state.State {
LastSentAt: randomTimeInPast(), LastSentAt: randomTimeInPast(),
Annotations: make(map[string]string), Annotations: make(map[string]string),
Labels: make(map[string]string), Labels: make(map[string]string),
Values: make(map[string]float64),
} }
} }

@ -3,6 +3,7 @@ package state
import ( import (
"context" "context"
"fmt" "fmt"
"math"
"net/url" "net/url"
"strings" "strings"
"sync" "sync"
@ -50,6 +51,15 @@ func (c *cache) getOrCreate(ctx context.Context, log log.Logger, alertRule *ngMo
func (rs *ruleStates) getOrCreate(ctx context.Context, log log.Logger, alertRule *ngModels.AlertRule, result eval.Result, extraLabels data.Labels, externalURL *url.URL) *State { func (rs *ruleStates) getOrCreate(ctx context.Context, log log.Logger, alertRule *ngModels.AlertRule, result eval.Result, extraLabels data.Labels, externalURL *url.URL) *State {
ruleLabels, annotations := rs.expandRuleLabelsAndAnnotations(ctx, log, alertRule, result, extraLabels, externalURL) ruleLabels, annotations := rs.expandRuleLabelsAndAnnotations(ctx, log, alertRule, result, extraLabels, externalURL)
values := make(map[string]float64)
for _, v := range result.Values {
if v.Value != nil {
values[v.Var] = *v.Value
} else {
values[v.Var] = math.NaN()
}
}
lbs := make(data.Labels, len(extraLabels)+len(ruleLabels)+len(result.Instance)) lbs := make(data.Labels, len(extraLabels)+len(ruleLabels)+len(result.Instance))
dupes := make(data.Labels) dupes := make(data.Labels)
for key, val := range extraLabels { for key, val := range extraLabels {
@ -102,6 +112,7 @@ func (rs *ruleStates) getOrCreate(ctx context.Context, log log.Logger, alertRule
} }
} }
state.Annotations = annotations state.Annotations = annotations
state.Values = values
rs.states[id] = state rs.states[id] = state
return state return state
} }
@ -115,6 +126,7 @@ func (rs *ruleStates) getOrCreate(ctx context.Context, log log.Logger, alertRule
Labels: lbs, Labels: lbs,
Annotations: annotations, Annotations: annotations,
EvaluationDuration: result.EvaluationDuration, EvaluationDuration: result.EvaluationDuration,
Values: values,
} }
if result.State == eval.Alerting { if result.State == eval.Alerting {
newState.StartsAt = result.EvaluatedAt newState.StartsAt = result.EvaluatedAt

@ -125,7 +125,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.Normal, Values: make(map[string]float64),
State: eval.Normal,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -179,7 +180,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label_1": "test", "instance_label_1": "test",
}, },
State: eval.Normal, Values: make(map[string]float64),
State: eval.Normal,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -202,7 +204,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label_2": "test", "instance_label_2": "test",
}, },
State: eval.Alerting, Values: make(map[string]float64),
State: eval.Alerting,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -259,7 +262,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.Normal, Values: make(map[string]float64),
State: eval.Normal,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -320,7 +324,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.Alerting, Values: make(map[string]float64),
State: eval.Alerting,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -392,7 +397,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.Alerting, Values: make(map[string]float64),
State: eval.Alerting,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -486,7 +492,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.Pending, Values: make(map[string]float64),
State: eval.Pending,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime.Add(30 * time.Second), EvaluationTime: evaluationTime.Add(30 * time.Second),
@ -567,7 +574,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.NoData, Values: make(map[string]float64),
State: eval.NoData,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime.Add(20 * time.Second), EvaluationTime: evaluationTime.Add(20 * time.Second),
@ -631,7 +639,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.Pending, Values: make(map[string]float64),
State: eval.Pending,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -695,7 +704,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.Pending, Values: make(map[string]float64),
State: eval.Pending,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -759,6 +769,7 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
Values: make(map[string]float64),
State: eval.Alerting, State: eval.Alerting,
StateReason: eval.NoData.String(), StateReason: eval.NoData.String(),
Results: []state.Evaluation{ Results: []state.Evaluation{
@ -824,7 +835,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.NoData, Values: make(map[string]float64),
State: eval.NoData,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -888,7 +900,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.Normal, Values: make(map[string]float64),
State: eval.Normal,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -912,7 +925,8 @@ func TestProcessEvalResults(t *testing.T) {
"alertname": "test_title", "alertname": "test_title",
"label": "test", "label": "test",
}, },
State: eval.NoData, Values: make(map[string]float64),
State: eval.NoData,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime.Add(10 * time.Second), EvaluationTime: evaluationTime.Add(10 * time.Second),
@ -977,7 +991,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test-1", "instance_label": "test-1",
}, },
State: eval.Normal, Values: make(map[string]float64),
State: eval.Normal,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -1002,7 +1017,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test-2", "instance_label": "test-2",
}, },
State: eval.Normal, Values: make(map[string]float64),
State: eval.Normal,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -1026,7 +1042,8 @@ func TestProcessEvalResults(t *testing.T) {
"alertname": "test_title", "alertname": "test_title",
"label": "test", "label": "test",
}, },
State: eval.NoData, Values: make(map[string]float64),
State: eval.NoData,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime.Add(10 * time.Second), EvaluationTime: evaluationTime.Add(10 * time.Second),
@ -1093,7 +1110,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.Normal, Values: make(map[string]float64),
State: eval.Normal,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -1122,7 +1140,8 @@ func TestProcessEvalResults(t *testing.T) {
"alertname": "test_title", "alertname": "test_title",
"label": "test", "label": "test",
}, },
State: eval.NoData, Values: make(map[string]float64),
State: eval.NoData,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime.Add(10 * time.Second), EvaluationTime: evaluationTime.Add(10 * time.Second),
@ -1181,6 +1200,7 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
Values: make(map[string]float64),
State: eval.Normal, State: eval.Normal,
StateReason: eval.NoData.String(), StateReason: eval.NoData.String(),
Results: []state.Evaluation{ Results: []state.Evaluation{
@ -1247,6 +1267,7 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
Values: make(map[string]float64),
State: eval.Alerting, State: eval.Alerting,
StateReason: eval.NoData.String(), StateReason: eval.NoData.String(),
@ -1314,6 +1335,7 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
Values: make(map[string]float64),
State: eval.Pending, State: eval.Pending,
StateReason: eval.Error.String(), StateReason: eval.Error.String(),
Results: []state.Evaluation{ Results: []state.Evaluation{
@ -1404,6 +1426,7 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
Values: make(map[string]float64),
State: eval.Alerting, State: eval.Alerting,
StateReason: eval.Error.String(), StateReason: eval.Error.String(),
Results: []state.Evaluation{ Results: []state.Evaluation{
@ -1485,7 +1508,8 @@ func TestProcessEvalResults(t *testing.T) {
"datasource_uid": "datasource_uid_1", "datasource_uid": "datasource_uid_1",
"ref_id": "A", "ref_id": "A",
}, },
State: eval.Error, Values: make(map[string]float64),
State: eval.Error,
Error: expr.QueryError{ Error: expr.QueryError{
RefID: "A", RefID: "A",
Err: errors.New("this is an error"), Err: errors.New("this is an error"),
@ -1562,6 +1586,7 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
Values: make(map[string]float64),
State: eval.Normal, State: eval.Normal,
StateReason: eval.Error.String(), StateReason: eval.Error.String(),
Error: nil, Error: nil,
@ -1635,6 +1660,7 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
Values: make(map[string]float64),
State: eval.Normal, State: eval.Normal,
StateReason: eval.Error.String(), StateReason: eval.Error.String(),
Error: nil, Error: nil,
@ -1734,7 +1760,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.Error, Values: make(map[string]float64),
State: eval.Error,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime.Add(40 * time.Second), EvaluationTime: evaluationTime.Add(40 * time.Second),
@ -1815,7 +1842,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.Alerting, Values: make(map[string]float64),
State: eval.Alerting,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime.Add(30 * time.Second), EvaluationTime: evaluationTime.Add(30 * time.Second),
@ -1902,7 +1930,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"instance_label": "test", "instance_label": "test",
}, },
State: eval.NoData, Values: make(map[string]float64),
State: eval.NoData,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime.Add(30 * time.Second), EvaluationTime: evaluationTime.Add(30 * time.Second),
@ -1964,7 +1993,8 @@ func TestProcessEvalResults(t *testing.T) {
"label": "test", "label": "test",
"job": "prod/grafana", "job": "prod/grafana",
}, },
State: eval.Normal, Values: make(map[string]float64),
State: eval.Normal,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,
@ -2117,7 +2147,8 @@ func TestStaleResultsHandler(t *testing.T) {
"alertname": rule.Title, "alertname": rule.Title,
"test1": "testValue1", "test1": "testValue1",
}, },
State: eval.Normal, Values: make(map[string]float64),
State: eval.Normal,
Results: []state.Evaluation{ Results: []state.Evaluation{
{ {
EvaluationTime: evaluationTime, EvaluationTime: evaluationTime,

@ -32,6 +32,7 @@ type State struct {
Resolved bool Resolved bool
Annotations map[string]string Annotations map[string]string
Labels data.Labels Labels data.Labels
Values map[string]float64
Image *models.Image Image *models.Image
Error error Error error
} }

@ -2275,6 +2275,7 @@ var expEmailNotifications = []*models.SendEmailCommandSync{
SilenceURL: "http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DEmailAlert&matcher=grafana_folder%3Ddefault", SilenceURL: "http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DEmailAlert&matcher=grafana_folder%3Ddefault",
DashboardURL: "", DashboardURL: "",
PanelURL: "", PanelURL: "",
Values: map[string]float64{"A": 1},
ValueString: "[ var='A' labels={} value=1 ]", ValueString: "[ var='A' labels={} value=1 ]",
}, },
}, },
@ -2332,7 +2333,7 @@ var expNonEmailNotifications = map[string][]string{
{ {
"title": "[FIRING:1] SlackAlert2 (default)", "title": "[FIRING:1] SlackAlert2 (default)",
"title_link": "http://localhost:3000/alerting/list", "title_link": "http://localhost:3000/alerting/list",
"text": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = SlackAlert2\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_SlackAlert2/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DSlackAlert2&matcher=grafana_folder%%3Ddefault\n", "text": "**Firing**\n\nValue: A=1\nLabels:\n - alertname = SlackAlert2\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_SlackAlert2/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DSlackAlert2&matcher=grafana_folder%%3Ddefault\n",
"fallback": "[FIRING:1] SlackAlert2 (default)", "fallback": "[FIRING:1] SlackAlert2 (default)",
"footer": "Grafana v", "footer": "Grafana v",
"footer_icon": "https://grafana.com/assets/img/fav32.png", "footer_icon": "https://grafana.com/assets/img/fav32.png",
@ -2365,7 +2366,7 @@ var expNonEmailNotifications = map[string][]string{
"component": "Integration Test", "component": "Integration Test",
"group": "testgroup", "group": "testgroup",
"custom_details": { "custom_details": {
"firing": "\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = PagerdutyAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_PagerdutyAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DPagerdutyAlert&matcher=grafana_folder%%3Ddefault\n", "firing": "\nValue: A=1\nLabels:\n - alertname = PagerdutyAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_PagerdutyAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DPagerdutyAlert&matcher=grafana_folder%%3Ddefault\n",
"num_firing": "1", "num_firing": "1",
"num_resolved": "0", "num_resolved": "0",
"resolved": "" "resolved": ""
@ -2385,7 +2386,7 @@ var expNonEmailNotifications = map[string][]string{
`{ `{
"link": { "link": {
"messageUrl": "dingtalk://dingtalkclient/page/link?pc_slide=false&url=http%3A%2F%2Flocalhost%3A3000%2Falerting%2Flist", "messageUrl": "dingtalk://dingtalkclient/page/link?pc_slide=false&url=http%3A%2F%2Flocalhost%3A3000%2Falerting%2Flist",
"text": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = DingDingAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_DingDingAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DDingDingAlert&matcher=grafana_folder%3Ddefault\n", "text": "**Firing**\n\nValue: A=1\nLabels:\n - alertname = DingDingAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_DingDingAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DDingDingAlert&matcher=grafana_folder%3Ddefault\n",
"title": "[FIRING:1] DingDingAlert (default)" "title": "[FIRING:1] DingDingAlert (default)"
}, },
"msgtype": "link" "msgtype": "link"
@ -2406,7 +2407,7 @@ var expNonEmailNotifications = map[string][]string{
"weight": "bolder", "weight": "bolder",
"wrap": true "wrap": true
}, { }, {
"text": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = TeamsAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_TeamsAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana\u0026matcher=alertname%3DTeamsAlert\u0026matcher=grafana_folder%3Ddefault\n", "text": "**Firing**\n\nValue: A=1\nLabels:\n - alertname = TeamsAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_TeamsAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana\u0026matcher=alertname%3DTeamsAlert\u0026matcher=grafana_folder%3Ddefault\n",
"type": "TextBlock", "type": "TextBlock",
"wrap": true "wrap": true
}, { }, {
@ -2447,7 +2448,8 @@ var expNonEmailNotifications = map[string][]string{
}, },
"annotations": {}, "annotations": {},
"startsAt": "%s", "startsAt": "%s",
"valueString": "[ var='A' labels={} value=1 ]", "values": {"A": 1},
"valueString": "[ var='A' labels={} value=1 ]",
"endsAt": "0001-01-01T00:00:00Z", "endsAt": "0001-01-01T00:00:00Z",
"generatorURL": "http://localhost:3000/alerting/grafana/UID_WebhookAlert/view", "generatorURL": "http://localhost:3000/alerting/grafana/UID_WebhookAlert/view",
"fingerprint": "15c59b0a380bd9f1", "fingerprint": "15c59b0a380bd9f1",
@ -2470,12 +2472,12 @@ var expNonEmailNotifications = map[string][]string{
"truncatedAlerts": 0, "truncatedAlerts": 0,
"title": "[FIRING:1] WebhookAlert (default)", "title": "[FIRING:1] WebhookAlert (default)",
"state": "alerting", "state": "alerting",
"message": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = WebhookAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_WebhookAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DWebhookAlert&matcher=grafana_folder%%3Ddefault\n" "message": "**Firing**\n\nValue: A=1\nLabels:\n - alertname = WebhookAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_WebhookAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DWebhookAlert&matcher=grafana_folder%%3Ddefault\n"
}`, }`,
}, },
"discord_recv/discord_test": { "discord_recv/discord_test": {
`{ `{
"content": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = DiscordAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_DiscordAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DDiscordAlert&matcher=grafana_folder%3Ddefault\n", "content": "**Firing**\n\nValue: A=1\nLabels:\n - alertname = DiscordAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_DiscordAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DDiscordAlert&matcher=grafana_folder%3Ddefault\n",
"embeds": [ "embeds": [
{ {
"color": 14037554, "color": 14037554,
@ -2503,7 +2505,7 @@ var expNonEmailNotifications = map[string][]string{
}, },
"name": "default" "name": "default"
}, },
"output": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = SensuGoAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_SensuGoAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DSensuGoAlert&matcher=grafana_folder%%3Ddefault\n", "output": "**Firing**\n\nValue: A=1\nLabels:\n - alertname = SensuGoAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_SensuGoAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DSensuGoAlert&matcher=grafana_folder%%3Ddefault\n",
"status": 2 "status": 2
}, },
"entity": { "entity": {
@ -2516,10 +2518,10 @@ var expNonEmailNotifications = map[string][]string{
}`, }`,
}, },
"pushover_recv/pushover_test": { "pushover_recv/pushover_test": {
"--abcd\r\nContent-Disposition: form-data; name=\"user\"\r\n\r\nmysecretkey\r\n--abcd\r\nContent-Disposition: form-data; name=\"token\"\r\n\r\nmysecrettoken\r\n--abcd\r\nContent-Disposition: form-data; name=\"priority\"\r\n\r\n0\r\n--abcd\r\nContent-Disposition: form-data; name=\"sound\"\r\n\r\n\r\n--abcd\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\n[FIRING:1] PushoverAlert (default)\r\n--abcd\r\nContent-Disposition: form-data; name=\"url\"\r\n\r\nhttp://localhost:3000/alerting/list\r\n--abcd\r\nContent-Disposition: form-data; name=\"url_title\"\r\n\r\nShow alert rule\r\n--abcd\r\nContent-Disposition: form-data; name=\"message\"\r\n\r\n**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = PushoverAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_PushoverAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DPushoverAlert&matcher=grafana_folder%3Ddefault\n\r\n--abcd\r\nContent-Disposition: form-data; name=\"html\"\r\n\r\n1\r\n--abcd--\r\n", "--abcd\r\nContent-Disposition: form-data; name=\"user\"\r\n\r\nmysecretkey\r\n--abcd\r\nContent-Disposition: form-data; name=\"token\"\r\n\r\nmysecrettoken\r\n--abcd\r\nContent-Disposition: form-data; name=\"priority\"\r\n\r\n0\r\n--abcd\r\nContent-Disposition: form-data; name=\"sound\"\r\n\r\n\r\n--abcd\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\n[FIRING:1] PushoverAlert (default)\r\n--abcd\r\nContent-Disposition: form-data; name=\"url\"\r\n\r\nhttp://localhost:3000/alerting/list\r\n--abcd\r\nContent-Disposition: form-data; name=\"url_title\"\r\n\r\nShow alert rule\r\n--abcd\r\nContent-Disposition: form-data; name=\"message\"\r\n\r\n**Firing**\n\nValue: A=1\nLabels:\n - alertname = PushoverAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_PushoverAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DPushoverAlert&matcher=grafana_folder%3Ddefault\n\r\n--abcd\r\nContent-Disposition: form-data; name=\"html\"\r\n\r\n1\r\n--abcd--\r\n",
}, },
"telegram_recv/bot6sh027hs034h": { "telegram_recv/bot6sh027hs034h": {
"--abcd\r\nContent-Disposition: form-data; name=\"chat_id\"\r\n\r\ntelegram_chat_id\r\n--abcd\r\nContent-Disposition: form-data; name=\"parse_mode\"\r\n\r\nhtml\r\n--abcd\r\nContent-Disposition: form-data; name=\"text\"\r\n\r\n**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = TelegramAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_TelegramAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DTelegramAlert&matcher=grafana_folder%3Ddefault\n\r\n--abcd--\r\n", "--abcd\r\nContent-Disposition: form-data; name=\"chat_id\"\r\n\r\ntelegram_chat_id\r\n--abcd\r\nContent-Disposition: form-data; name=\"parse_mode\"\r\n\r\nhtml\r\n--abcd\r\nContent-Disposition: form-data; name=\"text\"\r\n\r\n**Firing**\n\nValue: A=1\nLabels:\n - alertname = TelegramAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_TelegramAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DTelegramAlert&matcher=grafana_folder%3Ddefault\n\r\n--abcd--\r\n",
}, },
"googlechat_recv/googlechat_test": { "googlechat_recv/googlechat_test": {
`{ `{
@ -2535,7 +2537,7 @@ var expNonEmailNotifications = map[string][]string{
"widgets": [ "widgets": [
{ {
"textParagraph": { "textParagraph": {
"text": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = GoogleChatAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_GoogleChatAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DGoogleChatAlert&matcher=grafana_folder%%3Ddefault\n" "text": "**Firing**\n\nValue: A=1\nLabels:\n - alertname = GoogleChatAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_GoogleChatAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DGoogleChatAlert&matcher=grafana_folder%%3Ddefault\n"
} }
}, },
{ {
@ -2573,7 +2575,7 @@ var expNonEmailNotifications = map[string][]string{
"client": "Grafana", "client": "Grafana",
"client_url": "http://localhost:3000/alerting/list", "client_url": "http://localhost:3000/alerting/list",
"description": "[FIRING:1] KafkaAlert (default)", "description": "[FIRING:1] KafkaAlert (default)",
"details": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = KafkaAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_KafkaAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DKafkaAlert&matcher=grafana_folder%3Ddefault\n", "details": "**Firing**\n\nValue: A=1\nLabels:\n - alertname = KafkaAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_KafkaAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DKafkaAlert&matcher=grafana_folder%3Ddefault\n",
"incident_key": "35c0bdb1715f9162a20d7b2a01cb2e3a4c5b1dc663571701e3f67212b696332f" "incident_key": "35c0bdb1715f9162a20d7b2a01cb2e3a4c5b1dc663571701e3f67212b696332f"
} }
} }
@ -2581,10 +2583,10 @@ var expNonEmailNotifications = map[string][]string{
}`, }`,
}, },
"line_recv/line_test": { "line_recv/line_test": {
`message=%5BFIRING%3A1%5D+LineAlert+%28default%29%0Ahttp%3A%2Flocalhost%3A3000%2Falerting%2Flist%0A%0A%2A%2AFiring%2A%2A%0A%0AValue%3A+%5B+var%3D%27A%27+labels%3D%7B%7D+value%3D1+%5D%0ALabels%3A%0A+-+alertname+%3D+LineAlert%0A+-+grafana_folder+%3D+default%0AAnnotations%3A%0ASource%3A+http%3A%2F%2Flocalhost%3A3000%2Falerting%2Fgrafana%2FUID_LineAlert%2Fview%0ASilence%3A+http%3A%2F%2Flocalhost%3A3000%2Falerting%2Fsilence%2Fnew%3Falertmanager%3Dgrafana%26matcher%3Dalertname%253DLineAlert%26matcher%3Dgrafana_folder%253Ddefault%0A`, `message=%5BFIRING%3A1%5D+LineAlert+%28default%29%0Ahttp%3A%2Flocalhost%3A3000%2Falerting%2Flist%0A%0A%2A%2AFiring%2A%2A%0A%0AValue%3A+A%3D1%0ALabels%3A%0A+-+alertname+%3D+LineAlert%0A+-+grafana_folder+%3D+default%0AAnnotations%3A%0ASource%3A+http%3A%2F%2Flocalhost%3A3000%2Falerting%2Fgrafana%2FUID_LineAlert%2Fview%0ASilence%3A+http%3A%2F%2Flocalhost%3A3000%2Falerting%2Fsilence%2Fnew%3Falertmanager%3Dgrafana%26matcher%3Dalertname%253DLineAlert%26matcher%3Dgrafana_folder%253Ddefault%0A`,
}, },
"threema_recv/threema_test": { "threema_recv/threema_test": {
`from=%2A1234567&secret=myapisecret&text=%E2%9A%A0%EF%B8%8F+%5BFIRING%3A1%5D+ThreemaAlert+%28default%29%0A%0A%2AMessage%3A%2A%0A%2A%2AFiring%2A%2A%0A%0AValue%3A+%5B+var%3D%27A%27+labels%3D%7B%7D+value%3D1+%5D%0ALabels%3A%0A+-+alertname+%3D+ThreemaAlert%0A+-+grafana_folder+%3D+default%0AAnnotations%3A%0ASource%3A+http%3A%2F%2Flocalhost%3A3000%2Falerting%2Fgrafana%2FUID_ThreemaAlert%2Fview%0ASilence%3A+http%3A%2F%2Flocalhost%3A3000%2Falerting%2Fsilence%2Fnew%3Falertmanager%3Dgrafana%26matcher%3Dalertname%253DThreemaAlert%26matcher%3Dgrafana_folder%253Ddefault%0A%0A%2AURL%3A%2A+http%3A%2Flocalhost%3A3000%2Falerting%2Flist%0A&to=abcdefgh`, `from=%2A1234567&secret=myapisecret&text=%E2%9A%A0%EF%B8%8F+%5BFIRING%3A1%5D+ThreemaAlert+%28default%29%0A%0A%2AMessage%3A%2A%0A%2A%2AFiring%2A%2A%0A%0AValue%3A+A%3D1%0ALabels%3A%0A+-+alertname+%3D+ThreemaAlert%0A+-+grafana_folder+%3D+default%0AAnnotations%3A%0ASource%3A+http%3A%2F%2Flocalhost%3A3000%2Falerting%2Fgrafana%2FUID_ThreemaAlert%2Fview%0ASilence%3A+http%3A%2F%2Flocalhost%3A3000%2Falerting%2Fsilence%2Fnew%3Falertmanager%3Dgrafana%26matcher%3Dalertname%253DThreemaAlert%26matcher%3Dgrafana_folder%253Ddefault%0A%0A%2AURL%3A%2A+http%3A%2Flocalhost%3A3000%2Falerting%2Flist%0A&to=abcdefgh`,
}, },
"victorops_recv/victorops_test": { "victorops_recv/victorops_test": {
`{ `{
@ -2593,14 +2595,14 @@ var expNonEmailNotifications = map[string][]string{
"entity_id": "633ae988fa7074bcb51f3d1c5fef2ba1c5c4ccb45b3ecbf681f7d507b078b1ae", "entity_id": "633ae988fa7074bcb51f3d1c5fef2ba1c5c4ccb45b3ecbf681f7d507b078b1ae",
"message_type": "CRITICAL", "message_type": "CRITICAL",
"monitoring_tool": "Grafana v", "monitoring_tool": "Grafana v",
"state_message": "**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = VictorOpsAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_VictorOpsAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DVictorOpsAlert&matcher=grafana_folder%%3Ddefault\n", "state_message": "**Firing**\n\nValue: A=1\nLabels:\n - alertname = VictorOpsAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_VictorOpsAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%%3DVictorOpsAlert&matcher=grafana_folder%%3Ddefault\n",
"timestamp": %s "timestamp": %s
}`, }`,
}, },
"opsgenie_recv/opsgenie_test": { "opsgenie_recv/opsgenie_test": {
`{ `{
"alias": "47e92f0f6ef9fe99f3954e0d6155f8d09c4b9a038d8c3105e82c0cee4c62956e", "alias": "47e92f0f6ef9fe99f3954e0d6155f8d09c4b9a038d8c3105e82c0cee4c62956e",
"description": "[FIRING:1] OpsGenieAlert (default)\nhttp://localhost:3000/alerting/list\n\n**Firing**\n\nValue: [ var='A' labels={} value=1 ]\nLabels:\n - alertname = OpsGenieAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_OpsGenieAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DOpsGenieAlert&matcher=grafana_folder%3Ddefault\n", "description": "[FIRING:1] OpsGenieAlert (default)\nhttp://localhost:3000/alerting/list\n\n**Firing**\n\nValue: A=1\nLabels:\n - alertname = OpsGenieAlert\n - grafana_folder = default\nAnnotations:\nSource: http://localhost:3000/alerting/grafana/UID_OpsGenieAlert/view\nSilence: http://localhost:3000/alerting/silence/new?alertmanager=grafana&matcher=alertname%3DOpsGenieAlert&matcher=grafana_folder%3Ddefault\n",
"details": { "details": {
"url": "http://localhost:3000/alerting/list" "url": "http://localhost:3000/alerting/list"
}, },
@ -2619,8 +2621,9 @@ var expNonEmailNotifications = map[string][]string{
"grafana_folder": "default" "grafana_folder": "default"
}, },
"annotations": { "annotations": {
"__value_string__": "[ var='A' labels={} value=1 ]" "__values__": "{\"A\":1}",
}, "__value_string__": "[ var='A' labels={} value=1 ]"
},
"startsAt": "%s", "startsAt": "%s",
"endsAt": "0001-01-01T00:00:00Z", "endsAt": "0001-01-01T00:00:00Z",
"generatorURL": "http://localhost:3000/alerting/grafana/UID_AlertmanagerAlert/view", "generatorURL": "http://localhost:3000/alerting/grafana/UID_AlertmanagerAlert/view",

@ -75,6 +75,12 @@ export const AlertTemplateData: TemplateDataItem[] = [
type: 'KeyValue', type: 'KeyValue',
notes: 'Set of annotations attached to the alert.', notes: 'Set of annotations attached to the alert.',
}, },
{
name: 'Values',
type: 'KeyValue',
notes:
'The values of all instant queries, reduce and math expressions, and classic conditions for the alert. It does not contain time series data.',
},
{ {
name: 'StartsAt', name: 'StartsAt',
type: 'time.Time', type: 'time.Time',

@ -209,6 +209,10 @@ text-decoration: underline;
{{Subject .Subject "{{.Title}}"}} {{Subject .Subject "{{.Title}}"}}
{{ define "__text_values_list" }}{{ $len := len .Values }}{{ if $len }}{{ $first := gt $len 1 }}{{ range $refID, $value := .Values -}}
{{ $refID }}={{ $value }}{{ if $first }}, {{ end }}{{ $first = false }}{{ end -}}
{{ else }}[no value]{{ end }}{{ end }}
{{ define "alert" }} {{ define "alert" }}
{{ if ne .ImageURL "" }} {{ if ne .ImageURL "" }}
@ -227,7 +231,7 @@ text-decoration: underline;
{{ end }} {{ end }}
<tr style="vertical-align: top; padding: 0;" align="left"> <tr style="vertical-align: top; padding: 0;" align="left">
<td colspan="2" class="value" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 24px 0 0;" align="left" valign="top"> <td colspan="2" class="value" style="word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; color: #222222; font-family: 'Open Sans', 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-weight: normal; line-height: 19px; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; margin: 0; padding: 24px 0 0;" align="left" valign="top">
<span class="value-heading" style="font-weight: bold;">Value:</span> <span class="value-value" style="padding-left: 8px;">{{ .ValueString }}</span> <span class="value-heading" style="font-weight: bold;">Value:</span> <span class="value-value" style="padding-left: 8px;">{{ template "__text_values_list" . }}</span>
</td> </td>
</tr> </tr>
{{ if gt (len .Annotations.SortedPairs) 0 }} {{ if gt (len .Annotations.SortedPairs) 0 }}

Loading…
Cancel
Save