Add more required fields to Event.

This adds mandatory Summary and Description fields to Event.

As for the alert name, there were two options: keep it a separate field and
treat it separately everywhere (including in silence Filter matching), or
make it a required field in the event's labels. The latter was causing far
less trouble, so I went with that. The alertname label still doesn't have
a special meaning to most parts of the code, except that the API checks its
presence and the web UI displays it differently.
This commit is contained in:
Julius Volz 2013-07-30 13:18:11 +02:00
parent a64c37bb03
commit f431335c69
4 changed files with 55 additions and 7 deletions

View File

@ -19,7 +19,7 @@ import (
"sort"
)
const eventNameLabel = "name"
const EventNameLabel = "alertname"
type EventFingerprint uint64
@ -28,16 +28,19 @@ type EventPayload map[string]string
// Event models an action triggered by Prometheus.
type Event struct {
// Short summary of event.
Summary string
// Long description of event.
Description string
// Label value pairs for purpose of aggregation, matching, and disposition
// dispatching. This must minimally include a "name" label.
// dispatching. This must minimally include an "alertname" label.
Labels EventLabels
// Extra key/value information which is not used for aggregation.
Payload EventPayload
}
func (e Event) Name() string {
// BUG: ensure in a proper place that all events have a name?
return e.Labels[eventNameLabel]
return e.Labels[EventNameLabel]
}
func (e Event) Fingerprint() EventFingerprint {

View File

@ -14,14 +14,31 @@
package api
import (
"log"
"net/http"
"github.com/prometheus/alert_manager/manager"
)
func (s AlertManagerService) AddEvents(es manager.Events) {
for i, ev := range es {
if ev.Summary == "" || ev.Description == "" {
log.Printf("Missing field in event %d: %s", i, ev)
rb := s.ResponseBuilder()
rb.SetResponseCode(http.StatusBadRequest)
return
}
if _, ok := ev.Labels[manager.EventNameLabel]; !ok {
log.Printf("Missing alert name label in event %d: %s", i, ev)
rb := s.ResponseBuilder()
rb.SetResponseCode(http.StatusBadRequest)
return
}
}
err := s.Aggregator.Receive(es)
if err != nil {
log.Println("Error during aggregation:", err)
rb := s.ResponseBuilder()
rb.SetResponseCode(http.StatusServiceUnavailable)
}

View File

@ -15,6 +15,7 @@ package web
import (
"html/template"
"reflect"
"time"
)
@ -22,6 +23,31 @@ func timeSince(t time.Time) string {
return time.Now().Round(time.Second / 10).Sub(t.Round(time.Second / 10)).String()
}
// By Russ Cox, https://groups.google.com/d/msg/golang-nuts/OEdSDgEC7js/iyhU9DW_IKcJ.
func eq(args ...interface{}) bool {
if len(args) == 0 {
return false
}
x := args[0]
switch x := x.(type) {
case string, int, int64, byte, float32, float64:
for _, y := range args[1:] {
if x == y {
return true
}
}
return false
}
for _, y := range args[1:] {
if reflect.DeepEqual(x, y) {
return true
}
}
return false
}
var webHelpers = template.FuncMap{
"timeSince": timeSince,
"eq": eq,
}

View File

@ -27,14 +27,16 @@
<td>
<span class="label label-important">{{index .Event.Name}}</span>
<form class="add_silence_form">
<input type="hidden" name="label[]" value="name">
<input type="hidden" name="value[]" value="{{.Event.Labels.name}}">
<input type="hidden" name="label[]" value="alertname">
<input type="hidden" name="value[]" value="{{.Event.Name}}">
<a href="#edit_silence_modal" role="button" class="btn btn-mini add_silence_btn" data-toggle="modal">Silence Alert</a>
</form>
</td>
<td>
{{range $label, $value := .Event.Labels}}
<span class="label label-info"><b>{{$label}}</b>="{{$value}}"</span>
{{if not (eq $label "alertname")}}
<span class="label label-info"><b>{{$label}}</b>="{{$value}}"</span>
{{end}}
{{end}}
<form class="add_silence_form">
{{range $label, $value := .Event.Labels}}