diff --git a/app/vmalert/alerting.go b/app/vmalert/alerting.go
index 4aa77adea4..671750ecdf 100644
--- a/app/vmalert/alerting.go
+++ b/app/vmalert/alerting.go
@@ -415,7 +415,7 @@ func (ar *AlertingRule) AlertsAPI() []*APIAlert {
}
func (ar *AlertingRule) newAlertAPI(a notifier.Alert) *APIAlert {
- return &APIAlert{
+ aa := &APIAlert{
// encode as strings to avoid rounding
ID: fmt.Sprintf("%d", a.ID),
GroupID: fmt.Sprintf("%d", a.GroupID),
@@ -429,6 +429,10 @@ func (ar *AlertingRule) newAlertAPI(a notifier.Alert) *APIAlert {
ActiveAt: a.Start,
Value: strconv.FormatFloat(a.Value, 'f', -1, 32),
}
+ if alertURLGeneratorFn != nil {
+ aa.SourceLink = alertURLGeneratorFn(a)
+ }
+ return aa
}
const (
diff --git a/app/vmalert/main.go b/app/vmalert/main.go
index fa8ed3753a..7e9c109f3b 100644
--- a/app/vmalert/main.go
+++ b/app/vmalert/main.go
@@ -59,6 +59,8 @@ eg. 'explore?orgId=1&left=[\"now-1h\",\"now\",\"VictoriaMetrics\",{\"expr\": \"{
dryRun = flag.Bool("dryRun", false, "Whether to check only config files without running vmalert. The rules file are validated. The `-rule` flag must be specified.")
)
+var alertURLGeneratorFn notifier.AlertURLGenerator
+
func main() {
// Write flags and help message to stdout, since it is easier to grep or pipe.
flag.CommandLine.SetOutput(os.Stdout)
@@ -79,15 +81,22 @@ func main() {
}
return
}
+
+ eu, err := getExternalURL(*externalURL, *httpListenAddr, httpserver.IsTLS())
+ if err != nil {
+ logger.Fatalf("failed to init `external.url`: %s", err)
+ }
+ notifier.InitTemplateFunc(eu)
+ alertURLGeneratorFn, err = getAlertURLGenerator(eu, *externalAlertSource, *validateTemplates)
+ if err != nil {
+ logger.Fatalf("failed to init `external.alert.source`: %s", err)
+ }
+
if *replayFrom != "" || *replayTo != "" {
rw, err := remotewrite.Init(context.Background())
if err != nil {
logger.Fatalf("failed to init remoteWrite: %s", err)
}
- eu, err := getExternalURL(*externalURL, *httpListenAddr, httpserver.IsTLS())
- if err != nil {
- logger.Fatalf("failed to init `external.url`: %s", err)
- }
notifier.InitTemplateFunc(eu)
groupsCfg, err := config.Parse(*rulePath, *validateTemplates, *validateExpressions)
if err != nil {
@@ -148,20 +157,10 @@ func newManager(ctx context.Context) (*manager, error) {
if err != nil {
return nil, fmt.Errorf("failed to init datasource: %w", err)
}
- eu, err := getExternalURL(*externalURL, *httpListenAddr, httpserver.IsTLS())
- if err != nil {
- return nil, fmt.Errorf("failed to init `external.url`: %w", err)
- }
- notifier.InitTemplateFunc(eu)
- aug, err := getAlertURLGenerator(eu, *externalAlertSource, *validateTemplates)
- if err != nil {
- return nil, fmt.Errorf("failed to init `external.alert.source`: %w", err)
- }
- nts, err := notifier.Init(aug)
+ nts, err := notifier.Init(alertURLGeneratorFn)
if err != nil {
return nil, fmt.Errorf("failed to init notifier: %w", err)
}
-
manager := &manager{
groups: make(map[uint64]*Group),
querierBuilder: q,
diff --git a/app/vmalert/web.qtpl b/app/vmalert/web.qtpl
index 7c5c6b2b4e..b64640a450 100644
--- a/app/vmalert/web.qtpl
+++ b/app/vmalert/web.qtpl
@@ -155,7 +155,9 @@
sort.Strings(labelKeys)
%}
- alert: {%s defaultAR.Name %} ({%d len(alertsByRule[ruleID]) %})
+ alert: {%s defaultAR.Name %} ({%d len(alertsByRule[ruleID]) %})
+ | Source
+
expr:{%s defaultAR.Expression %}
@@ -270,6 +272,16 @@
{%s alert.GroupID %}
+
+
{%= tpl.Footer() %}
diff --git a/app/vmalert/web.qtpl.go b/app/vmalert/web.qtpl.go
index a15c6523f8..5b7a3bbb4d 100644
--- a/app/vmalert/web.qtpl.go
+++ b/app/vmalert/web.qtpl.go
@@ -573,11 +573,17 @@ func StreamListAlerts(qw422016 *qt422016.Writer, groupAlerts []GroupAlerts) {
//line app/vmalert/web.qtpl:158
qw422016.N().D(len(alertsByRule[ruleID]))
//line app/vmalert/web.qtpl:158
- qw422016.N().S(`)
+ qw422016.N().S(`)
+ | Source
+
expr:`)
-//line app/vmalert/web.qtpl:159
+//line app/vmalert/web.qtpl:161
qw422016.E().S(defaultAR.Expression)
-//line app/vmalert/web.qtpl:159
+//line app/vmalert/web.qtpl:161
qw422016.N().S(`
@@ -591,152 +597,152 @@ func StreamListAlerts(qw422016 *qt422016.Writer, groupAlerts []GroupAlerts) {
`)
-//line app/vmalert/web.qtpl:171
+//line app/vmalert/web.qtpl:173
for _, ar := range alertsByRule[ruleID] {
-//line app/vmalert/web.qtpl:171
+//line app/vmalert/web.qtpl:173
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:174
+//line app/vmalert/web.qtpl:176
for _, k := range labelKeys {
-//line app/vmalert/web.qtpl:174
+//line app/vmalert/web.qtpl:176
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:175
+//line app/vmalert/web.qtpl:177
qw422016.E().S(k)
-//line app/vmalert/web.qtpl:175
+//line app/vmalert/web.qtpl:177
qw422016.N().S(`=`)
-//line app/vmalert/web.qtpl:175
+//line app/vmalert/web.qtpl:177
qw422016.E().S(ar.Labels[k])
-//line app/vmalert/web.qtpl:175
+//line app/vmalert/web.qtpl:177
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:176
+//line app/vmalert/web.qtpl:178
}
-//line app/vmalert/web.qtpl:176
+//line app/vmalert/web.qtpl:178
qw422016.N().S(`
|
`)
-//line app/vmalert/web.qtpl:178
+//line app/vmalert/web.qtpl:180
qw422016.E().S(ar.State)
-//line app/vmalert/web.qtpl:178
+//line app/vmalert/web.qtpl:180
qw422016.N().S(` |
`)
-//line app/vmalert/web.qtpl:179
+//line app/vmalert/web.qtpl:181
qw422016.E().S(ar.ActiveAt.Format("2006-01-02T15:04:05Z07:00"))
-//line app/vmalert/web.qtpl:179
+//line app/vmalert/web.qtpl:181
qw422016.N().S(` |
`)
-//line app/vmalert/web.qtpl:180
+//line app/vmalert/web.qtpl:182
qw422016.E().S(ar.Value)
-//line app/vmalert/web.qtpl:180
+//line app/vmalert/web.qtpl:182
qw422016.N().S(` |
Details
|
`)
-//line app/vmalert/web.qtpl:185
+//line app/vmalert/web.qtpl:187
}
-//line app/vmalert/web.qtpl:185
+//line app/vmalert/web.qtpl:187
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:188
+//line app/vmalert/web.qtpl:190
}
-//line app/vmalert/web.qtpl:188
+//line app/vmalert/web.qtpl:190
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:191
+//line app/vmalert/web.qtpl:193
}
-//line app/vmalert/web.qtpl:191
+//line app/vmalert/web.qtpl:193
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:193
+//line app/vmalert/web.qtpl:195
} else {
-//line app/vmalert/web.qtpl:193
+//line app/vmalert/web.qtpl:195
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:197
+//line app/vmalert/web.qtpl:199
}
-//line app/vmalert/web.qtpl:197
+//line app/vmalert/web.qtpl:199
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:199
+//line app/vmalert/web.qtpl:201
tpl.StreamFooter(qw422016)
-//line app/vmalert/web.qtpl:199
+//line app/vmalert/web.qtpl:201
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:201
+//line app/vmalert/web.qtpl:203
}
-//line app/vmalert/web.qtpl:201
+//line app/vmalert/web.qtpl:203
func WriteListAlerts(qq422016 qtio422016.Writer, groupAlerts []GroupAlerts) {
-//line app/vmalert/web.qtpl:201
+//line app/vmalert/web.qtpl:203
qw422016 := qt422016.AcquireWriter(qq422016)
-//line app/vmalert/web.qtpl:201
+//line app/vmalert/web.qtpl:203
StreamListAlerts(qw422016, groupAlerts)
-//line app/vmalert/web.qtpl:201
+//line app/vmalert/web.qtpl:203
qt422016.ReleaseWriter(qw422016)
-//line app/vmalert/web.qtpl:201
+//line app/vmalert/web.qtpl:203
}
-//line app/vmalert/web.qtpl:201
+//line app/vmalert/web.qtpl:203
func ListAlerts(groupAlerts []GroupAlerts) string {
-//line app/vmalert/web.qtpl:201
+//line app/vmalert/web.qtpl:203
qb422016 := qt422016.AcquireByteBuffer()
-//line app/vmalert/web.qtpl:201
+//line app/vmalert/web.qtpl:203
WriteListAlerts(qb422016, groupAlerts)
-//line app/vmalert/web.qtpl:201
+//line app/vmalert/web.qtpl:203
qs422016 := string(qb422016.B)
-//line app/vmalert/web.qtpl:201
+//line app/vmalert/web.qtpl:203
qt422016.ReleaseByteBuffer(qb422016)
-//line app/vmalert/web.qtpl:201
+//line app/vmalert/web.qtpl:203
return qs422016
-//line app/vmalert/web.qtpl:201
+//line app/vmalert/web.qtpl:203
}
-//line app/vmalert/web.qtpl:203
+//line app/vmalert/web.qtpl:205
func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
-//line app/vmalert/web.qtpl:203
- qw422016.N().S(`
- `)
-//line app/vmalert/web.qtpl:204
- tpl.StreamHeader(qw422016, "", navItems)
-//line app/vmalert/web.qtpl:204
+//line app/vmalert/web.qtpl:205
qw422016.N().S(`
`)
//line app/vmalert/web.qtpl:206
+ tpl.StreamHeader(qw422016, "", navItems)
+//line app/vmalert/web.qtpl:206
+ qw422016.N().S(`
+ `)
+//line app/vmalert/web.qtpl:208
var labelKeys []string
for k := range alert.Labels {
labelKeys = append(labelKeys, k)
@@ -749,28 +755,28 @@ func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
}
sort.Strings(annotationKeys)
-//line app/vmalert/web.qtpl:217
+//line app/vmalert/web.qtpl:219
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:218
+//line app/vmalert/web.qtpl:220
qw422016.E().S(alert.Name)
-//line app/vmalert/web.qtpl:218
+//line app/vmalert/web.qtpl:220
qw422016.N().S(``)
-//line app/vmalert/web.qtpl:218
+//line app/vmalert/web.qtpl:220
qw422016.E().S(alert.State)
-//line app/vmalert/web.qtpl:218
+//line app/vmalert/web.qtpl:220
qw422016.N().S(`
@@ -779,9 +785,9 @@ func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
`)
-//line app/vmalert/web.qtpl:225
+//line app/vmalert/web.qtpl:227
qw422016.E().S(alert.ActiveAt.Format("2006-01-02T15:04:05Z07:00"))
-//line app/vmalert/web.qtpl:225
+//line app/vmalert/web.qtpl:227
qw422016.N().S(`
@@ -793,9 +799,9 @@ func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
`)
-//line app/vmalert/web.qtpl:235
+//line app/vmalert/web.qtpl:237
qw422016.E().S(alert.Expression)
-//line app/vmalert/web.qtpl:235
+//line app/vmalert/web.qtpl:237
qw422016.N().S(`
@@ -807,23 +813,23 @@ func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
`)
-//line app/vmalert/web.qtpl:245
+//line app/vmalert/web.qtpl:247
for _, k := range labelKeys {
-//line app/vmalert/web.qtpl:245
+//line app/vmalert/web.qtpl:247
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:246
+//line app/vmalert/web.qtpl:248
qw422016.E().S(k)
-//line app/vmalert/web.qtpl:246
+//line app/vmalert/web.qtpl:248
qw422016.N().S(`=`)
-//line app/vmalert/web.qtpl:246
+//line app/vmalert/web.qtpl:248
qw422016.E().S(alert.Labels[k])
-//line app/vmalert/web.qtpl:246
+//line app/vmalert/web.qtpl:248
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:247
+//line app/vmalert/web.qtpl:249
}
-//line app/vmalert/web.qtpl:247
+//line app/vmalert/web.qtpl:249
qw422016.N().S(`
@@ -835,24 +841,24 @@ func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
`)
-//line app/vmalert/web.qtpl:257
+//line app/vmalert/web.qtpl:259
for _, k := range annotationKeys {
-//line app/vmalert/web.qtpl:257
+//line app/vmalert/web.qtpl:259
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:258
+//line app/vmalert/web.qtpl:260
qw422016.E().S(k)
-//line app/vmalert/web.qtpl:258
+//line app/vmalert/web.qtpl:260
qw422016.N().S(`:
`)
-//line app/vmalert/web.qtpl:259
+//line app/vmalert/web.qtpl:261
qw422016.E().S(alert.Annotations[k])
-//line app/vmalert/web.qtpl:259
+//line app/vmalert/web.qtpl:261
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:260
+//line app/vmalert/web.qtpl:262
}
-//line app/vmalert/web.qtpl:260
+//line app/vmalert/web.qtpl:262
qw422016.N().S(`
@@ -864,49 +870,63 @@ func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
+
+
`)
-//line app/vmalert/web.qtpl:274
+//line app/vmalert/web.qtpl:286
tpl.StreamFooter(qw422016)
-//line app/vmalert/web.qtpl:274
+//line app/vmalert/web.qtpl:286
qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:276
+//line app/vmalert/web.qtpl:288
}
-//line app/vmalert/web.qtpl:276
+//line app/vmalert/web.qtpl:288
func WriteAlert(qq422016 qtio422016.Writer, alert *APIAlert) {
-//line app/vmalert/web.qtpl:276
+//line app/vmalert/web.qtpl:288
qw422016 := qt422016.AcquireWriter(qq422016)
-//line app/vmalert/web.qtpl:276
+//line app/vmalert/web.qtpl:288
StreamAlert(qw422016, alert)
-//line app/vmalert/web.qtpl:276
+//line app/vmalert/web.qtpl:288
qt422016.ReleaseWriter(qw422016)
-//line app/vmalert/web.qtpl:276
+//line app/vmalert/web.qtpl:288
}
-//line app/vmalert/web.qtpl:276
+//line app/vmalert/web.qtpl:288
func Alert(alert *APIAlert) string {
-//line app/vmalert/web.qtpl:276
+//line app/vmalert/web.qtpl:288
qb422016 := qt422016.AcquireByteBuffer()
-//line app/vmalert/web.qtpl:276
+//line app/vmalert/web.qtpl:288
WriteAlert(qb422016, alert)
-//line app/vmalert/web.qtpl:276
+//line app/vmalert/web.qtpl:288
qs422016 := string(qb422016.B)
-//line app/vmalert/web.qtpl:276
+//line app/vmalert/web.qtpl:288
qt422016.ReleaseByteBuffer(qb422016)
-//line app/vmalert/web.qtpl:276
+//line app/vmalert/web.qtpl:288
return qs422016
-//line app/vmalert/web.qtpl:276
+//line app/vmalert/web.qtpl:288
}
diff --git a/app/vmalert/web_types.go b/app/vmalert/web_types.go
index 5a4f253626..fa14d3cc14 100644
--- a/app/vmalert/web_types.go
+++ b/app/vmalert/web_types.go
@@ -17,6 +17,7 @@ type APIAlert struct {
Labels map[string]string `json:"labels"`
Annotations map[string]string `json:"annotations"`
ActiveAt time.Time `json:"activeAt"`
+ SourceLink string `json:"source"`
}
// APIGroup represents Group for WEB view