Alerting: Allow customization of Google chat message (#43568)

* Allow customizable googlechat message via optional setting

* Add optional message field in googlechat contact point configurator

* Fix strange error message on send if template fails to fully evaluate

* Elevate template evaluation failure logs to Warn level

* Extract default.title template embed from all channels to shared constant
This commit is contained in:
Alexander Weaver
2022-01-05 09:47:08 -06:00
committed by GitHub
parent b826804ef7
commit fd583a0e3b
23 changed files with 197 additions and 43 deletions

View File

@@ -740,6 +740,12 @@ func GetAvailableNotifiers() []*alerting.NotifierPlugin {
PropertyName: "url", PropertyName: "url",
Required: true, Required: true,
}, },
{
Label: "Message",
Element: alerting.ElementTypeTextArea,
Placeholder: `{{ template "default.message" . }}`,
PropertyName: "message",
},
}, },
}, },
{ {

View File

@@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
const DefaultMessageTitleEmbed = `{{ template "default.title" . }}`
var DefaultTemplateString = ` var DefaultTemplateString = `
{{ 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 }}

View File

@@ -85,7 +85,7 @@ func TestDefaultTemplateString(t *testing.T) {
expected string expected string
}{ }{
{ {
templateString: `{{ template "default.title" .}}`, templateString: DefaultMessageTitleEmbed,
expected: `[FIRING:2] (alert1)`, expected: `[FIRING:2] (alert1)`,
}, },
{ {

View File

@@ -74,7 +74,7 @@ func (dd *DingDingNotifier) Notify(ctx context.Context, as ...*types.Alert) (boo
tmpl, _ := TmplText(ctx, dd.tmpl, as, dd.log, &tmplErr) tmpl, _ := TmplText(ctx, dd.tmpl, as, dd.log, &tmplErr)
message := tmpl(dd.Message) message := tmpl(dd.Message)
title := tmpl(`{{ template "default.title" . }}`) title := tmpl(DefaultMessageTitleEmbed)
var bodyMsg map[string]interface{} var bodyMsg map[string]interface{}
if tmpl(dd.MsgType) == "actionCard" { if tmpl(dd.MsgType) == "actionCard" {
@@ -102,7 +102,7 @@ func (dd *DingDingNotifier) Notify(ctx context.Context, as ...*types.Alert) (boo
u := tmpl(dd.URL) u := tmpl(dd.URL)
if tmplErr != nil { if tmplErr != nil {
dd.log.Debug("failed to template DingDing message", "err", tmplErr.Error()) dd.log.Warn("failed to template DingDing message", "err", tmplErr.Error())
} }
body, err := json.Marshal(bodyMsg) body, err := json.Marshal(bodyMsg)

View File

@@ -86,7 +86,7 @@ func (d DiscordNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
} }
embed := simplejson.New() embed := simplejson.New()
embed.Set("title", tmpl(`{{ template "default.title" . }}`)) embed.Set("title", tmpl(DefaultMessageTitleEmbed))
embed.Set("footer", footer) embed.Set("footer", footer)
embed.Set("type", "rich") embed.Set("type", "rich")
@@ -100,7 +100,7 @@ func (d DiscordNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
u := tmpl(d.WebhookURL) u := tmpl(d.WebhookURL)
if tmplErr != nil { if tmplErr != nil {
d.log.Debug("failed to template Discord message", "err", tmplErr.Error()) d.log.Warn("failed to template Discord message", "err", tmplErr.Error())
} }
body, err := json.Marshal(bodyJSON) body, err := json.Marshal(bodyJSON)

View File

@@ -63,7 +63,7 @@ func (en *EmailNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
var tmplErr error var tmplErr error
tmpl, data := TmplText(ctx, en.tmpl, as, en.log, &tmplErr) tmpl, data := TmplText(ctx, en.tmpl, as, en.log, &tmplErr)
title := tmpl(`{{ template "default.title" . }}`) title := tmpl(DefaultMessageTitleEmbed)
alertPageURL := en.tmpl.ExternalURL.String() alertPageURL := en.tmpl.ExternalURL.String()
ruleURL := en.tmpl.ExternalURL.String() ruleURL := en.tmpl.ExternalURL.String()
@@ -100,7 +100,7 @@ func (en *EmailNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
} }
if tmplErr != nil { if tmplErr != nil {
en.log.Debug("failed to template email message", "err", tmplErr.Error()) en.log.Warn("failed to template email message", "err", tmplErr.Error())
} }
if err := bus.Dispatch(ctx, cmd); err != nil { if err := bus.Dispatch(ctx, cmd); err != nil {

View File

@@ -19,9 +19,10 @@ import (
// alert notifications to Google chat. // alert notifications to Google chat.
type GoogleChatNotifier struct { type GoogleChatNotifier struct {
*Base *Base
URL string URL string
log log.Logger log log.Logger
tmpl *template.Template tmpl *template.Template
content string
} }
func NewGoogleChatNotifier(model *NotificationChannelConfig, t *template.Template) (*GoogleChatNotifier, error) { func NewGoogleChatNotifier(model *NotificationChannelConfig, t *template.Template) (*GoogleChatNotifier, error) {
@@ -34,6 +35,8 @@ func NewGoogleChatNotifier(model *NotificationChannelConfig, t *template.Templat
return nil, receiverInitError{Cfg: *model, Reason: "could not find url property in settings"} return nil, receiverInitError{Cfg: *model, Reason: "could not find url property in settings"}
} }
content := model.Settings.Get("message").MustString(`{{ template "default.message" . }}`)
return &GoogleChatNotifier{ return &GoogleChatNotifier{
Base: NewBase(&models.AlertNotification{ Base: NewBase(&models.AlertNotification{
Uid: model.UID, Uid: model.UID,
@@ -42,9 +45,10 @@ func NewGoogleChatNotifier(model *NotificationChannelConfig, t *template.Templat
DisableResolveMessage: model.DisableResolveMessage, DisableResolveMessage: model.DisableResolveMessage,
Settings: model.Settings, Settings: model.Settings,
}), }),
URL: url, URL: url,
log: log.New("alerting.notifier.googlechat"), log: log.New("alerting.notifier.googlechat"),
tmpl: t, tmpl: t,
content: content,
}, nil }, nil
} }
@@ -57,7 +61,7 @@ func (gcn *GoogleChatNotifier) Notify(ctx context.Context, as ...*types.Alert) (
widgets := []widget{} widgets := []widget{}
if msg := tmpl(`{{ template "default.message" . }}`); msg != "" { if msg := tmpl(gcn.content); msg != "" {
// Add a text paragraph widget for the message if there is a message. // Add a text paragraph widget for the message if there is a message.
// Google Chat API doesn't accept an empty text property. // Google Chat API doesn't accept an empty text property.
widgets = append(widgets, textParagraphWidget{ widgets = append(widgets, textParagraphWidget{
@@ -67,6 +71,11 @@ func (gcn *GoogleChatNotifier) Notify(ctx context.Context, as ...*types.Alert) (
}) })
} }
if tmplErr != nil {
gcn.log.Warn("failed to template Google Chat message", "err", tmplErr.Error())
tmplErr = nil
}
ruleURL := joinUrlPath(gcn.tmpl.ExternalURL.String(), "/alerting/list", gcn.log) ruleURL := joinUrlPath(gcn.tmpl.ExternalURL.String(), "/alerting/list", gcn.log)
// Add a button widget (link to Grafana). // Add a button widget (link to Grafana).
widgets = append(widgets, buttonWidget{ widgets = append(widgets, buttonWidget{
@@ -93,12 +102,12 @@ func (gcn *GoogleChatNotifier) Notify(ctx context.Context, as ...*types.Alert) (
// Nest the required structs. // Nest the required structs.
res := &outerStruct{ res := &outerStruct{
PreviewText: tmpl(`{{ template "default.title" . }}`), PreviewText: tmpl(DefaultMessageTitleEmbed),
FallbackText: tmpl(`{{ template "default.title" . }}`), FallbackText: tmpl(DefaultMessageTitleEmbed),
Cards: []card{ Cards: []card{
{ {
Header: header{ Header: header{
Title: tmpl(`{{ template "default.title" . }}`), Title: tmpl(DefaultMessageTitleEmbed),
}, },
Sections: []section{ Sections: []section{
{ {
@@ -111,7 +120,7 @@ func (gcn *GoogleChatNotifier) Notify(ctx context.Context, as ...*types.Alert) (
u := tmpl(gcn.URL) u := tmpl(gcn.URL)
if tmplErr != nil { if tmplErr != nil {
gcn.log.Debug("failed to template GoogleChat message", "err", tmplErr.Error()) gcn.log.Warn("failed to template GoogleChat message", "err", tmplErr.Error())
} }
body, err := json.Marshal(res) body, err := json.Marshal(res)

View File

@@ -152,6 +152,114 @@ func TestGoogleChatNotifier(t *testing.T) {
name: "Error in initing", name: "Error in initing",
settings: `{}`, settings: `{}`,
expInitError: `failed to validate receiver "googlechat_testing" of type "googlechat": could not find url property in settings`, expInitError: `failed to validate receiver "googlechat_testing" of type "googlechat": could not find url property in settings`,
}, {
name: "Customized message",
settings: `{"url": "http://localhost", "message": "I'm a custom template and you have {{ len .Alerts.Firing }} firing alert."}`,
alerts: []*types.Alert{
{
Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh"},
},
},
},
expMsg: &outerStruct{
PreviewText: "[FIRING:1] (val1)",
FallbackText: "[FIRING:1] (val1)",
Cards: []card{
{
Header: header{
Title: "[FIRING:1] (val1)",
},
Sections: []section{
{
Widgets: []widget{
textParagraphWidget{
Text: text{
Text: "I'm a custom template and you have 1 firing alert.",
},
},
buttonWidget{
Buttons: []button{
{
TextButton: textButton{
Text: "OPEN IN GRAFANA",
OnClick: onClick{
OpenLink: openLink{
URL: "http://localhost/alerting/list",
},
},
},
},
},
},
textParagraphWidget{
Text: text{
// RFC822 only has the minute, hence it works in most cases.
Text: "Grafana v" + setting.BuildVersion + " | " + constNow.Format(time.RFC822),
},
},
},
},
},
},
},
},
expMsgError: nil,
}, {
name: "Invalid template",
settings: `{"url": "http://localhost", "message": "I'm a custom template {{ .NotAField }} bad template"}`,
alerts: []*types.Alert{
{
Alert: model.Alert{
Labels: model.LabelSet{"alertname": "alert1", "lbl1": "val1"},
Annotations: model.LabelSet{"ann1": "annv1", "__dashboardUid__": "abcd", "__panelId__": "efgh"},
},
},
},
expMsg: &outerStruct{
PreviewText: "[FIRING:1] (val1)",
FallbackText: "[FIRING:1] (val1)",
Cards: []card{
{
Header: header{
Title: "[FIRING:1] (val1)",
},
Sections: []section{
{
Widgets: []widget{
textParagraphWidget{
Text: text{
Text: "I'm a custom template ",
},
},
buttonWidget{
Buttons: []button{
{
TextButton: textButton{
Text: "OPEN IN GRAFANA",
OnClick: onClick{
OpenLink: openLink{
URL: "http://localhost/alerting/list",
},
},
},
},
},
},
textParagraphWidget{
Text: text{
// RFC822 only has the minute, hence it works in most cases.
Text: "Grafana v" + setting.BuildVersion + " | " + constNow.Format(time.RFC822),
},
},
},
},
},
},
},
},
expMsgError: nil,
}, },
} }

View File

@@ -72,7 +72,7 @@ func (kn *KafkaNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
bodyJSON := simplejson.New() bodyJSON := simplejson.New()
bodyJSON.Set("alert_state", state) bodyJSON.Set("alert_state", state)
bodyJSON.Set("description", tmpl(`{{ template "default.title" . }}`)) bodyJSON.Set("description", tmpl(DefaultMessageTitleEmbed))
bodyJSON.Set("client", "Grafana") bodyJSON.Set("client", "Grafana")
bodyJSON.Set("details", tmpl(`{{ template "default.message" . }}`)) bodyJSON.Set("details", tmpl(`{{ template "default.message" . }}`))
@@ -99,7 +99,7 @@ func (kn *KafkaNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
topicURL := strings.TrimRight(kn.Endpoint, "/") + "/topics/" + tmpl(kn.Topic) topicURL := strings.TrimRight(kn.Endpoint, "/") + "/topics/" + tmpl(kn.Topic)
if tmplErr != nil { if tmplErr != nil {
kn.log.Debug("failed to template Kafka message", "err", tmplErr.Error()) kn.log.Warn("failed to template Kafka message", "err", tmplErr.Error())
} }
cmd := &models.SendWebhookSync{ cmd := &models.SendWebhookSync{

View File

@@ -65,12 +65,12 @@ func (ln *LineNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, e
body := fmt.Sprintf( body := fmt.Sprintf(
"%s\n%s\n\n%s", "%s\n%s\n\n%s",
tmpl(`{{ template "default.title" . }}`), tmpl(DefaultMessageTitleEmbed),
ruleURL, ruleURL,
tmpl(`{{ template "default.message" . }}`), tmpl(`{{ template "default.message" . }}`),
) )
if tmplErr != nil { if tmplErr != nil {
ln.log.Debug("failed to template Line message", "err", tmplErr.Error()) ln.log.Warn("failed to template Line message", "err", tmplErr.Error())
} }
form := url.Values{} form := url.Values{}

View File

@@ -156,10 +156,10 @@ func (on *OpsgenieNotifier) buildOpsgenieMessage(ctx context.Context, alerts mod
var tmplErr error var tmplErr error
tmpl, data := TmplText(ctx, on.tmpl, as, on.log, &tmplErr) tmpl, data := TmplText(ctx, on.tmpl, as, on.log, &tmplErr)
title := tmpl(`{{ template "default.title" . }}`) title := tmpl(DefaultMessageTitleEmbed)
description := fmt.Sprintf( description := fmt.Sprintf(
"%s\n%s\n\n%s", "%s\n%s\n\n%s",
tmpl(`{{ template "default.title" . }}`), tmpl(DefaultMessageTitleEmbed),
ruleURL, ruleURL,
tmpl(`{{ template "default.message" . }}`), tmpl(`{{ template "default.message" . }}`),
) )
@@ -207,7 +207,7 @@ func (on *OpsgenieNotifier) buildOpsgenieMessage(ctx context.Context, alerts mod
apiURL = tmpl(on.APIUrl) apiURL = tmpl(on.APIUrl)
if tmplErr != nil { if tmplErr != nil {
on.log.Debug("failed to template Opsgenie message", "err", tmplErr.Error()) on.log.Warn("failed to template Opsgenie message", "err", tmplErr.Error())
} }
return bodyJSON, apiURL, nil return bodyJSON, apiURL, nil

View File

@@ -72,7 +72,7 @@ func NewPagerdutyNotifier(model *NotificationChannelConfig, t *template.Template
Class: model.Settings.Get("class").MustString("default"), Class: model.Settings.Get("class").MustString("default"),
Component: model.Settings.Get("component").MustString("Grafana"), Component: model.Settings.Get("component").MustString("Grafana"),
Group: model.Settings.Get("group").MustString("default"), Group: model.Settings.Get("group").MustString("default"),
Summary: model.Settings.Get("summary").MustString(`{{ template "default.title" . }}`), Summary: model.Settings.Get("summary").MustString(DefaultMessageTitleEmbed),
tmpl: t, tmpl: t,
log: log.New("alerting.notifier." + model.Name), log: log.New("alerting.notifier." + model.Name),
}, nil }, nil
@@ -145,7 +145,7 @@ func (pn *PagerdutyNotifier) buildPagerdutyMessage(ctx context.Context, alerts m
HRef: pn.tmpl.ExternalURL.String(), HRef: pn.tmpl.ExternalURL.String(),
Text: "External URL", Text: "External URL",
}}, }},
Description: tmpl(`{{ template "default.title" . }}`), // TODO: this can be configurable template. Description: tmpl(DefaultMessageTitleEmbed), // TODO: this can be configurable template.
Payload: pagerDutyPayload{ Payload: pagerDutyPayload{
Component: tmpl(pn.Component), Component: tmpl(pn.Component),
Summary: tmpl(pn.Summary), Summary: tmpl(pn.Summary),
@@ -167,7 +167,7 @@ func (pn *PagerdutyNotifier) buildPagerdutyMessage(ctx context.Context, alerts m
} }
if tmplErr != nil { if tmplErr != nil {
pn.log.Debug("failed to template PagerDuty message", "err", tmplErr.Error()) pn.log.Warn("failed to template PagerDuty message", "err", tmplErr.Error())
} }
return msg, eventType, nil return msg, eventType, nil

View File

@@ -195,7 +195,7 @@ func (pn *PushoverNotifier) genPushoverBody(ctx context.Context, as ...*types.Al
} }
// Add title // Add title
err = w.WriteField("title", tmpl(`{{ template "default.title" . }}`)) err = w.WriteField("title", tmpl(DefaultMessageTitleEmbed))
if err != nil { if err != nil {
return nil, b, err return nil, b, err
} }
@@ -218,7 +218,7 @@ func (pn *PushoverNotifier) genPushoverBody(ctx context.Context, as ...*types.Al
} }
if tmplErr != nil { if tmplErr != nil {
pn.log.Debug("failed to template pushover message", "err", tmplErr.Error()) pn.log.Warn("failed to template pushover message", "err", tmplErr.Error())
} }
// Mark as html message // Mark as html message

View File

@@ -128,7 +128,7 @@ func (sn *SensuGoNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool
} }
if tmplErr != nil { if tmplErr != nil {
sn.log.Debug("failed to template sensugo message", "err", tmplErr.Error()) sn.log.Warn("failed to template sensugo message", "err", tmplErr.Error())
} }
body, err := json.Marshal(bodyMsgType) body, err := json.Marshal(bodyMsgType)

View File

@@ -118,7 +118,7 @@ func NewSlackNotifier(model *NotificationChannelConfig, t *template.Template, fn
IconURL: model.Settings.Get("icon_url").MustString(), IconURL: model.Settings.Get("icon_url").MustString(),
Token: token, Token: token,
Text: model.Settings.Get("text").MustString(`{{ template "default.message" . }}`), Text: model.Settings.Get("text").MustString(`{{ template "default.message" . }}`),
Title: model.Settings.Get("title").MustString(`{{ template "default.title" . }}`), Title: model.Settings.Get("title").MustString(DefaultMessageTitleEmbed),
log: log.New("alerting.notifier.slack"), log: log.New("alerting.notifier.slack"),
tmpl: t, tmpl: t,
}, nil }, nil
@@ -269,7 +269,7 @@ func (sn *SlackNotifier) buildSlackMessage(ctx context.Context, as []*types.Aler
}, },
} }
if tmplErr != nil { if tmplErr != nil {
sn.log.Debug("failed to template Slack message", "err", tmplErr.Error()) sn.log.Warn("failed to template Slack message", "err", tmplErr.Error())
} }
mentionsBuilder := strings.Builder{} mentionsBuilder := strings.Builder{}

View File

@@ -56,7 +56,7 @@ func (tn *TeamsNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
ruleURL := joinUrlPath(tn.tmpl.ExternalURL.String(), "/alerting/list", tn.log) ruleURL := joinUrlPath(tn.tmpl.ExternalURL.String(), "/alerting/list", tn.log)
title := tmpl(`{{ template "default.title" . }}`) title := tmpl(DefaultMessageTitleEmbed)
body := map[string]interface{}{ body := map[string]interface{}{
"@type": "MessageCard", "@type": "MessageCard",
"@context": "http://schema.org/extensions", "@context": "http://schema.org/extensions",
@@ -88,7 +88,7 @@ func (tn *TeamsNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool,
u := tmpl(tn.URL) u := tmpl(tn.URL)
if tmplErr != nil { if tmplErr != nil {
tn.log.Debug("failed to template Teams message", "err", tmplErr.Error()) tn.log.Warn("failed to template Teams message", "err", tmplErr.Error())
} }
b, err := json.Marshal(&body) b, err := json.Marshal(&body)

View File

@@ -127,7 +127,7 @@ func (tn *TelegramNotifier) buildTelegramMessage(ctx context.Context, as []*type
message := tmpl(tn.Message) message := tmpl(tn.Message)
if tmplErr != nil { if tmplErr != nil {
tn.log.Debug("failed to template Telegram message", "err", tmplErr.Error()) tn.log.Warn("failed to template Telegram message", "err", tmplErr.Error())
} }
msg["text"] = message msg["text"] = message

View File

@@ -101,14 +101,14 @@ func (tn *ThreemaNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool
// Build message // Build message
message := fmt.Sprintf("%s%s\n\n*Message:*\n%s\n*URL:* %s\n", message := fmt.Sprintf("%s%s\n\n*Message:*\n%s\n*URL:* %s\n",
stateEmoji, stateEmoji,
tmpl(`{{ template "default.title" . }}`), tmpl(DefaultMessageTitleEmbed),
tmpl(`{{ template "default.message" . }}`), tmpl(`{{ template "default.message" . }}`),
path.Join(tn.tmpl.ExternalURL.String(), "/alerting/list"), path.Join(tn.tmpl.ExternalURL.String(), "/alerting/list"),
) )
data.Set("text", message) data.Set("text", message)
if tmplErr != nil { if tmplErr != nil {
tn.log.Debug("failed to template Threema message", "err", tmplErr.Error()) tn.log.Warn("failed to template Threema message", "err", tmplErr.Error())
} }
cmd := &models.SendWebhookSync{ cmd := &models.SendWebhookSync{

View File

@@ -87,7 +87,7 @@ func (vn *VictoropsNotifier) Notify(ctx context.Context, as ...*types.Alert) (bo
bodyJSON := simplejson.New() bodyJSON := simplejson.New()
bodyJSON.Set("message_type", messageType) bodyJSON.Set("message_type", messageType)
bodyJSON.Set("entity_id", groupKey.Hash()) bodyJSON.Set("entity_id", groupKey.Hash())
bodyJSON.Set("entity_display_name", tmpl(`{{ template "default.title" . }}`)) bodyJSON.Set("entity_display_name", tmpl(DefaultMessageTitleEmbed))
bodyJSON.Set("timestamp", time.Now().Unix()) bodyJSON.Set("timestamp", time.Now().Unix())
bodyJSON.Set("state_message", tmpl(`{{ template "default.message" . }}`)) bodyJSON.Set("state_message", tmpl(`{{ template "default.message" . }}`))
bodyJSON.Set("monitoring_tool", "Grafana v"+setting.BuildVersion) bodyJSON.Set("monitoring_tool", "Grafana v"+setting.BuildVersion)
@@ -97,7 +97,7 @@ func (vn *VictoropsNotifier) Notify(ctx context.Context, as ...*types.Alert) (bo
u := tmpl(vn.URL) u := tmpl(vn.URL)
if tmplErr != nil { if tmplErr != nil {
vn.log.Debug("failed to template VictorOps message", "err", tmplErr.Error()) vn.log.Warn("failed to template VictorOps message", "err", tmplErr.Error())
} }
b, err := bodyJSON.MarshalJSON() b, err := bodyJSON.MarshalJSON()

View File

@@ -92,7 +92,7 @@ func (wn *WebhookNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool
GroupKey: groupKey.String(), GroupKey: groupKey.String(),
TruncatedAlerts: numTruncated, TruncatedAlerts: numTruncated,
OrgID: wn.orgID, OrgID: wn.orgID,
Title: tmpl(`{{ template "default.title" . }}`), Title: tmpl(DefaultMessageTitleEmbed),
Message: tmpl(`{{ template "default.message" . }}`), Message: tmpl(`{{ template "default.message" . }}`),
} }
if types.Alerts(as...).Status() == model.AlertFiring { if types.Alerts(as...).Status() == model.AlertFiring {
@@ -102,7 +102,7 @@ func (wn *WebhookNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool
} }
if tmplErr != nil { if tmplErr != nil {
wn.log.Debug("failed to template webhook message", "err", tmplErr.Error()) wn.log.Warn("failed to template webhook message", "err", tmplErr.Error())
} }
body, err := json.Marshal(msg) body, err := json.Marshal(msg)

View File

@@ -56,7 +56,7 @@ func (w *WeComNotifier) Notify(ctx context.Context, as ...*types.Alert) (bool, e
"msgtype": "markdown", "msgtype": "markdown",
} }
content := fmt.Sprintf("# %s\n%s\n", content := fmt.Sprintf("# %s\n%s\n",
tmpl(`{{ template "default.title" . }}`), tmpl(DefaultMessageTitleEmbed),
tmpl(w.Message), tmpl(w.Message),
) )

View File

@@ -1487,6 +1487,22 @@ var expAvailableChannelJsonOutput = `
"required": true, "required": true,
"validationRule": "", "validationRule": "",
"secure": false "secure": false
},
{
"element": "textarea",
"inputType": "",
"label": "Message",
"description": "",
"placeholder": "{{ template \"default.message\" . }}",
"propertyName": "message",
"selectOptions": null,
"showWhen": {
"field": "",
"is": ""
},
"required": false,
"validationRule": "",
"secure": false
} }
] ]
}, },

View File

@@ -81,6 +81,19 @@ export const grafanaNotifiersMock: NotifierDTO[] = [
validationRule: '', validationRule: '',
secure: false, secure: false,
}, },
{
element: 'textarea',
inputType: '',
label: 'Message',
description: '',
placeholder: '{{ template "default.message" . }}',
propertyName: 'message',
selectOptions: null,
showWhen: { field: '', is: '' },
required: false,
validationRule: '',
secure: false,
},
], ],
}, },
{ {