diff --git a/retrieval/target.go b/retrieval/target.go index c51f5048cf..ac52eb4129 100644 --- a/retrieval/target.go +++ b/retrieval/target.go @@ -73,11 +73,11 @@ type TargetHealth int func (t TargetHealth) String() string { switch t { case HealthUnknown: - return "UNKNOWN" + return "unknown" case HealthGood: - return "HEALTHY" + return "healthy" case HealthBad: - return "UNHEALTHY" + return "unhealthy" } panic("unknown state") } diff --git a/rules/alerting.go b/rules/alerting.go index 54514db9bb..49655740df 100644 --- a/rules/alerting.go +++ b/rules/alerting.go @@ -209,23 +209,38 @@ func (rule *AlertingRule) eval(timestamp clientmodel.Timestamp, engine *promql.E } func (rule *AlertingRule) String() string { - return fmt.Sprintf("ALERT %s IF %s FOR %s WITH %s", rule.name, rule.vector, strutil.DurationToString(rule.holdDuration), rule.labels) + s := fmt.Sprintf("ALERT %s", rule.name) + s += fmt.Sprintf("\n\tIF %s", rule.vector) + if rule.holdDuration > 0 { + s += fmt.Sprintf("\n\tFOR %s", strutil.DurationToString(rule.holdDuration)) + } + if len(rule.labels) > 0 { + s += fmt.Sprintf("\n\tWITH %s", rule.labels) + } + s += fmt.Sprintf("\n\tSUMMARY %q", rule.summary) + s += fmt.Sprintf("\n\tDESCRIPTION %q", rule.description) + return s } -// HTMLSnippet returns an HTML snippet representing this alerting rule. +// HTMLSnippet returns an HTML snippet representing this alerting rule. The +// resulting snippet is expected to be presented in a
element, so that
+// line breaks and other returned whitespace is respected.
func (rule *AlertingRule) HTMLSnippet(pathPrefix string) template.HTML {
alertMetric := clientmodel.Metric{
clientmodel.MetricNameLabel: alertMetricName,
alertNameLabel: clientmodel.LabelValue(rule.name),
}
- return template.HTML(fmt.Sprintf(
- `ALERT %s IF %s FOR %s WITH %s`,
- pathPrefix+strutil.GraphLinkForExpression(alertMetric.String()),
- rule.name,
- pathPrefix+strutil.GraphLinkForExpression(rule.vector.String()),
- rule.vector,
- strutil.DurationToString(rule.holdDuration),
- rule.labels))
+ s := fmt.Sprintf("ALERT %s", pathPrefix+strutil.GraphLinkForExpression(alertMetric.String()), rule.name)
+ s += fmt.Sprintf("\n IF %s", pathPrefix+strutil.GraphLinkForExpression(rule.vector.String()), rule.vector)
+ if rule.holdDuration > 0 {
+ s += fmt.Sprintf("\n FOR %s", strutil.DurationToString(rule.holdDuration))
+ }
+ if len(rule.labels) > 0 {
+ s += fmt.Sprintf("\n WITH %s", rule.labels)
+ }
+ s += fmt.Sprintf("\n SUMMARY %q", rule.summary)
+ s += fmt.Sprintf("\n DESCRIPTION %q", rule.description)
+ return template.HTML(s)
}
// State returns the "maximum" state: firing > pending > inactive.
diff --git a/template/template.go b/template/template.go
index e7060cc4e9..7b7cdd2670 100644
--- a/template/template.go
+++ b/template/template.go
@@ -238,6 +238,13 @@ func NewTemplateExpander(text string, name string, data interface{}, timestamp c
}
return fmt.Sprintf("%.4g%ss", v, prefix)
},
+ "humanizeTimestamp": func(v float64) string {
+ if math.IsNaN(v) || math.IsInf(v, 0) {
+ return fmt.Sprintf("%.4g", v)
+ }
+ t := clientmodel.TimestampFromUnixNano(int64(v * 1000000000)).Time()
+ return fmt.Sprint(t)
+ },
"pathPrefix": func() string {
return pathPrefix
},
@@ -245,6 +252,14 @@ func NewTemplateExpander(text string, name string, data interface{}, timestamp c
}
}
+// Funcs adds the functions in fm to the templateExpander's function map.
+// Existing functions will be overwritten in case of conflict.
+func (te templateExpander) Funcs(fm text_template.FuncMap) {
+ for k, v := range fm {
+ te.funcMap[k] = v
+ }
+}
+
// Expand a template.
func (te templateExpander) Expand() (result string, resultErr error) {
// It'd better to have no alert description than to kill the whole process
diff --git a/template/template_test.go b/template/template_test.go
index b7e1b54c05..b3a92bc531 100644
--- a/template/template_test.go
+++ b/template/template_test.go
@@ -135,9 +135,14 @@ func TestTemplateExpansion(t *testing.T) {
},
{
// Humanize* Inf and NaN.
- text: "{{ range . }}{{ humanize . }}:{{ humanize1024 . }}:{{ humanizeDuration . }}:{{ end }}",
+ text: "{{ range . }}{{ humanize . }}:{{ humanize1024 . }}:{{ humanizeDuration . }}:{{humanizeTimestamp .}}:{{ end }}",
input: []float64{math.Inf(1), math.Inf(-1), math.NaN()},
- output: "+Inf:+Inf:+Inf:-Inf:-Inf:-Inf:NaN:NaN:NaN:",
+ output: "+Inf:+Inf:+Inf:+Inf:-Inf:-Inf:-Inf:-Inf:NaN:NaN:NaN:NaN:",
+ },
+ {
+ // HumanizeTimestamp - clientmodel.SampleValue input.
+ text: "{{ 1435065584.128 | humanizeTimestamp }}",
+ output: "2015-06-23 15:19:44.128 +0200 CEST",
},
{
// Title.
diff --git a/web/blob/static/css/alerts.css b/web/blob/static/css/alerts.css
index f7061c4d8a..798fc36912 100644
--- a/web/blob/static/css/alerts.css
+++ b/web/blob/static/css/alerts.css
@@ -5,19 +5,3 @@
.alert_details {
display: none;
}
-
-.silence_children_link {
- margin-left: 5px;
-}
-
-.alert_rule {
- padding: 5px;
- color: #333;
- background-color: #ddd;
- font-family: monospace;
- font-weight: normal;
-}
-
-.alert_description {
- padding: 8px 0 8px 0;
-}
diff --git a/web/blob/static/css/prometheus.css b/web/blob/static/css/prometheus.css
index 623fd743cd..c44c65ec17 100644
--- a/web/blob/static/css/prometheus.css
+++ b/web/blob/static/css/prometheus.css
@@ -10,8 +10,9 @@ th.job_header {
padding-bottom: 10px;
}
-.target_status_alert {
+.state_indicator {
padding: 0 4px 0 4px;
+ text-transform: uppercase;
}
.literal_output td {
diff --git a/web/blob/templates/alerts.html b/web/blob/templates/alerts.html
index fc374e00d5..bc9015ddec 100644
--- a/web/blob/templates/alerts.html
+++ b/web/blob/templates/alerts.html
@@ -16,9 +16,9 @@
-
- {{.HTMLSnippet pathPrefix}}
- Silence All Children…
+
+ {{.HTMLSnippet pathPrefix}}
+ Silence all instances of this alert…
{{if $activeAlerts}}
@@ -30,12 +30,16 @@
Silence
{{range $activeAlerts}}
-
- {{.Labels}}
- {{.State}}
- {{.ActiveSince}}
+
+
+ {{range $label, $value := .Labels}}
+ {{$label}}="{{$value}}"
+ {{end}}
+
+ {{.State}}
+ {{.ActiveSince.Time}}
{{.Value}}
- Silence…
+ Silence…
{{end}}
diff --git a/web/blob/templates/status.html b/web/blob/templates/status.html
index becba8f6cf..69678a32d4 100644
--- a/web/blob/templates/status.html
+++ b/web/blob/templates/status.html
@@ -55,7 +55,7 @@
{{end}}
-
+
{{.Status.Health}}
diff --git a/web/web.go b/web/web.go
index 844d0cda39..3059835c59 100644
--- a/web/web.go
+++ b/web/web.go
@@ -27,8 +27,8 @@ import (
"sync"
"time"
- template_std "html/template"
pprof_runtime "runtime/pprof"
+ template_text "text/template"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/client_golang/prometheus"
@@ -320,11 +320,26 @@ func (h *Handler) getConsoles() string {
return ""
}
-func (h *Handler) getTemplate(name string) (*template_std.Template, error) {
- t := template_std.New("_base")
- var err error
+func (h *Handler) getTemplate(name string) (string, error) {
+ baseTmpl, err := h.getTemplateFile("_base")
+ if err != nil {
+ return "", fmt.Errorf("Error reading base template: %s", err)
+ }
+ pageTmpl, err := h.getTemplateFile(name)
+ if err != nil {
+ return "", fmt.Errorf("Error reading page template %s: %s", name, err)
+ }
+ return baseTmpl + pageTmpl, nil
+}
- t.Funcs(template_std.FuncMap{
+func (h *Handler) executeTemplate(w http.ResponseWriter, name string, data interface{}) {
+ text, err := h.getTemplate(name)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+
+ tmpl := template.NewTemplateExpander(text, name, data, clientmodel.Now(), h.queryEngine, h.options.PathPrefix)
+ tmpl.Funcs(template_text.FuncMap{
"since": time.Since,
"getConsoles": h.getConsoles,
"pathPrefix": func() string { return h.options.PathPrefix },
@@ -352,40 +367,26 @@ func (h *Handler) getTemplate(name string) (*template_std.Template, error) {
return "danger"
}
},
+ "alertStateToClass": func(as rules.AlertState) string {
+ switch as {
+ case rules.StateInactive:
+ return "success"
+ case rules.StatePending:
+ return "warning"
+ case rules.StateFiring:
+ return "danger"
+ default:
+ panic("unknown alert state")
+ }
+ },
})
- file, err := h.getTemplateFile("_base")
+ result, err := tmpl.ExpandHTML(nil)
if err != nil {
- log.Errorln("Could not read base template:", err)
- return nil, err
- }
- t, err = t.Parse(file)
- if err != nil {
- log.Errorln("Could not parse base template:", err)
- }
-
- file, err = h.getTemplateFile(name)
- if err != nil {
- log.Error("Could not read template %s: %s", name, err)
- return nil, err
- }
- t, err = t.Parse(file)
- if err != nil {
- log.Errorf("Could not parse template %s: %s", name, err)
- }
- return t, err
-}
-
-func (h *Handler) executeTemplate(w http.ResponseWriter, name string, data interface{}) {
- tpl, err := h.getTemplate(name)
- if err != nil {
- log.Error("Error preparing layout template: ", err)
+ http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- err = tpl.Execute(w, data)
- if err != nil {
- log.Error("Error executing template: ", err)
- }
+ io.WriteString(w, result)
}
func dumpHeap(w http.ResponseWriter, r *http.Request) {