`)
-//line app/vmalert/tpl/header.qtpl:93
+//line app/vmalert/tpl/header.qtpl:94
for _, item := range items {
-//line app/vmalert/tpl/header.qtpl:93
+//line app/vmalert/tpl/header.qtpl:94
qw422016.N().S(`
-
`)
-//line app/vmalert/tpl/header.qtpl:96
+//line app/vmalert/tpl/header.qtpl:97
u, _ := url.Parse(item.Url)
-//line app/vmalert/tpl/header.qtpl:97
+//line app/vmalert/tpl/header.qtpl:98
qw422016.N().S(`
`)
-//line app/vmalert/tpl/header.qtpl:103
+//line app/vmalert/tpl/header.qtpl:104
}
-//line app/vmalert/tpl/header.qtpl:103
+//line app/vmalert/tpl/header.qtpl:104
qw422016.N().S(`
+ `)
+//line app/vmalert/tpl/header.qtpl:107
+ streamerrorIcon(qw422016, userErr)
+//line app/vmalert/tpl/header.qtpl:107
+ qw422016.N().S(`
`)
-//line app/vmalert/tpl/header.qtpl:107
+//line app/vmalert/tpl/header.qtpl:109
}
-//line app/vmalert/tpl/header.qtpl:107
-func writeprintNavItems(qq422016 qtio422016.Writer, r *http.Request, current string, items []NavItem) {
-//line app/vmalert/tpl/header.qtpl:107
+//line app/vmalert/tpl/header.qtpl:109
+func writeprintNavItems(qq422016 qtio422016.Writer, r *http.Request, current string, items []NavItem, userErr error) {
+//line app/vmalert/tpl/header.qtpl:109
qw422016 := qt422016.AcquireWriter(qq422016)
-//line app/vmalert/tpl/header.qtpl:107
- streamprintNavItems(qw422016, r, current, items)
-//line app/vmalert/tpl/header.qtpl:107
+//line app/vmalert/tpl/header.qtpl:109
+ streamprintNavItems(qw422016, r, current, items, userErr)
+//line app/vmalert/tpl/header.qtpl:109
qt422016.ReleaseWriter(qw422016)
-//line app/vmalert/tpl/header.qtpl:107
+//line app/vmalert/tpl/header.qtpl:109
}
-//line app/vmalert/tpl/header.qtpl:107
-func printNavItems(r *http.Request, current string, items []NavItem) string {
-//line app/vmalert/tpl/header.qtpl:107
+//line app/vmalert/tpl/header.qtpl:109
+func printNavItems(r *http.Request, current string, items []NavItem, userErr error) string {
+//line app/vmalert/tpl/header.qtpl:109
qb422016 := qt422016.AcquireByteBuffer()
-//line app/vmalert/tpl/header.qtpl:107
- writeprintNavItems(qb422016, r, current, items)
-//line app/vmalert/tpl/header.qtpl:107
+//line app/vmalert/tpl/header.qtpl:109
+ writeprintNavItems(qb422016, r, current, items, userErr)
+//line app/vmalert/tpl/header.qtpl:109
qs422016 := string(qb422016.B)
-//line app/vmalert/tpl/header.qtpl:107
+//line app/vmalert/tpl/header.qtpl:109
qt422016.ReleaseByteBuffer(qb422016)
-//line app/vmalert/tpl/header.qtpl:107
+//line app/vmalert/tpl/header.qtpl:109
return qs422016
-//line app/vmalert/tpl/header.qtpl:107
+//line app/vmalert/tpl/header.qtpl:109
+}
+
+//line app/vmalert/tpl/header.qtpl:111
+func streamerrorIcon(qw422016 *qt422016.Writer, err error) {
+//line app/vmalert/tpl/header.qtpl:111
+ qw422016.N().S(`
+`)
+//line app/vmalert/tpl/header.qtpl:112
+ if err != nil {
+//line app/vmalert/tpl/header.qtpl:112
+ qw422016.N().S(`
+
+`)
+//line app/vmalert/tpl/header.qtpl:122
+ }
+//line app/vmalert/tpl/header.qtpl:122
+ qw422016.N().S(`
+`)
+//line app/vmalert/tpl/header.qtpl:123
+}
+
+//line app/vmalert/tpl/header.qtpl:123
+func writeerrorIcon(qq422016 qtio422016.Writer, err error) {
+//line app/vmalert/tpl/header.qtpl:123
+ qw422016 := qt422016.AcquireWriter(qq422016)
+//line app/vmalert/tpl/header.qtpl:123
+ streamerrorIcon(qw422016, err)
+//line app/vmalert/tpl/header.qtpl:123
+ qt422016.ReleaseWriter(qw422016)
+//line app/vmalert/tpl/header.qtpl:123
+}
+
+//line app/vmalert/tpl/header.qtpl:123
+func errorIcon(err error) string {
+//line app/vmalert/tpl/header.qtpl:123
+ qb422016 := qt422016.AcquireByteBuffer()
+//line app/vmalert/tpl/header.qtpl:123
+ writeerrorIcon(qb422016, err)
+//line app/vmalert/tpl/header.qtpl:123
+ qs422016 := string(qb422016.B)
+//line app/vmalert/tpl/header.qtpl:123
+ qt422016.ReleaseByteBuffer(qb422016)
+//line app/vmalert/tpl/header.qtpl:123
+ return qs422016
+//line app/vmalert/tpl/header.qtpl:123
+}
+
+//line app/vmalert/tpl/header.qtpl:125
+func streamerrorBody(qw422016 *qt422016.Writer, err error) {
+//line app/vmalert/tpl/header.qtpl:125
+ qw422016.N().S(`
+`)
+//line app/vmalert/tpl/header.qtpl:126
+ if err != nil {
+//line app/vmalert/tpl/header.qtpl:126
+ qw422016.N().S(`
+
+
+ `)
+//line app/vmalert/tpl/header.qtpl:129
+ qw422016.E().S(err.Error())
+//line app/vmalert/tpl/header.qtpl:129
+ qw422016.N().S(`
+
+
+`)
+//line app/vmalert/tpl/header.qtpl:132
+ }
+//line app/vmalert/tpl/header.qtpl:132
+ qw422016.N().S(`
+`)
+//line app/vmalert/tpl/header.qtpl:133
+}
+
+//line app/vmalert/tpl/header.qtpl:133
+func writeerrorBody(qq422016 qtio422016.Writer, err error) {
+//line app/vmalert/tpl/header.qtpl:133
+ qw422016 := qt422016.AcquireWriter(qq422016)
+//line app/vmalert/tpl/header.qtpl:133
+ streamerrorBody(qw422016, err)
+//line app/vmalert/tpl/header.qtpl:133
+ qt422016.ReleaseWriter(qw422016)
+//line app/vmalert/tpl/header.qtpl:133
+}
+
+//line app/vmalert/tpl/header.qtpl:133
+func errorBody(err error) string {
+//line app/vmalert/tpl/header.qtpl:133
+ qb422016 := qt422016.AcquireByteBuffer()
+//line app/vmalert/tpl/header.qtpl:133
+ writeerrorBody(qb422016, err)
+//line app/vmalert/tpl/header.qtpl:133
+ qs422016 := string(qb422016.B)
+//line app/vmalert/tpl/header.qtpl:133
+ qt422016.ReleaseByteBuffer(qb422016)
+//line app/vmalert/tpl/header.qtpl:133
+ return qs422016
+//line app/vmalert/tpl/header.qtpl:133
}
diff --git a/app/vmalert/web.qtpl b/app/vmalert/web.qtpl
index 55bc0e3e58..1b412c7c30 100644
--- a/app/vmalert/web.qtpl
+++ b/app/vmalert/web.qtpl
@@ -12,7 +12,7 @@
{% func Welcome(r *http.Request) %}
- {%= tpl.Header(r, navItems, "vmalert") %}
+ {%= tpl.Header(r, navItems, "vmalert", configError()) %}
API:
{% for _, p := range apiLinks %}
@@ -40,7 +40,7 @@ btn-primary
{% func ListGroups(r *http.Request, originGroups []APIGroup) %}
{%code prefix := utils.Prefix(r.URL.Path) %}
- {%= tpl.Header(r, navItems, "Groups") %}
+ {%= tpl.Header(r, navItems, "Groups", configError()) %}
{%code
filter := r.URL.Query().Get("filter")
rOk := make(map[string]int)
@@ -164,7 +164,7 @@ btn-primary
{% func ListAlerts(r *http.Request, groupAlerts []GroupAlerts) %}
{%code prefix := utils.Prefix(r.URL.Path) %}
- {%= tpl.Header(r, navItems, "Alerts") %}
+ {%= tpl.Header(r, navItems, "Alerts", configError()) %}
{% if len(groupAlerts) > 0 %}
Collapse All
Expand All
@@ -250,7 +250,7 @@ btn-primary
{% endfunc %}
{% func ListTargets(r *http.Request, targets map[notifier.TargetType][]notifier.Target) %}
- {%= tpl.Header(r, navItems, "Notifiers") %}
+ {%= tpl.Header(r, navItems, "Notifiers", configError()) %}
{% if len(targets) > 0 %}
Collapse All
Expand All
@@ -307,7 +307,7 @@ btn-primary
{% func Alert(r *http.Request, alert *APIAlert) %}
{%code prefix := utils.Prefix(r.URL.Path) %}
- {%= tpl.Header(r, navItems, "") %}
+ {%= tpl.Header(r, navItems, "", configError()) %}
{%code
var labelKeys []string
for k := range alert.Labels {
@@ -394,7 +394,7 @@ btn-primary
{% func RuleDetails(r *http.Request, rule APIRule) %}
{%code prefix := utils.Prefix(r.URL.Path) %}
- {%= tpl.Header(r, navItems, "") %}
+ {%= tpl.Header(r, navItems, "", configError()) %}
{%code
var labelKeys []string
for k := range rule.Labels {
@@ -578,4 +578,4 @@ btn-primary
func isNoMatch (r APIRule) bool {
return r.LastSamples == 0 && r.LastSeriesFetched != nil && *r.LastSeriesFetched == 0
}
-%}
\ No newline at end of file
+%}
diff --git a/app/vmalert/web.qtpl.go b/app/vmalert/web.qtpl.go
index 029bc80642..97999a74f8 100644
--- a/app/vmalert/web.qtpl.go
+++ b/app/vmalert/web.qtpl.go
@@ -34,7 +34,7 @@ func StreamWelcome(qw422016 *qt422016.Writer, r *http.Request) {
qw422016.N().S(`
`)
//line app/vmalert/web.qtpl:15
- tpl.StreamHeader(qw422016, r, navItems, "vmalert")
+ tpl.StreamHeader(qw422016, r, navItems, "vmalert", configError())
//line app/vmalert/web.qtpl:15
qw422016.N().S(`
@@ -207,7 +207,7 @@ func StreamListGroups(qw422016 *qt422016.Writer, r *http.Request, originGroups [
qw422016.N().S(`
`)
//line app/vmalert/web.qtpl:43
- tpl.StreamHeader(qw422016, r, navItems, "Groups")
+ tpl.StreamHeader(qw422016, r, navItems, "Groups", configError())
//line app/vmalert/web.qtpl:43
qw422016.N().S(`
`)
@@ -619,7 +619,7 @@ func StreamListAlerts(qw422016 *qt422016.Writer, r *http.Request, groupAlerts []
qw422016.N().S(`
`)
//line app/vmalert/web.qtpl:167
- tpl.StreamHeader(qw422016, r, navItems, "Alerts")
+ tpl.StreamHeader(qw422016, r, navItems, "Alerts", configError())
//line app/vmalert/web.qtpl:167
qw422016.N().S(`
`)
@@ -885,7 +885,7 @@ func StreamListTargets(qw422016 *qt422016.Writer, r *http.Request, targets map[n
qw422016.N().S(`
`)
//line app/vmalert/web.qtpl:253
- tpl.StreamHeader(qw422016, r, navItems, "Notifiers")
+ tpl.StreamHeader(qw422016, r, navItems, "Notifiers", configError())
//line app/vmalert/web.qtpl:253
qw422016.N().S(`
`)
@@ -1065,7 +1065,7 @@ func StreamAlert(qw422016 *qt422016.Writer, r *http.Request, alert *APIAlert) {
qw422016.N().S(`
`)
//line app/vmalert/web.qtpl:310
- tpl.StreamHeader(qw422016, r, navItems, "")
+ tpl.StreamHeader(qw422016, r, navItems, "", configError())
//line app/vmalert/web.qtpl:310
qw422016.N().S(`
`)
@@ -1274,7 +1274,7 @@ func StreamRuleDetails(qw422016 *qt422016.Writer, r *http.Request, rule APIRule)
qw422016.N().S(`
`)
//line app/vmalert/web.qtpl:397
- tpl.StreamHeader(qw422016, r, navItems, "")
+ tpl.StreamHeader(qw422016, r, navItems, "", configError())
//line app/vmalert/web.qtpl:397
qw422016.N().S(`
`)
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 5cad4bcd39..e3f7ccc09a 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -32,6 +32,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
* FEATURE: accept timestamps in milliseconds at `start`, `end` and `time` query args in [Prometheus querying API](https://docs.victoriametrics.com/#prometheus-querying-api-usage). See [these docs](https://docs.victoriametrics.com/#timestamp-formats) and [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4459).
* FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): update retry policy for pushing data to `-remoteWrite.url`. By default, vmalert will make multiple retry attempts with exponential delay. The total time spent during retry attempts shouldn't exceed `-remoteWrite.retryMaxTime` (default is 30s). When retry time is exceeded vmalert drops the data dedicated for `-remoteWrite.url`. Before, vmalert dropped data after 5 retry attempts with 1s delay between attempts (not configurable). See `-remoteWrite.retryMinInterval` and `-remoteWrite.retryMaxTime` cmd-line flags.
* FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): expose `vmalert_remotewrite_send_duration_seconds_total` counter, which can be used for determining high saturation of every connection to remote storage with an alerting query `sum(rate(vmalert_remotewrite_send_duration_seconds_total[5m])) by(job, instance) > 0.9 * max(vmalert_remotewrite_concurrency) by(job, instance)`. This query triggers when a connection is saturated by more than 90%. This usually means that `-remoteWrite.concurrency` command-line flag must be increased in order to increase the number of concurrent writings into remote endpoint. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4516).
+* FEATUTE: [vmalert](https://docs.victoriametrics.com/vmalert.html): display the error message received during unsuccessful config reload in vmalert's UI. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4076) for details.
* FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth.html): expose `vmauth_user_request_duration_seconds` and `vmauth_unauthorized_user_request_duration_seconds` summary metrics for measuring requests latency per user.
* FEATURE: [vmbackup](https://docs.victoriametrics.com/vmbackup.html): show backup progress percentage in log during backup uploading. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4460).
* FEATURE: [vmrestore](https://docs.victoriametrics.com/vmrestore.html): show restoring progress percentage in log during backup downloading. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4460).