mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
vmalert: add initial UI implementation (#1602)
New UI pages: / - welcome page with API handlers list; /groups - list of all rules per group; /alerts - list of all active alerts; /groupID/alertID/status - status of the active alert;
This commit is contained in:
parent
83356af4c7
commit
bb26aa9b1f
13 changed files with 1638 additions and 18 deletions
|
@ -1,6 +1,9 @@
|
|||
# All these commands must run from repository root.
|
||||
|
||||
vmalert:
|
||||
templates-gen: install-qtc
|
||||
qtc -dir=./app/vmalert
|
||||
|
||||
vmalert: templates-gen
|
||||
APP_NAME=vmalert $(MAKE) app-local
|
||||
|
||||
vmalert-race:
|
||||
|
|
|
@ -24,7 +24,6 @@ may fail;
|
|||
* by default, rules execution is sequential within one group, but persisting of execution results to remote
|
||||
storage is asynchronous. Hence, user shouldn't rely on recording rules chaining when result of previous
|
||||
recording rule is reused in next one;
|
||||
* `vmalert` has no UI, just an API for getting groups and rules statuses.
|
||||
|
||||
## QuickStart
|
||||
|
||||
|
@ -243,6 +242,7 @@ tags at [Docker Hub](https://hub.docker.com/r/victoriametrics/vmalert/tags).
|
|||
### WEB
|
||||
|
||||
`vmalert` runs a web-server (`-httpListenAddr`) for serving metrics and alerts endpoints:
|
||||
* `http://<vmalert-addr>` - UI;
|
||||
* `http://<vmalert-addr>/api/v1/groups` - list of all loaded groups and rules;
|
||||
* `http://<vmalert-addr>/api/v1/alerts` - list of all active alerts;
|
||||
* `http://<vmalert-addr>/api/v1/<groupID>/<alertID>/status" ` - get alert status by ID.
|
||||
|
|
|
@ -419,6 +419,7 @@ func (ar *AlertingRule) newAlertAPI(a notifier.Alert) *APIAlert {
|
|||
// encode as strings to avoid rounding
|
||||
ID: fmt.Sprintf("%d", a.ID),
|
||||
GroupID: fmt.Sprintf("%d", a.GroupID),
|
||||
RuleID: fmt.Sprintf("%d", ar.RuleID),
|
||||
|
||||
Name: a.Name,
|
||||
Expression: ar.Expr,
|
||||
|
@ -426,7 +427,7 @@ func (ar *AlertingRule) newAlertAPI(a notifier.Alert) *APIAlert {
|
|||
Annotations: a.Annotations,
|
||||
State: a.State.String(),
|
||||
ActiveAt: a.Start,
|
||||
Value: strconv.FormatFloat(a.Value, 'e', -1, 64),
|
||||
Value: strconv.FormatFloat(a.Value, 'f', -1, 32),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
36
app/vmalert/tpl/footer.qtpl
Normal file
36
app/vmalert/tpl/footer.qtpl
Normal file
|
@ -0,0 +1,36 @@
|
|||
{% func Footer() %}
|
||||
</main>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
function expandAll() {
|
||||
$('.collapse').addClass('show');
|
||||
}
|
||||
function collapseAll() {
|
||||
$('.collapse').removeClass('show');
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// prevent collapse logic on link click
|
||||
$(".group-heading a").click(function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
$(".group-heading").click(function(e) {
|
||||
let target = $(this).attr('data-bs-target');
|
||||
let el = $('#'+target);
|
||||
new bootstrap.Collapse(el, {
|
||||
toggle: true
|
||||
});
|
||||
});
|
||||
|
||||
var hash = window.location.hash.substr(1);
|
||||
let group = $('#'+hash);
|
||||
if (group.length > 0) {
|
||||
group.click();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{% endfunc %}
|
90
app/vmalert/tpl/footer.qtpl.go
Normal file
90
app/vmalert/tpl/footer.qtpl.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
// Code generated by qtc from "footer.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line app/vmalert/tpl/footer.qtpl:1
|
||||
package tpl
|
||||
|
||||
//line app/vmalert/tpl/footer.qtpl:1
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line app/vmalert/tpl/footer.qtpl:1
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line app/vmalert/tpl/footer.qtpl:1
|
||||
func StreamFooter(qw422016 *qt422016.Writer) {
|
||||
//line app/vmalert/tpl/footer.qtpl:1
|
||||
qw422016.N().S(`
|
||||
</main>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
function expandAll() {
|
||||
$('.collapse').addClass('show');
|
||||
}
|
||||
function collapseAll() {
|
||||
$('.collapse').removeClass('show');
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// prevent collapse logic on link click
|
||||
$(".group-heading a").click(function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
$(".group-heading").click(function(e) {
|
||||
let target = $(this).attr('data-bs-target');
|
||||
let el = $('#'+target);
|
||||
new bootstrap.Collapse(el, {
|
||||
toggle: true
|
||||
});
|
||||
});
|
||||
|
||||
var hash = window.location.hash.substr(1);
|
||||
let group = $('#'+hash);
|
||||
if (group.length > 0) {
|
||||
group.click();
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
//line app/vmalert/tpl/footer.qtpl:40
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/footer.qtpl:40
|
||||
func WriteFooter(qq422016 qtio422016.Writer) {
|
||||
//line app/vmalert/tpl/footer.qtpl:40
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmalert/tpl/footer.qtpl:40
|
||||
StreamFooter(qw422016)
|
||||
//line app/vmalert/tpl/footer.qtpl:40
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmalert/tpl/footer.qtpl:40
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/footer.qtpl:40
|
||||
func Footer() string {
|
||||
//line app/vmalert/tpl/footer.qtpl:40
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmalert/tpl/footer.qtpl:40
|
||||
WriteFooter(qb422016)
|
||||
//line app/vmalert/tpl/footer.qtpl:40
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmalert/tpl/footer.qtpl:40
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmalert/tpl/footer.qtpl:40
|
||||
return qs422016
|
||||
//line app/vmalert/tpl/footer.qtpl:40
|
||||
}
|
43
app/vmalert/tpl/header.qtpl
Normal file
43
app/vmalert/tpl/header.qtpl
Normal file
|
@ -0,0 +1,43 @@
|
|||
{% func Header(title string, pages []NavItem) %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>vmalert{% if title != "" %} - {%s title %}{% endif %}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<style>
|
||||
body{
|
||||
min-height: 75rem;
|
||||
padding-top: 4.5rem;
|
||||
}
|
||||
pre {
|
||||
overflow: scroll;
|
||||
max-width: 600px;
|
||||
min-height: 30px;
|
||||
}
|
||||
.group-heading {
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
position: relative;
|
||||
}
|
||||
.group-heading .anchor {
|
||||
position:absolute;
|
||||
top:-60px;
|
||||
}
|
||||
.group-heading span {
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.group-heading:hover {
|
||||
background-color: #f8f9fa!important;
|
||||
}
|
||||
.table .error-cell{
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{%= PrintNavItems(title, pages) %}
|
||||
<main class="px-2">
|
||||
{% endfunc %}
|
107
app/vmalert/tpl/header.qtpl.go
Normal file
107
app/vmalert/tpl/header.qtpl.go
Normal file
|
@ -0,0 +1,107 @@
|
|||
// Code generated by qtc from "header.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:1
|
||||
package tpl
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:1
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:1
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:1
|
||||
func StreamHeader(qw422016 *qt422016.Writer, title string, pages []NavItem) {
|
||||
//line app/vmalert/tpl/header.qtpl:1
|
||||
qw422016.N().S(`
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>vmalert`)
|
||||
//line app/vmalert/tpl/header.qtpl:5
|
||||
if title != "" {
|
||||
//line app/vmalert/tpl/header.qtpl:5
|
||||
qw422016.N().S(` - `)
|
||||
//line app/vmalert/tpl/header.qtpl:5
|
||||
qw422016.E().S(title)
|
||||
//line app/vmalert/tpl/header.qtpl:5
|
||||
}
|
||||
//line app/vmalert/tpl/header.qtpl:5
|
||||
qw422016.N().S(`</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<style>
|
||||
body{
|
||||
min-height: 75rem;
|
||||
padding-top: 4.5rem;
|
||||
}
|
||||
pre {
|
||||
overflow: scroll;
|
||||
max-width: 600px;
|
||||
min-height: 30px;
|
||||
}
|
||||
.group-heading {
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
margin-top: 5px;
|
||||
position: relative;
|
||||
}
|
||||
.group-heading .anchor {
|
||||
position:absolute;
|
||||
top:-60px;
|
||||
}
|
||||
.group-heading span {
|
||||
float: right;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.group-heading:hover {
|
||||
background-color: #f8f9fa!important;
|
||||
}
|
||||
.table .error-cell{
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
`)
|
||||
//line app/vmalert/tpl/header.qtpl:41
|
||||
StreamPrintNavItems(qw422016, title, pages)
|
||||
//line app/vmalert/tpl/header.qtpl:41
|
||||
qw422016.N().S(`
|
||||
<main class="px-2">
|
||||
`)
|
||||
//line app/vmalert/tpl/header.qtpl:43
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:43
|
||||
func WriteHeader(qq422016 qtio422016.Writer, title string, pages []NavItem) {
|
||||
//line app/vmalert/tpl/header.qtpl:43
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmalert/tpl/header.qtpl:43
|
||||
StreamHeader(qw422016, title, pages)
|
||||
//line app/vmalert/tpl/header.qtpl:43
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmalert/tpl/header.qtpl:43
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/header.qtpl:43
|
||||
func Header(title string, pages []NavItem) string {
|
||||
//line app/vmalert/tpl/header.qtpl:43
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmalert/tpl/header.qtpl:43
|
||||
WriteHeader(qb422016, title, pages)
|
||||
//line app/vmalert/tpl/header.qtpl:43
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmalert/tpl/header.qtpl:43
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmalert/tpl/header.qtpl:43
|
||||
return qs422016
|
||||
//line app/vmalert/tpl/header.qtpl:43
|
||||
}
|
25
app/vmalert/tpl/nav.qtpl
Normal file
25
app/vmalert/tpl/nav.qtpl
Normal file
|
@ -0,0 +1,25 @@
|
|||
{% code
|
||||
type NavItem struct {
|
||||
Name string
|
||||
Url string
|
||||
}
|
||||
%}
|
||||
|
||||
{% func PrintNavItems(current string, items []NavItem) %}
|
||||
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
||||
<div class="container-fluid">
|
||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-md-0">
|
||||
{% for _, item := range items %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link{% if current == item.Name %} active{% endif %}" href="{%s item.Url %}">
|
||||
{%s item.Name %}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
{% endfunc %}
|
||||
|
||||
|
96
app/vmalert/tpl/nav.qtpl.go
Normal file
96
app/vmalert/tpl/nav.qtpl.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Code generated by qtc from "nav.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line app/vmalert/tpl/nav.qtpl:1
|
||||
package tpl
|
||||
|
||||
//line app/vmalert/tpl/nav.qtpl:1
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line app/vmalert/tpl/nav.qtpl:1
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line app/vmalert/tpl/nav.qtpl:2
|
||||
type NavItem struct {
|
||||
Name string
|
||||
Url string
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/nav.qtpl:8
|
||||
func StreamPrintNavItems(qw422016 *qt422016.Writer, current string, items []NavItem) {
|
||||
//line app/vmalert/tpl/nav.qtpl:8
|
||||
qw422016.N().S(`
|
||||
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
|
||||
<div class="container-fluid">
|
||||
<div class="collapse navbar-collapse" id="navbarCollapse">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-md-0">
|
||||
`)
|
||||
//line app/vmalert/tpl/nav.qtpl:13
|
||||
for _, item := range items {
|
||||
//line app/vmalert/tpl/nav.qtpl:13
|
||||
qw422016.N().S(`
|
||||
<li class="nav-item">
|
||||
<a class="nav-link`)
|
||||
//line app/vmalert/tpl/nav.qtpl:15
|
||||
if current == item.Name {
|
||||
//line app/vmalert/tpl/nav.qtpl:15
|
||||
qw422016.N().S(` active`)
|
||||
//line app/vmalert/tpl/nav.qtpl:15
|
||||
}
|
||||
//line app/vmalert/tpl/nav.qtpl:15
|
||||
qw422016.N().S(`" href="`)
|
||||
//line app/vmalert/tpl/nav.qtpl:15
|
||||
qw422016.E().S(item.Url)
|
||||
//line app/vmalert/tpl/nav.qtpl:15
|
||||
qw422016.N().S(`">
|
||||
`)
|
||||
//line app/vmalert/tpl/nav.qtpl:16
|
||||
qw422016.E().S(item.Name)
|
||||
//line app/vmalert/tpl/nav.qtpl:16
|
||||
qw422016.N().S(`
|
||||
</a>
|
||||
</li>
|
||||
`)
|
||||
//line app/vmalert/tpl/nav.qtpl:19
|
||||
}
|
||||
//line app/vmalert/tpl/nav.qtpl:19
|
||||
qw422016.N().S(`
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
`)
|
||||
//line app/vmalert/tpl/nav.qtpl:23
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/nav.qtpl:23
|
||||
func WritePrintNavItems(qq422016 qtio422016.Writer, current string, items []NavItem) {
|
||||
//line app/vmalert/tpl/nav.qtpl:23
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmalert/tpl/nav.qtpl:23
|
||||
StreamPrintNavItems(qw422016, current, items)
|
||||
//line app/vmalert/tpl/nav.qtpl:23
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmalert/tpl/nav.qtpl:23
|
||||
}
|
||||
|
||||
//line app/vmalert/tpl/nav.qtpl:23
|
||||
func PrintNavItems(current string, items []NavItem) string {
|
||||
//line app/vmalert/tpl/nav.qtpl:23
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmalert/tpl/nav.qtpl:23
|
||||
WritePrintNavItems(qb422016, current, items)
|
||||
//line app/vmalert/tpl/nav.qtpl:23
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmalert/tpl/nav.qtpl:23
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmalert/tpl/nav.qtpl:23
|
||||
return qs422016
|
||||
//line app/vmalert/tpl/nav.qtpl:23
|
||||
}
|
|
@ -23,7 +23,7 @@ func (rh *requestHandler) handler(w http.ResponseWriter, r *http.Request) bool {
|
|||
if r.Method != "GET" {
|
||||
return false
|
||||
}
|
||||
httpserver.WriteAPIHelp(w, [][2]string{
|
||||
WriteWelcome(w, [][2]string{
|
||||
{"/api/v1/groups", "list all loaded groups and rules"},
|
||||
{"/api/v1/alerts", "list all active alerts"},
|
||||
{"/api/v1/groupID/alertID/status", "get alert status by ID"},
|
||||
|
@ -31,6 +31,12 @@ func (rh *requestHandler) handler(w http.ResponseWriter, r *http.Request) bool {
|
|||
{"/-/reload", "reload configuration"},
|
||||
})
|
||||
return true
|
||||
case "/alerts":
|
||||
WriteListAlerts(w, rh.groupAlerts())
|
||||
return true
|
||||
case "/groups":
|
||||
WriteListGroups(w, rh.groups())
|
||||
return true
|
||||
case "/api/v1/groups":
|
||||
data, err := rh.listGroups()
|
||||
if err != nil {
|
||||
|
@ -58,14 +64,26 @@ func (rh *requestHandler) handler(w http.ResponseWriter, r *http.Request) bool {
|
|||
if !strings.HasSuffix(r.URL.Path, "/status") {
|
||||
return false
|
||||
}
|
||||
// /api/v1/<groupName>/<alertID>/status
|
||||
data, err := rh.alert(r.URL.Path)
|
||||
alert, err := rh.alertByPath(strings.TrimPrefix(r.URL.Path, "/api/v1/"))
|
||||
if err != nil {
|
||||
httpserver.Errorf(w, r, "%s", err)
|
||||
return true
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.Write(data)
|
||||
|
||||
// /api/v1/<groupID>/<alertID>/status
|
||||
if strings.HasPrefix(r.URL.Path, "/api/v1/") {
|
||||
data, err := json.Marshal(alert)
|
||||
if err != nil {
|
||||
httpserver.Errorf(w, r, "failed to marshal alert: %s", err)
|
||||
return true
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.Write(data)
|
||||
return true
|
||||
}
|
||||
|
||||
// <groupID>/<alertID>/status
|
||||
WriteAlert(w, alert)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -77,20 +95,25 @@ type listGroupsResponse struct {
|
|||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
func (rh *requestHandler) listGroups() ([]byte, error) {
|
||||
func (rh *requestHandler) groups() []APIGroup {
|
||||
rh.m.groupsMu.RLock()
|
||||
defer rh.m.groupsMu.RUnlock()
|
||||
|
||||
lr := listGroupsResponse{Status: "success"}
|
||||
var groups []APIGroup
|
||||
for _, g := range rh.m.groups {
|
||||
lr.Data.Groups = append(lr.Data.Groups, g.toAPI())
|
||||
groups = append(groups, g.toAPI())
|
||||
}
|
||||
|
||||
// sort list of alerts for deterministic output
|
||||
sort.Slice(lr.Data.Groups, func(i, j int) bool {
|
||||
return lr.Data.Groups[i].Name < lr.Data.Groups[j].Name
|
||||
sort.Slice(groups, func(i, j int) bool {
|
||||
return groups[i].Name < groups[j].Name
|
||||
})
|
||||
|
||||
return groups
|
||||
}
|
||||
func (rh *requestHandler) listGroups() ([]byte, error) {
|
||||
lr := listGroupsResponse{Status: "success"}
|
||||
lr.Data.Groups = rh.groups()
|
||||
b, err := json.Marshal(lr)
|
||||
if err != nil {
|
||||
return nil, &httpserver.ErrorWithStatusCode{
|
||||
|
@ -108,6 +131,30 @@ type listAlertsResponse struct {
|
|||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
func (rh *requestHandler) groupAlerts() []GroupAlerts {
|
||||
rh.m.groupsMu.RLock()
|
||||
defer rh.m.groupsMu.RUnlock()
|
||||
|
||||
var groupAlerts []GroupAlerts
|
||||
for _, g := range rh.m.groups {
|
||||
var alerts []*APIAlert
|
||||
for _, r := range g.Rules {
|
||||
a, ok := r.(*AlertingRule)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
alerts = append(alerts, a.AlertsAPI()...)
|
||||
}
|
||||
if len(alerts) > 0 {
|
||||
groupAlerts = append(groupAlerts, GroupAlerts{
|
||||
Group: g.toAPI(),
|
||||
Alerts: alerts,
|
||||
})
|
||||
}
|
||||
}
|
||||
return groupAlerts
|
||||
}
|
||||
|
||||
func (rh *requestHandler) listAlerts() ([]byte, error) {
|
||||
rh.m.groupsMu.RLock()
|
||||
defer rh.m.groupsMu.RUnlock()
|
||||
|
@ -138,18 +185,17 @@ func (rh *requestHandler) listAlerts() ([]byte, error) {
|
|||
return b, nil
|
||||
}
|
||||
|
||||
func (rh *requestHandler) alert(path string) ([]byte, error) {
|
||||
func (rh *requestHandler) alertByPath(path string) (*APIAlert, error) {
|
||||
rh.m.groupsMu.RLock()
|
||||
defer rh.m.groupsMu.RUnlock()
|
||||
|
||||
parts := strings.SplitN(strings.TrimPrefix(path, "/api/v1/"), "/", 3)
|
||||
parts := strings.SplitN(strings.TrimLeft(path, "/"), "/", 3)
|
||||
if len(parts) != 3 {
|
||||
return nil, &httpserver.ErrorWithStatusCode{
|
||||
Err: fmt.Errorf(`path %q cointains /status suffix but doesn't match pattern "/group/alert/status"`, path),
|
||||
Err: fmt.Errorf(`path %q cointains /status suffix but doesn't match pattern "/groupID/alertID/status"`, path),
|
||||
StatusCode: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
groupID, err := uint64FromPath(parts[0])
|
||||
if err != nil {
|
||||
return nil, badRequest(fmt.Errorf(`cannot parse groupID: %w`, err))
|
||||
|
@ -162,7 +208,7 @@ func (rh *requestHandler) alert(path string) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, errResponse(err, http.StatusNotFound)
|
||||
}
|
||||
return json.Marshal(resp)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func uint64FromPath(path string) (uint64, error) {
|
||||
|
|
278
app/vmalert/web.qtpl
Normal file
278
app/vmalert/web.qtpl
Normal file
|
@ -0,0 +1,278 @@
|
|||
{% package main %}
|
||||
|
||||
{% import (
|
||||
"time"
|
||||
"sort"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/tpl"
|
||||
) %}
|
||||
|
||||
|
||||
{% code
|
||||
var navItems = []tpl.NavItem{
|
||||
{Name: "vmalert", Url: "/"},
|
||||
{Name: "Groups", Url: "/groups"},
|
||||
{Name: "Alerts", Url: "/alerts"},
|
||||
{Name: "Docs", Url: "https://docs.victoriametrics.com/vmalert.html"},
|
||||
}
|
||||
%}
|
||||
|
||||
{% func Welcome(pathList [][2]string) %}
|
||||
{%= tpl.Header("vmalert", navItems) %}
|
||||
<p>
|
||||
API:<br>
|
||||
{% for _, p := range pathList %}
|
||||
{%code
|
||||
p, doc := p[0], p[1]
|
||||
%}
|
||||
<a href="{%s p %}">{%s p %}</a> - {%s doc %}<br/>
|
||||
{% endfor %}
|
||||
</p>
|
||||
{%= tpl.Footer() %}
|
||||
{% endfunc %}
|
||||
|
||||
{% func ListGroups(groups []APIGroup) %}
|
||||
{%= tpl.Header("Groups", navItems) %}
|
||||
{% if len(groups) > 0 %}
|
||||
{%code
|
||||
rOk := make(map[string]int)
|
||||
rNotOk := make(map[string]int)
|
||||
for _, g := range groups {
|
||||
for _, r := range g.AlertingRules{
|
||||
if r.LastError != "" {
|
||||
rNotOk[g.Name]++
|
||||
} else {
|
||||
rOk[g.Name]++
|
||||
}
|
||||
}
|
||||
for _, r := range g.RecordingRules{
|
||||
if r.LastError != "" {
|
||||
rNotOk[g.Name]++
|
||||
} else {
|
||||
rOk[g.Name]++
|
||||
}
|
||||
}
|
||||
}
|
||||
%}
|
||||
<a class="btn btn-primary" role="button" onclick="collapseAll()">Collapse All</a>
|
||||
<a class="btn btn-primary" role="button" onclick="expandAll()">Expand All</a>
|
||||
{% for _, g := range groups %}
|
||||
<div class="group-heading{% if rNotOk[g.Name] > 0 %} alert-danger{% endif %}" data-bs-target="rules-{%s g.ID %}">
|
||||
<span class="anchor" id="group-{%s g.ID %}"></span>
|
||||
<a href="#group-{%s g.ID %}">{%s g.Name %}{% if g.Type != "prometheus" %} ({%s g.Type %}){% endif %} (every {%s g.Interval %})</a>
|
||||
{% if rNotOk[g.Name] > 0 %}<span class="badge bg-danger" title="Number of rules withs status Error">{%d rNotOk[g.Name] %}</span> {% endif %}
|
||||
<span class="badge bg-success" title="Number of rules withs status Ok">{%d rOk[g.Name] %}</span>
|
||||
<p class="fs-6 fw-lighter">{%s g.File %}</p>
|
||||
</div>
|
||||
<div class="collapse" id="rules-{%s g.ID %}">
|
||||
<table class="table table-striped table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Rule</th>
|
||||
<th scope="col" title="Shows if rule's execution ended with error">Error</th>
|
||||
<th scope="col" title="How many samples were produced by the rule">Samples</th>
|
||||
<th scope="col" title="How many seconds ago rule was executed">Updated</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for _, ar := range g.AlertingRules %}
|
||||
<tr{% if ar.LastError != "" %} class="alert-danger"{% endif %}>
|
||||
<td>
|
||||
<b>alert:</b> {%s ar.Name %} (for: {%v ar.For %})<br>
|
||||
<code><pre>{%s ar.Expression %}</pre></code><br>
|
||||
{% if len(ar.Labels) > 0 %} <b>Labels:</b>{% endif %}
|
||||
{% for k, v := range ar.Labels %}
|
||||
<span class="ms-1 badge bg-primary">{%s k %}={%s v %}</span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td><div class="error-cell">{%s ar.LastError %}</div></td>
|
||||
<td>{%d ar.LastSamples %}</td>
|
||||
<td>{%f.3 time.Since(ar.LastExec).Seconds() %}s ago</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% for _, rr := range g.RecordingRules %}
|
||||
<tr>
|
||||
<td>
|
||||
<b>record:</b> {%s rr.Name %}<br>
|
||||
<code><pre>{%s rr.Expression %}</pre></code>
|
||||
{% if len(rr.Labels) > 0 %} <b>Labels:</b>{% endif %}
|
||||
{% for k, v := range rr.Labels %}
|
||||
<span class="ms-1 badge bg-primary">{%s k %}={%s v %}</span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td><div class="error-cell">{%s rr.LastError %}</div></td>
|
||||
<td>{%d rr.LastSamples %}</td>
|
||||
<td>{%f.3 time.Since(rr.LastExec).Seconds() %}s ago</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% else %}
|
||||
<div>
|
||||
<p>No items...</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{%= tpl.Footer() %}
|
||||
|
||||
{% endfunc %}
|
||||
|
||||
|
||||
{% func ListAlerts(groupAlerts []GroupAlerts) %}
|
||||
{%= tpl.Header("Alerts", navItems) %}
|
||||
{% if len(groupAlerts) > 0 %}
|
||||
<a class="btn btn-primary" role="button" onclick="collapseAll()">Collapse All</a>
|
||||
<a class="btn btn-primary" role="button" onclick="expandAll()">Expand All</a>
|
||||
{% for _, ga := range groupAlerts %}
|
||||
{%code g := ga.Group %}
|
||||
<div class="group-heading alert-danger" data-bs-target="rules-{%s g.ID %}">
|
||||
<span class="anchor" id="group-{%s g.ID %}"></span>
|
||||
<a href="#group-{%s g.ID %}">{%s g.Name %}{% if g.Type != "prometheus" %} ({%s g.Type %}){% endif %}</a>
|
||||
<span class="badge bg-danger" title="Number of active alerts">{%d len(ga.Alerts) %}</span>
|
||||
<br>
|
||||
<p class="fs-6 fw-lighter">{%s g.File %}</p>
|
||||
</div>
|
||||
{%code
|
||||
var keys []string
|
||||
alertsByRule := make(map[string][]*APIAlert)
|
||||
for _, alert := range ga.Alerts {
|
||||
if len(alertsByRule[alert.RuleID]) < 1 {
|
||||
keys = append(keys, alert.RuleID)
|
||||
}
|
||||
alertsByRule[alert.RuleID] = append(alertsByRule[alert.RuleID], alert)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
%}
|
||||
<div class="collapse" id="rules-{%s g.ID %}">
|
||||
{% for _, ruleID := range keys %}
|
||||
{%code
|
||||
defaultAR := alertsByRule[ruleID][0]
|
||||
var labelKeys []string
|
||||
for k := range defaultAR.Labels {
|
||||
labelKeys = append(labelKeys, k)
|
||||
}
|
||||
sort.Strings(labelKeys)
|
||||
%}
|
||||
<br>
|
||||
<b>alert:</b> {%s defaultAR.Name %} ({%d len(alertsByRule[ruleID]) %})<br>
|
||||
<b>expr:</b><code><pre>{%s defaultAR.Expression %}</pre></code>
|
||||
<table class="table table-striped table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Labels</th>
|
||||
<th scope="col">State</th>
|
||||
<th scope="col">Active at</th>
|
||||
<th scope="col">Value</th>
|
||||
<th scope="col">Link</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for _, ar := range alertsByRule[ruleID] %}
|
||||
<tr>
|
||||
<td>
|
||||
{% for _, k := range labelKeys %}
|
||||
<span class="ms-1 badge bg-primary">{%s k %}={%s ar.Labels[k] %}</span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td><span class="badge {% if ar.State=="firing" %}bg-danger{% else %} bg-warning text-dark{% endif %}">{%s ar.State %}</span></td>
|
||||
<td>{%s ar.ActiveAt.Format("2006-01-02T15:04:05Z07:00") %}</td>
|
||||
<td>{%s ar.Value %}</td>
|
||||
<td>
|
||||
<a href="/{%s g.ID %}/{%s ar.ID %}/status">Details</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<br>
|
||||
{% endfor %}
|
||||
|
||||
{% else %}
|
||||
<div>
|
||||
<p>No items...</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{%= tpl.Footer() %}
|
||||
|
||||
{% endfunc %}
|
||||
|
||||
{% func Alert(alert *APIAlert) %}
|
||||
{%= tpl.Header("", navItems) %}
|
||||
{%code
|
||||
var labelKeys []string
|
||||
for k := range alert.Labels {
|
||||
labelKeys = append(labelKeys, k)
|
||||
}
|
||||
sort.Strings(labelKeys)
|
||||
|
||||
var annotationKeys []string
|
||||
for k := range alert.Annotations {
|
||||
annotationKeys = append(annotationKeys, k)
|
||||
}
|
||||
sort.Strings(annotationKeys)
|
||||
%}
|
||||
<div class="display-6 pb-3 mb-3">{%s alert.Name %}<span class="ms-2 badge {% if alert.State=="firing" %}bg-danger{% else %} bg-warning text-dark{% endif %}">{%s alert.State %}</span></div>
|
||||
<div class="container border-bottom p-2">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
Active at
|
||||
</div>
|
||||
<div class="col">
|
||||
{%s alert.ActiveAt.Format("2006-01-02T15:04:05Z07:00") %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container border-bottom p-2">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
Expr
|
||||
</div>
|
||||
<div class="col">
|
||||
<code><pre>{%s alert.Expression %}</pre></code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container border-bottom p-2">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
Labels
|
||||
</div>
|
||||
<div class="col">
|
||||
{% for _, k := range labelKeys %}
|
||||
<span class="m-1 badge bg-primary">{%s k %}={%s alert.Labels[k] %}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container border-bottom p-2">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
Annotations
|
||||
</div>
|
||||
<div class="col">
|
||||
{% for _, k := range annotationKeys %}
|
||||
<b>{%s k %}:</b><br>
|
||||
<p>{%s alert.Annotations[k] %}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container border-bottom p-2">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
Group
|
||||
</div>
|
||||
<div class="col">
|
||||
<a target="_blank" href="/groups#group-{%s alert.GroupID %}">{%s alert.GroupID %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{%= tpl.Footer() %}
|
||||
|
||||
{% endfunc %}
|
889
app/vmalert/web.qtpl.go
Normal file
889
app/vmalert/web.qtpl.go
Normal file
|
@ -0,0 +1,889 @@
|
|||
// Code generated by qtc from "web.qtpl". DO NOT EDIT.
|
||||
// See https://github.com/valyala/quicktemplate for details.
|
||||
|
||||
//line app/vmalert/web.qtpl:1
|
||||
package main
|
||||
|
||||
//line app/vmalert/web.qtpl:3
|
||||
import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/tpl"
|
||||
)
|
||||
|
||||
//line app/vmalert/web.qtpl:11
|
||||
import (
|
||||
qtio422016 "io"
|
||||
|
||||
qt422016 "github.com/valyala/quicktemplate"
|
||||
)
|
||||
|
||||
//line app/vmalert/web.qtpl:11
|
||||
var (
|
||||
_ = qtio422016.Copy
|
||||
_ = qt422016.AcquireByteBuffer
|
||||
)
|
||||
|
||||
//line app/vmalert/web.qtpl:12
|
||||
var navItems = []tpl.NavItem{
|
||||
{Name: "vmalert", Url: "/"},
|
||||
{Name: "Groups", Url: "/groups"},
|
||||
{Name: "Alerts", Url: "/alerts"},
|
||||
{Name: "Docs", Url: "https://docs.victoriametrics.com/vmalert.html"},
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:20
|
||||
func StreamWelcome(qw422016 *qt422016.Writer, pathList [][2]string) {
|
||||
//line app/vmalert/web.qtpl:20
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:21
|
||||
tpl.StreamHeader(qw422016, "vmalert", navItems)
|
||||
//line app/vmalert/web.qtpl:21
|
||||
qw422016.N().S(`
|
||||
<p>
|
||||
API:<br>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:24
|
||||
for _, p := range pathList {
|
||||
//line app/vmalert/web.qtpl:24
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:26
|
||||
p, doc := p[0], p[1]
|
||||
|
||||
//line app/vmalert/web.qtpl:27
|
||||
qw422016.N().S(`
|
||||
<a href="`)
|
||||
//line app/vmalert/web.qtpl:28
|
||||
qw422016.E().S(p)
|
||||
//line app/vmalert/web.qtpl:28
|
||||
qw422016.N().S(`">`)
|
||||
//line app/vmalert/web.qtpl:28
|
||||
qw422016.E().S(p)
|
||||
//line app/vmalert/web.qtpl:28
|
||||
qw422016.N().S(`</a> - `)
|
||||
//line app/vmalert/web.qtpl:28
|
||||
qw422016.E().S(doc)
|
||||
//line app/vmalert/web.qtpl:28
|
||||
qw422016.N().S(`<br/>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:29
|
||||
}
|
||||
//line app/vmalert/web.qtpl:29
|
||||
qw422016.N().S(`
|
||||
</p>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:31
|
||||
tpl.StreamFooter(qw422016)
|
||||
//line app/vmalert/web.qtpl:31
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:32
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:32
|
||||
func WriteWelcome(qq422016 qtio422016.Writer, pathList [][2]string) {
|
||||
//line app/vmalert/web.qtpl:32
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmalert/web.qtpl:32
|
||||
StreamWelcome(qw422016, pathList)
|
||||
//line app/vmalert/web.qtpl:32
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmalert/web.qtpl:32
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:32
|
||||
func Welcome(pathList [][2]string) string {
|
||||
//line app/vmalert/web.qtpl:32
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmalert/web.qtpl:32
|
||||
WriteWelcome(qb422016, pathList)
|
||||
//line app/vmalert/web.qtpl:32
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmalert/web.qtpl:32
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmalert/web.qtpl:32
|
||||
return qs422016
|
||||
//line app/vmalert/web.qtpl:32
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:34
|
||||
func StreamListGroups(qw422016 *qt422016.Writer, groups []APIGroup) {
|
||||
//line app/vmalert/web.qtpl:34
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:35
|
||||
tpl.StreamHeader(qw422016, "Groups", navItems)
|
||||
//line app/vmalert/web.qtpl:35
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:36
|
||||
if len(groups) > 0 {
|
||||
//line app/vmalert/web.qtpl:36
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:38
|
||||
rOk := make(map[string]int)
|
||||
rNotOk := make(map[string]int)
|
||||
for _, g := range groups {
|
||||
for _, r := range g.AlertingRules {
|
||||
if r.LastError != "" {
|
||||
rNotOk[g.Name]++
|
||||
} else {
|
||||
rOk[g.Name]++
|
||||
}
|
||||
}
|
||||
for _, r := range g.RecordingRules {
|
||||
if r.LastError != "" {
|
||||
rNotOk[g.Name]++
|
||||
} else {
|
||||
rOk[g.Name]++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:56
|
||||
qw422016.N().S(`
|
||||
<a class="btn btn-primary" role="button" onclick="collapseAll()">Collapse All</a>
|
||||
<a class="btn btn-primary" role="button" onclick="expandAll()">Expand All</a>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:59
|
||||
for _, g := range groups {
|
||||
//line app/vmalert/web.qtpl:59
|
||||
qw422016.N().S(`
|
||||
<div class="group-heading`)
|
||||
//line app/vmalert/web.qtpl:60
|
||||
if rNotOk[g.Name] > 0 {
|
||||
//line app/vmalert/web.qtpl:60
|
||||
qw422016.N().S(` alert-danger`)
|
||||
//line app/vmalert/web.qtpl:60
|
||||
}
|
||||
//line app/vmalert/web.qtpl:60
|
||||
qw422016.N().S(`" data-bs-target="rules-`)
|
||||
//line app/vmalert/web.qtpl:60
|
||||
qw422016.E().S(g.ID)
|
||||
//line app/vmalert/web.qtpl:60
|
||||
qw422016.N().S(`">
|
||||
<span class="anchor" id="group-`)
|
||||
//line app/vmalert/web.qtpl:61
|
||||
qw422016.E().S(g.ID)
|
||||
//line app/vmalert/web.qtpl:61
|
||||
qw422016.N().S(`"></span>
|
||||
<a href="#group-`)
|
||||
//line app/vmalert/web.qtpl:62
|
||||
qw422016.E().S(g.ID)
|
||||
//line app/vmalert/web.qtpl:62
|
||||
qw422016.N().S(`">`)
|
||||
//line app/vmalert/web.qtpl:62
|
||||
qw422016.E().S(g.Name)
|
||||
//line app/vmalert/web.qtpl:62
|
||||
if g.Type != "prometheus" {
|
||||
//line app/vmalert/web.qtpl:62
|
||||
qw422016.N().S(` (`)
|
||||
//line app/vmalert/web.qtpl:62
|
||||
qw422016.E().S(g.Type)
|
||||
//line app/vmalert/web.qtpl:62
|
||||
qw422016.N().S(`)`)
|
||||
//line app/vmalert/web.qtpl:62
|
||||
}
|
||||
//line app/vmalert/web.qtpl:62
|
||||
qw422016.N().S(` (every `)
|
||||
//line app/vmalert/web.qtpl:62
|
||||
qw422016.E().S(g.Interval)
|
||||
//line app/vmalert/web.qtpl:62
|
||||
qw422016.N().S(`)</a>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:63
|
||||
if rNotOk[g.Name] > 0 {
|
||||
//line app/vmalert/web.qtpl:63
|
||||
qw422016.N().S(`<span class="badge bg-danger" title="Number of rules withs status Error">`)
|
||||
//line app/vmalert/web.qtpl:63
|
||||
qw422016.N().D(rNotOk[g.Name])
|
||||
//line app/vmalert/web.qtpl:63
|
||||
qw422016.N().S(`</span> `)
|
||||
//line app/vmalert/web.qtpl:63
|
||||
}
|
||||
//line app/vmalert/web.qtpl:63
|
||||
qw422016.N().S(`
|
||||
<span class="badge bg-success" title="Number of rules withs status Ok">`)
|
||||
//line app/vmalert/web.qtpl:64
|
||||
qw422016.N().D(rOk[g.Name])
|
||||
//line app/vmalert/web.qtpl:64
|
||||
qw422016.N().S(`</span>
|
||||
<p class="fs-6 fw-lighter">`)
|
||||
//line app/vmalert/web.qtpl:65
|
||||
qw422016.E().S(g.File)
|
||||
//line app/vmalert/web.qtpl:65
|
||||
qw422016.N().S(`</p>
|
||||
</div>
|
||||
<div class="collapse" id="rules-`)
|
||||
//line app/vmalert/web.qtpl:67
|
||||
qw422016.E().S(g.ID)
|
||||
//line app/vmalert/web.qtpl:67
|
||||
qw422016.N().S(`">
|
||||
<table class="table table-striped table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Rule</th>
|
||||
<th scope="col" title="Shows if rule's execution ended with error">Error</th>
|
||||
<th scope="col" title="How many samples were produced by the rule">Samples</th>
|
||||
<th scope="col" title="How many seconds ago rule was executed">Updated</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:78
|
||||
for _, ar := range g.AlertingRules {
|
||||
//line app/vmalert/web.qtpl:78
|
||||
qw422016.N().S(`
|
||||
<tr`)
|
||||
//line app/vmalert/web.qtpl:79
|
||||
if ar.LastError != "" {
|
||||
//line app/vmalert/web.qtpl:79
|
||||
qw422016.N().S(` class="alert-danger"`)
|
||||
//line app/vmalert/web.qtpl:79
|
||||
}
|
||||
//line app/vmalert/web.qtpl:79
|
||||
qw422016.N().S(`>
|
||||
<td>
|
||||
<b>alert:</b> `)
|
||||
//line app/vmalert/web.qtpl:81
|
||||
qw422016.E().S(ar.Name)
|
||||
//line app/vmalert/web.qtpl:81
|
||||
qw422016.N().S(` (for: `)
|
||||
//line app/vmalert/web.qtpl:81
|
||||
qw422016.E().V(ar.For)
|
||||
//line app/vmalert/web.qtpl:81
|
||||
qw422016.N().S(`)<br>
|
||||
<code><pre>`)
|
||||
//line app/vmalert/web.qtpl:82
|
||||
qw422016.E().S(ar.Expression)
|
||||
//line app/vmalert/web.qtpl:82
|
||||
qw422016.N().S(`</pre></code><br>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:83
|
||||
if len(ar.Labels) > 0 {
|
||||
//line app/vmalert/web.qtpl:83
|
||||
qw422016.N().S(` <b>Labels:</b>`)
|
||||
//line app/vmalert/web.qtpl:83
|
||||
}
|
||||
//line app/vmalert/web.qtpl:83
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:84
|
||||
for k, v := range ar.Labels {
|
||||
//line app/vmalert/web.qtpl:84
|
||||
qw422016.N().S(`
|
||||
<span class="ms-1 badge bg-primary">`)
|
||||
//line app/vmalert/web.qtpl:85
|
||||
qw422016.E().S(k)
|
||||
//line app/vmalert/web.qtpl:85
|
||||
qw422016.N().S(`=`)
|
||||
//line app/vmalert/web.qtpl:85
|
||||
qw422016.E().S(v)
|
||||
//line app/vmalert/web.qtpl:85
|
||||
qw422016.N().S(`</span>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:86
|
||||
}
|
||||
//line app/vmalert/web.qtpl:86
|
||||
qw422016.N().S(`
|
||||
</td>
|
||||
<td><div class="error-cell">`)
|
||||
//line app/vmalert/web.qtpl:88
|
||||
qw422016.E().S(ar.LastError)
|
||||
//line app/vmalert/web.qtpl:88
|
||||
qw422016.N().S(`</div></td>
|
||||
<td>`)
|
||||
//line app/vmalert/web.qtpl:89
|
||||
qw422016.N().D(ar.LastSamples)
|
||||
//line app/vmalert/web.qtpl:89
|
||||
qw422016.N().S(`</td>
|
||||
<td>`)
|
||||
//line app/vmalert/web.qtpl:90
|
||||
qw422016.N().FPrec(time.Since(ar.LastExec).Seconds(), 3)
|
||||
//line app/vmalert/web.qtpl:90
|
||||
qw422016.N().S(`s ago</td>
|
||||
</tr>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:92
|
||||
}
|
||||
//line app/vmalert/web.qtpl:92
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:93
|
||||
for _, rr := range g.RecordingRules {
|
||||
//line app/vmalert/web.qtpl:93
|
||||
qw422016.N().S(`
|
||||
<tr>
|
||||
<td>
|
||||
<b>record:</b> `)
|
||||
//line app/vmalert/web.qtpl:96
|
||||
qw422016.E().S(rr.Name)
|
||||
//line app/vmalert/web.qtpl:96
|
||||
qw422016.N().S(`<br>
|
||||
<code><pre>`)
|
||||
//line app/vmalert/web.qtpl:97
|
||||
qw422016.E().S(rr.Expression)
|
||||
//line app/vmalert/web.qtpl:97
|
||||
qw422016.N().S(`</pre></code>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:98
|
||||
if len(rr.Labels) > 0 {
|
||||
//line app/vmalert/web.qtpl:98
|
||||
qw422016.N().S(` <b>Labels:</b>`)
|
||||
//line app/vmalert/web.qtpl:98
|
||||
}
|
||||
//line app/vmalert/web.qtpl:98
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:99
|
||||
for k, v := range rr.Labels {
|
||||
//line app/vmalert/web.qtpl:99
|
||||
qw422016.N().S(`
|
||||
<span class="ms-1 badge bg-primary">`)
|
||||
//line app/vmalert/web.qtpl:100
|
||||
qw422016.E().S(k)
|
||||
//line app/vmalert/web.qtpl:100
|
||||
qw422016.N().S(`=`)
|
||||
//line app/vmalert/web.qtpl:100
|
||||
qw422016.E().S(v)
|
||||
//line app/vmalert/web.qtpl:100
|
||||
qw422016.N().S(`</span>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:101
|
||||
}
|
||||
//line app/vmalert/web.qtpl:101
|
||||
qw422016.N().S(`
|
||||
</td>
|
||||
<td><div class="error-cell">`)
|
||||
//line app/vmalert/web.qtpl:103
|
||||
qw422016.E().S(rr.LastError)
|
||||
//line app/vmalert/web.qtpl:103
|
||||
qw422016.N().S(`</div></td>
|
||||
<td>`)
|
||||
//line app/vmalert/web.qtpl:104
|
||||
qw422016.N().D(rr.LastSamples)
|
||||
//line app/vmalert/web.qtpl:104
|
||||
qw422016.N().S(`</td>
|
||||
<td>`)
|
||||
//line app/vmalert/web.qtpl:105
|
||||
qw422016.N().FPrec(time.Since(rr.LastExec).Seconds(), 3)
|
||||
//line app/vmalert/web.qtpl:105
|
||||
qw422016.N().S(`s ago</td>
|
||||
</tr>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:107
|
||||
}
|
||||
//line app/vmalert/web.qtpl:107
|
||||
qw422016.N().S(`
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:111
|
||||
}
|
||||
//line app/vmalert/web.qtpl:111
|
||||
qw422016.N().S(`
|
||||
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:113
|
||||
} else {
|
||||
//line app/vmalert/web.qtpl:113
|
||||
qw422016.N().S(`
|
||||
<div>
|
||||
<p>No items...</p>
|
||||
</div>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:117
|
||||
}
|
||||
//line app/vmalert/web.qtpl:117
|
||||
qw422016.N().S(`
|
||||
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:119
|
||||
tpl.StreamFooter(qw422016)
|
||||
//line app/vmalert/web.qtpl:119
|
||||
qw422016.N().S(`
|
||||
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:121
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:121
|
||||
func WriteListGroups(qq422016 qtio422016.Writer, groups []APIGroup) {
|
||||
//line app/vmalert/web.qtpl:121
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmalert/web.qtpl:121
|
||||
StreamListGroups(qw422016, groups)
|
||||
//line app/vmalert/web.qtpl:121
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmalert/web.qtpl:121
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:121
|
||||
func ListGroups(groups []APIGroup) string {
|
||||
//line app/vmalert/web.qtpl:121
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmalert/web.qtpl:121
|
||||
WriteListGroups(qb422016, groups)
|
||||
//line app/vmalert/web.qtpl:121
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmalert/web.qtpl:121
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmalert/web.qtpl:121
|
||||
return qs422016
|
||||
//line app/vmalert/web.qtpl:121
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:124
|
||||
func StreamListAlerts(qw422016 *qt422016.Writer, groupAlerts []GroupAlerts) {
|
||||
//line app/vmalert/web.qtpl:124
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:125
|
||||
tpl.StreamHeader(qw422016, "Alerts", navItems)
|
||||
//line app/vmalert/web.qtpl:125
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:126
|
||||
if len(groupAlerts) > 0 {
|
||||
//line app/vmalert/web.qtpl:126
|
||||
qw422016.N().S(`
|
||||
<a class="btn btn-primary" role="button" onclick="collapseAll()">Collapse All</a>
|
||||
<a class="btn btn-primary" role="button" onclick="expandAll()">Expand All</a>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:129
|
||||
for _, ga := range groupAlerts {
|
||||
//line app/vmalert/web.qtpl:129
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:130
|
||||
g := ga.Group
|
||||
|
||||
//line app/vmalert/web.qtpl:130
|
||||
qw422016.N().S(`
|
||||
<div class="group-heading alert-danger" data-bs-target="rules-`)
|
||||
//line app/vmalert/web.qtpl:131
|
||||
qw422016.E().S(g.ID)
|
||||
//line app/vmalert/web.qtpl:131
|
||||
qw422016.N().S(`">
|
||||
<span class="anchor" id="group-`)
|
||||
//line app/vmalert/web.qtpl:132
|
||||
qw422016.E().S(g.ID)
|
||||
//line app/vmalert/web.qtpl:132
|
||||
qw422016.N().S(`"></span>
|
||||
<a href="#group-`)
|
||||
//line app/vmalert/web.qtpl:133
|
||||
qw422016.E().S(g.ID)
|
||||
//line app/vmalert/web.qtpl:133
|
||||
qw422016.N().S(`">`)
|
||||
//line app/vmalert/web.qtpl:133
|
||||
qw422016.E().S(g.Name)
|
||||
//line app/vmalert/web.qtpl:133
|
||||
if g.Type != "prometheus" {
|
||||
//line app/vmalert/web.qtpl:133
|
||||
qw422016.N().S(` (`)
|
||||
//line app/vmalert/web.qtpl:133
|
||||
qw422016.E().S(g.Type)
|
||||
//line app/vmalert/web.qtpl:133
|
||||
qw422016.N().S(`)`)
|
||||
//line app/vmalert/web.qtpl:133
|
||||
}
|
||||
//line app/vmalert/web.qtpl:133
|
||||
qw422016.N().S(`</a>
|
||||
<span class="badge bg-danger" title="Number of active alerts">`)
|
||||
//line app/vmalert/web.qtpl:134
|
||||
qw422016.N().D(len(ga.Alerts))
|
||||
//line app/vmalert/web.qtpl:134
|
||||
qw422016.N().S(`</span>
|
||||
<br>
|
||||
<p class="fs-6 fw-lighter">`)
|
||||
//line app/vmalert/web.qtpl:136
|
||||
qw422016.E().S(g.File)
|
||||
//line app/vmalert/web.qtpl:136
|
||||
qw422016.N().S(`</p>
|
||||
</div>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:139
|
||||
var keys []string
|
||||
alertsByRule := make(map[string][]*APIAlert)
|
||||
for _, alert := range ga.Alerts {
|
||||
if len(alertsByRule[alert.RuleID]) < 1 {
|
||||
keys = append(keys, alert.RuleID)
|
||||
}
|
||||
alertsByRule[alert.RuleID] = append(alertsByRule[alert.RuleID], alert)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
//line app/vmalert/web.qtpl:148
|
||||
qw422016.N().S(`
|
||||
<div class="collapse" id="rules-`)
|
||||
//line app/vmalert/web.qtpl:149
|
||||
qw422016.E().S(g.ID)
|
||||
//line app/vmalert/web.qtpl:149
|
||||
qw422016.N().S(`">
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:150
|
||||
for _, ruleID := range keys {
|
||||
//line app/vmalert/web.qtpl:150
|
||||
qw422016.N().S(`
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:152
|
||||
defaultAR := alertsByRule[ruleID][0]
|
||||
var labelKeys []string
|
||||
for k := range defaultAR.Labels {
|
||||
labelKeys = append(labelKeys, k)
|
||||
}
|
||||
sort.Strings(labelKeys)
|
||||
|
||||
//line app/vmalert/web.qtpl:158
|
||||
qw422016.N().S(`
|
||||
<br>
|
||||
<b>alert:</b> `)
|
||||
//line app/vmalert/web.qtpl:160
|
||||
qw422016.E().S(defaultAR.Name)
|
||||
//line app/vmalert/web.qtpl:160
|
||||
qw422016.N().S(` (`)
|
||||
//line app/vmalert/web.qtpl:160
|
||||
qw422016.N().D(len(alertsByRule[ruleID]))
|
||||
//line app/vmalert/web.qtpl:160
|
||||
qw422016.N().S(`)<br>
|
||||
<b>expr:</b><code><pre>`)
|
||||
//line app/vmalert/web.qtpl:161
|
||||
qw422016.E().S(defaultAR.Expression)
|
||||
//line app/vmalert/web.qtpl:161
|
||||
qw422016.N().S(`</pre></code>
|
||||
<table class="table table-striped table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Labels</th>
|
||||
<th scope="col">State</th>
|
||||
<th scope="col">Active at</th>
|
||||
<th scope="col">Value</th>
|
||||
<th scope="col">Link</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:173
|
||||
for _, ar := range alertsByRule[ruleID] {
|
||||
//line app/vmalert/web.qtpl:173
|
||||
qw422016.N().S(`
|
||||
<tr>
|
||||
<td>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:176
|
||||
for _, k := range labelKeys {
|
||||
//line app/vmalert/web.qtpl:176
|
||||
qw422016.N().S(`
|
||||
<span class="ms-1 badge bg-primary">`)
|
||||
//line app/vmalert/web.qtpl:177
|
||||
qw422016.E().S(k)
|
||||
//line app/vmalert/web.qtpl:177
|
||||
qw422016.N().S(`=`)
|
||||
//line app/vmalert/web.qtpl:177
|
||||
qw422016.E().S(ar.Labels[k])
|
||||
//line app/vmalert/web.qtpl:177
|
||||
qw422016.N().S(`</span>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:178
|
||||
}
|
||||
//line app/vmalert/web.qtpl:178
|
||||
qw422016.N().S(`
|
||||
</td>
|
||||
<td><span class="badge `)
|
||||
//line app/vmalert/web.qtpl:180
|
||||
if ar.State == "firing" {
|
||||
//line app/vmalert/web.qtpl:180
|
||||
qw422016.N().S(`bg-danger`)
|
||||
//line app/vmalert/web.qtpl:180
|
||||
} else {
|
||||
//line app/vmalert/web.qtpl:180
|
||||
qw422016.N().S(` bg-warning text-dark`)
|
||||
//line app/vmalert/web.qtpl:180
|
||||
}
|
||||
//line app/vmalert/web.qtpl:180
|
||||
qw422016.N().S(`">`)
|
||||
//line app/vmalert/web.qtpl:180
|
||||
qw422016.E().S(ar.State)
|
||||
//line app/vmalert/web.qtpl:180
|
||||
qw422016.N().S(`</span></td>
|
||||
<td>`)
|
||||
//line app/vmalert/web.qtpl:181
|
||||
qw422016.E().S(ar.ActiveAt.Format("2006-01-02T15:04:05Z07:00"))
|
||||
//line app/vmalert/web.qtpl:181
|
||||
qw422016.N().S(`</td>
|
||||
<td>`)
|
||||
//line app/vmalert/web.qtpl:182
|
||||
qw422016.E().S(ar.Value)
|
||||
//line app/vmalert/web.qtpl:182
|
||||
qw422016.N().S(`</td>
|
||||
<td>
|
||||
<a href="/`)
|
||||
//line app/vmalert/web.qtpl:184
|
||||
qw422016.E().S(g.ID)
|
||||
//line app/vmalert/web.qtpl:184
|
||||
qw422016.N().S(`/`)
|
||||
//line app/vmalert/web.qtpl:184
|
||||
qw422016.E().S(ar.ID)
|
||||
//line app/vmalert/web.qtpl:184
|
||||
qw422016.N().S(`/status">Details</a>
|
||||
</td>
|
||||
</tr>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:187
|
||||
}
|
||||
//line app/vmalert/web.qtpl:187
|
||||
qw422016.N().S(`
|
||||
</tbody>
|
||||
</table>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:190
|
||||
}
|
||||
//line app/vmalert/web.qtpl:190
|
||||
qw422016.N().S(`
|
||||
</div>
|
||||
<br>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:193
|
||||
}
|
||||
//line app/vmalert/web.qtpl:193
|
||||
qw422016.N().S(`
|
||||
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:195
|
||||
} else {
|
||||
//line app/vmalert/web.qtpl:195
|
||||
qw422016.N().S(`
|
||||
<div>
|
||||
<p>No items...</p>
|
||||
</div>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:199
|
||||
}
|
||||
//line app/vmalert/web.qtpl:199
|
||||
qw422016.N().S(`
|
||||
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:201
|
||||
tpl.StreamFooter(qw422016)
|
||||
//line app/vmalert/web.qtpl:201
|
||||
qw422016.N().S(`
|
||||
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:203
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:203
|
||||
func WriteListAlerts(qq422016 qtio422016.Writer, groupAlerts []GroupAlerts) {
|
||||
//line app/vmalert/web.qtpl:203
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmalert/web.qtpl:203
|
||||
StreamListAlerts(qw422016, groupAlerts)
|
||||
//line app/vmalert/web.qtpl:203
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmalert/web.qtpl:203
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:203
|
||||
func ListAlerts(groupAlerts []GroupAlerts) string {
|
||||
//line app/vmalert/web.qtpl:203
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmalert/web.qtpl:203
|
||||
WriteListAlerts(qb422016, groupAlerts)
|
||||
//line app/vmalert/web.qtpl:203
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmalert/web.qtpl:203
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmalert/web.qtpl:203
|
||||
return qs422016
|
||||
//line app/vmalert/web.qtpl:203
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:205
|
||||
func StreamAlert(qw422016 *qt422016.Writer, alert *APIAlert) {
|
||||
//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)
|
||||
}
|
||||
sort.Strings(labelKeys)
|
||||
|
||||
var annotationKeys []string
|
||||
for k := range alert.Annotations {
|
||||
annotationKeys = append(annotationKeys, k)
|
||||
}
|
||||
sort.Strings(annotationKeys)
|
||||
|
||||
//line app/vmalert/web.qtpl:219
|
||||
qw422016.N().S(`
|
||||
<div class="display-6 pb-3 mb-3">`)
|
||||
//line app/vmalert/web.qtpl:220
|
||||
qw422016.E().S(alert.Name)
|
||||
//line app/vmalert/web.qtpl:220
|
||||
qw422016.N().S(`<span class="ms-2 badge `)
|
||||
//line app/vmalert/web.qtpl:220
|
||||
if alert.State == "firing" {
|
||||
//line app/vmalert/web.qtpl:220
|
||||
qw422016.N().S(`bg-danger`)
|
||||
//line app/vmalert/web.qtpl:220
|
||||
} else {
|
||||
//line app/vmalert/web.qtpl:220
|
||||
qw422016.N().S(` bg-warning text-dark`)
|
||||
//line app/vmalert/web.qtpl:220
|
||||
}
|
||||
//line app/vmalert/web.qtpl:220
|
||||
qw422016.N().S(`">`)
|
||||
//line app/vmalert/web.qtpl:220
|
||||
qw422016.E().S(alert.State)
|
||||
//line app/vmalert/web.qtpl:220
|
||||
qw422016.N().S(`</span></div>
|
||||
<div class="container border-bottom p-2">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
Active at
|
||||
</div>
|
||||
<div class="col">
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:227
|
||||
qw422016.E().S(alert.ActiveAt.Format("2006-01-02T15:04:05Z07:00"))
|
||||
//line app/vmalert/web.qtpl:227
|
||||
qw422016.N().S(`
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container border-bottom p-2">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
Expr
|
||||
</div>
|
||||
<div class="col">
|
||||
<code><pre>`)
|
||||
//line app/vmalert/web.qtpl:237
|
||||
qw422016.E().S(alert.Expression)
|
||||
//line app/vmalert/web.qtpl:237
|
||||
qw422016.N().S(`</pre></code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container border-bottom p-2">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
Labels
|
||||
</div>
|
||||
<div class="col">
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:247
|
||||
for _, k := range labelKeys {
|
||||
//line app/vmalert/web.qtpl:247
|
||||
qw422016.N().S(`
|
||||
<span class="m-1 badge bg-primary">`)
|
||||
//line app/vmalert/web.qtpl:248
|
||||
qw422016.E().S(k)
|
||||
//line app/vmalert/web.qtpl:248
|
||||
qw422016.N().S(`=`)
|
||||
//line app/vmalert/web.qtpl:248
|
||||
qw422016.E().S(alert.Labels[k])
|
||||
//line app/vmalert/web.qtpl:248
|
||||
qw422016.N().S(`</span>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:249
|
||||
}
|
||||
//line app/vmalert/web.qtpl:249
|
||||
qw422016.N().S(`
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container border-bottom p-2">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
Annotations
|
||||
</div>
|
||||
<div class="col">
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:259
|
||||
for _, k := range annotationKeys {
|
||||
//line app/vmalert/web.qtpl:259
|
||||
qw422016.N().S(`
|
||||
<b>`)
|
||||
//line app/vmalert/web.qtpl:260
|
||||
qw422016.E().S(k)
|
||||
//line app/vmalert/web.qtpl:260
|
||||
qw422016.N().S(`:</b><br>
|
||||
<p>`)
|
||||
//line app/vmalert/web.qtpl:261
|
||||
qw422016.E().S(alert.Annotations[k])
|
||||
//line app/vmalert/web.qtpl:261
|
||||
qw422016.N().S(`</p>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:262
|
||||
}
|
||||
//line app/vmalert/web.qtpl:262
|
||||
qw422016.N().S(`
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container border-bottom p-2">
|
||||
<div class="row">
|
||||
<div class="col-2">
|
||||
Group
|
||||
</div>
|
||||
<div class="col">
|
||||
<a target="_blank" href="/groups#group-`)
|
||||
//line app/vmalert/web.qtpl:272
|
||||
qw422016.E().S(alert.GroupID)
|
||||
//line app/vmalert/web.qtpl:272
|
||||
qw422016.N().S(`">`)
|
||||
//line app/vmalert/web.qtpl:272
|
||||
qw422016.E().S(alert.GroupID)
|
||||
//line app/vmalert/web.qtpl:272
|
||||
qw422016.N().S(`</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:276
|
||||
tpl.StreamFooter(qw422016)
|
||||
//line app/vmalert/web.qtpl:276
|
||||
qw422016.N().S(`
|
||||
|
||||
`)
|
||||
//line app/vmalert/web.qtpl:278
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:278
|
||||
func WriteAlert(qq422016 qtio422016.Writer, alert *APIAlert) {
|
||||
//line app/vmalert/web.qtpl:278
|
||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||
//line app/vmalert/web.qtpl:278
|
||||
StreamAlert(qw422016, alert)
|
||||
//line app/vmalert/web.qtpl:278
|
||||
qt422016.ReleaseWriter(qw422016)
|
||||
//line app/vmalert/web.qtpl:278
|
||||
}
|
||||
|
||||
//line app/vmalert/web.qtpl:278
|
||||
func Alert(alert *APIAlert) string {
|
||||
//line app/vmalert/web.qtpl:278
|
||||
qb422016 := qt422016.AcquireByteBuffer()
|
||||
//line app/vmalert/web.qtpl:278
|
||||
WriteAlert(qb422016, alert)
|
||||
//line app/vmalert/web.qtpl:278
|
||||
qs422016 := string(qb422016.B)
|
||||
//line app/vmalert/web.qtpl:278
|
||||
qt422016.ReleaseByteBuffer(qb422016)
|
||||
//line app/vmalert/web.qtpl:278
|
||||
return qs422016
|
||||
//line app/vmalert/web.qtpl:278
|
||||
}
|
|
@ -9,6 +9,7 @@ import (
|
|||
type APIAlert struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
RuleID string `json:"rule_id"`
|
||||
GroupID string `json:"group_id"`
|
||||
Expression string `json:"expression"`
|
||||
State string `json:"state"`
|
||||
|
@ -59,3 +60,8 @@ type APIRecordingRule struct {
|
|||
LastExec time.Time `json:"last_exec"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
type GroupAlerts struct {
|
||||
Group APIGroup
|
||||
Alerts []*APIAlert
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue