Partition the status tab into items in a dropdown

I got feedback from different sources about rules and targets being
too heavy in the status tab if their are lots of them.

This change also allows for more fine-granular locking.
This commit is contained in:
beorn7 2016-05-13 17:59:59 +02:00
parent 8ec10ae7a5
commit da8cb10b43
10 changed files with 619 additions and 590 deletions

View file

@ -103,13 +103,6 @@ func Main() int {
flags[f.Name] = f.Value.String() flags[f.Name] = f.Value.String()
}) })
status := &web.PrometheusStatus{
TargetPools: targetManager.Pools,
Rules: ruleManager.Rules,
Flags: flags,
Birth: time.Now(),
}
version := &web.PrometheusVersion{ version := &web.PrometheusVersion{
Version: version.Version, Version: version.Version,
Revision: version.Revision, Revision: version.Revision,
@ -119,9 +112,9 @@ func Main() int {
GoVersion: version.GoVersion, GoVersion: version.GoVersion,
} }
webHandler := web.New(memStorage, queryEngine, ruleManager, status, version, &cfg.web) webHandler := web.New(memStorage, queryEngine, targetManager, ruleManager, version, flags, &cfg.web)
reloadables = append(reloadables, status, targetManager, ruleManager, webHandler, notifier) reloadables = append(reloadables, targetManager, ruleManager, webHandler, notifier)
if !reloadConfig(cfg.configFile, reloadables...) { if !reloadConfig(cfg.configFile, reloadables...) {
return 1 return 1

View file

@ -26,7 +26,7 @@ func TestParseTimestampOrNow(t *testing.T) {
t.Fatalf("err = %s; want nil", err) t.Fatalf("err = %s; want nil", err)
} }
if !testNow().Equal(ts) { if !testNow().Equal(ts) {
t.Fatalf("ts = %v; want ts = %v", ts, testNow) t.Fatalf("ts = %v; want ts = %v", ts, testNow())
} }
ts, err = parseTimestampOrNow("1426956073.12345", testNow()) ts, err = parseTimestampOrNow("1426956073.12345", testNow())

File diff suppressed because one or more lines are too long

View file

@ -39,7 +39,16 @@
{{ end }} {{ end }}
<li><a href="{{ pathPrefix }}/alerts">Alerts</a></li> <li><a href="{{ pathPrefix }}/alerts">Alerts</a></li>
<li><a href="{{ pathPrefix }}/graph">Graph</a></li> <li><a href="{{ pathPrefix }}/graph">Graph</a></li>
<li><a href="{{ pathPrefix }}/status">Status</a></li> w <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Status <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/status">Runtime &amp; Build Information</a></li>
<li><a href="/flags">Command-Line Flags</a></li>
<li><a href="/config">Configuration</a></li>
<li><a href="/rules">Rules</a></li>
<li><a href="/targets">Targets</a></li>
</ul>
</li>
<li> <li>
<a href="https://prometheus.io" target="_blank">Help</a> <a href="https://prometheus.io" target="_blank">Help</a>
</li> </li>

View file

@ -0,0 +1,8 @@
{{define "head"}}<!-- nix -->{{end}}
{{define "content"}}
<div class="container-fluid">
<h2 id="configuration">Configuration</h2>
<pre>{{.}}</pre>
</div>
{{end}}

View file

@ -0,0 +1,17 @@
{{define "head"}}<!-- nix -->{{end}}
{{define "content"}}
<div class="container-fluid">
<h2 id="startupflags">Command-Line Flags</h2>
<table class="table table-condensed table-bordered table-striped table-hover">
<tbody>
{{range $key, $value := . }}
<tr>
<th scope="row">{{$key}}</th>
<td>{{$value}}</td>
</tr>
{{end}}
</tbody>
</table>
</div>
{{end}}

View file

@ -0,0 +1,8 @@
{{define "head"}}<!-- nix -->{{end}}
{{define "content"}}
<div class="container-fluid">
<h2 id="rules">Rules</h2>
<pre>{{range .Rules}}{{.HTMLSnippet pathPrefix}}<br/>{{end}}</pre>
</div>
{{end}}

View file

@ -7,7 +7,7 @@
<tbody> <tbody>
<tr> <tr>
<th>Uptime</th> <th>Uptime</th>
<td>{{.Status.Birth.UTC}}</td> <td>{{.Birth.UTC}}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -41,76 +41,5 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<h2 id="configuration">Configuration</h2>
<pre>{{.Status.Config}}</pre>
<h2 id="rules">Rules</h2>
<pre>{{range call .Status.Rules}}{{.HTMLSnippet pathPrefix}}<br/>{{end}}</pre>
<h2 id="targets">Targets</h2>
<table class="table table-condensed table-bordered table-striped table-hover">
{{range $job, $pool := call .Status.TargetPools}}
<thead>
<tr><th colspan="5" class="job_header">{{$job}}</th></tr>
<tr>
<th>Endpoint</th>
<th>State</th>
<th>Labels</th>
<th>Last Scrape</th>
<th>Error</th>
</tr>
</thead>
<tbody>
{{range $pool}}
<tr>
<td>
<a href="{{.URL | globalURL}}">{{.URL.Scheme}}://{{.URL.Host}}{{.URL.Path}}</a><br>
{{range $label, $values := .URL.Query }}
{{range $i, $value := $values}}
<span class="label label-primary">{{$label}}="{{$value}}"</span>
{{end}}
{{end}}
</td>
<td>
<span class="alert alert-{{ .Health | healthToClass }} state_indicator text-uppercase">
{{.Health}}
</span>
</td>
<td>
<span class="cursor-pointer" data-toggle="tooltip" title="" data-html=true data-original-title="<b>Before relabeling:</b>{{range $k, $v := .MetaLabels}}<br>{{$k | html | html}}=&quot;{{$v | html | html}}&quot;{{end}}">
{{$labels := stripLabels .Labels "job" "instance"}}
{{range $label, $value := $labels}}
<span class="label label-primary">{{$label}}="{{$value}}"</span>
{{else}}
<span class="label label-default">none</span>
{{end}}
</span>
</td>
<td>
{{if .LastScrape.IsZero}}Never{{else}}{{since .LastScrape}} ago{{end}}
</td>
<td>
{{if .LastError}}
<span class="alert alert-danger state_indicator">{{.LastError}}</span>
{{end}}
</td>
</tr>
{{end}}
</tbody>
{{end}}
</table>
<h2 id="startupflags">Startup Flags</h2>
<table class="table table-condensed table-bordered table-striped table-hover">
<tbody>
{{range $key, $value := .Status.Flags}}
<tr>
<th scope="row">{{$key}}</th>
<td>{{$value}}</td>
</tr>
{{end}}
</tbody>
</table>
</div> </div>
{{end}} {{end}}

View file

@ -0,0 +1,58 @@
{{define "head"}}<!-- nix -->{{end}}
{{define "content"}}
<div class="container-fluid">
<h2 id="targets">Targets</h2>
<table class="table table-condensed table-bordered table-striped table-hover">
{{range $job, $pool := .Pools}}
<thead>
<tr><th colspan="5" class="job_header">{{$job}}</th></tr>
<tr>
<th>Endpoint</th>
<th>State</th>
<th>Labels</th>
<th>Last Scrape</th>
<th>Error</th>
</tr>
</thead>
<tbody>
{{range $pool}}
<tr>
<td>
<a href="{{.URL | globalURL}}">{{.URL.Scheme}}://{{.URL.Host}}{{.URL.Path}}</a><br>
{{range $label, $values := .URL.Query }}
{{range $i, $value := $values}}
<span class="label label-primary">{{$label}}="{{$value}}"</span>
{{end}}
{{end}}
</td>
<td>
<span class="alert alert-{{ .Health | healthToClass }} state_indicator text-uppercase">
{{.Health}}
</span>
</td>
<td>
<span class="cursor-pointer" data-toggle="tooltip" title="" data-html=true data-original-title="<b>Before relabeling:</b>{{range $k, $v := .MetaLabels}}<br>{{$k | html | html}}=&quot;{{$v | html | html}}&quot;{{end}}">
{{$labels := stripLabels .Labels "job" "instance"}}
{{range $label, $value := $labels}}
<span class="label label-primary">{{$label}}="{{$value}}"</span>
{{else}}
<span class="label label-default">none</span>
{{end}}
</span>
</td>
<td>
{{if .LastScrape.IsZero}}Never{{else}}{{since .LastScrape}} ago{{end}}
</td>
<td>
{{if .LastError}}
<span class="alert alert-danger state_indicator">{{.LastError}}</span>
{{end}}
</td>
</tr>
{{end}}
</tbody>
{{end}}
</table>
</div>
{{end}}

View file

@ -53,20 +53,23 @@ var localhostRepresentations = []string{"127.0.0.1", "localhost"}
// Handler serves various HTTP endpoints of the Prometheus server // Handler serves various HTTP endpoints of the Prometheus server
type Handler struct { type Handler struct {
ruleManager *rules.Manager targetManager *retrieval.TargetManager
queryEngine *promql.Engine ruleManager *rules.Manager
storage local.Storage queryEngine *promql.Engine
storage local.Storage
apiV1 *v1.API apiV1 *v1.API
apiLegacy *legacy.API apiLegacy *legacy.API
router *route.Router router *route.Router
listenErrCh chan error listenErrCh chan error
quitCh chan struct{} quitCh chan struct{}
reloadCh chan struct{} reloadCh chan struct{}
options *Options options *Options
statusInfo *PrometheusStatus configString string
versionInfo *PrometheusVersion versionInfo *PrometheusVersion
birth time.Time
flagsMap map[string]string
externalLabels model.LabelSet externalLabels model.LabelSet
mtx sync.RWMutex mtx sync.RWMutex
@ -79,38 +82,12 @@ func (h *Handler) ApplyConfig(conf *config.Config) bool {
defer h.mtx.Unlock() defer h.mtx.Unlock()
h.externalLabels = conf.GlobalConfig.ExternalLabels h.externalLabels = conf.GlobalConfig.ExternalLabels
h.configString = conf.String()
return true return true
} }
// PrometheusStatus contains various information about the status // PrometheusVersion contains build information about Prometheus.
// of the running Prometheus process.
type PrometheusStatus struct {
Birth time.Time
Flags map[string]string
Config string
// A function that returns the current scrape targets pooled
// by their job name.
TargetPools func() map[string]retrieval.Targets
// A function that returns all loaded rules.
Rules func() []rules.Rule
mu sync.RWMutex
}
// ApplyConfig updates the status state as the new config requires.
// Returns true on success.
func (s *PrometheusStatus) ApplyConfig(conf *config.Config) bool {
s.mu.Lock()
defer s.mu.Unlock()
s.Config = conf.String()
return true
}
// PrometheusVersion contains various build information about Prometheus
type PrometheusVersion struct { type PrometheusVersion struct {
Version string `json:"version"` Version string `json:"version"`
Revision string `json:"revision"` Revision string `json:"revision"`
@ -133,7 +110,15 @@ type Options struct {
} }
// New initializes a new web Handler. // New initializes a new web Handler.
func New(st local.Storage, qe *promql.Engine, rm *rules.Manager, status *PrometheusStatus, version *PrometheusVersion, o *Options) *Handler { func New(
st local.Storage,
qe *promql.Engine,
tm *retrieval.TargetManager,
rm *rules.Manager,
version *PrometheusVersion,
flags map[string]string,
o *Options,
) *Handler {
router := route.New() router := route.New()
h := &Handler{ h := &Handler{
@ -142,12 +127,14 @@ func New(st local.Storage, qe *promql.Engine, rm *rules.Manager, status *Prometh
quitCh: make(chan struct{}), quitCh: make(chan struct{}),
reloadCh: make(chan struct{}), reloadCh: make(chan struct{}),
options: o, options: o,
statusInfo: status,
versionInfo: version, versionInfo: version,
birth: time.Now(),
flagsMap: flags,
ruleManager: rm, targetManager: tm,
queryEngine: qe, ruleManager: rm,
storage: st, queryEngine: qe,
storage: st,
apiV1: v1.NewAPI(qe, st), apiV1: v1.NewAPI(qe, st),
apiLegacy: &legacy.API{ apiLegacy: &legacy.API{
@ -171,10 +158,14 @@ func New(st local.Storage, qe *promql.Engine, rm *rules.Manager, status *Prometh
router.Get("/", func(w http.ResponseWriter, r *http.Request) { router.Get("/", func(w http.ResponseWriter, r *http.Request) {
router.Redirect(w, r, "/graph", http.StatusFound) router.Redirect(w, r, "/graph", http.StatusFound)
}) })
router.Get("/graph", instrf("graph", h.graph))
router.Get("/status", instrf("status", h.status))
router.Get("/alerts", instrf("alerts", h.alerts)) router.Get("/alerts", instrf("alerts", h.alerts))
router.Get("/graph", instrf("graph", h.graph))
router.Get("/status", instrf("status", h.status))
router.Get("/flags", instrf("flags", h.flags))
router.Get("/config", instrf("config", h.config))
router.Get("/rules", instrf("rules", h.rules))
router.Get("/targets", instrf("targets", h.targets))
router.Get("/version", instrf("version", h.version)) router.Get("/version", instrf("version", h.version))
router.Get("/heap", instrf("heap", dumpHeap)) router.Get("/heap", instrf("heap", dumpHeap))
@ -322,18 +313,34 @@ func (h *Handler) graph(w http.ResponseWriter, r *http.Request) {
} }
func (h *Handler) status(w http.ResponseWriter, r *http.Request) { func (h *Handler) status(w http.ResponseWriter, r *http.Request) {
h.statusInfo.mu.RLock()
defer h.statusInfo.mu.RUnlock()
h.executeTemplate(w, "status.html", struct { h.executeTemplate(w, "status.html", struct {
Status *PrometheusStatus Birth time.Time
Version *PrometheusVersion Version *PrometheusVersion
}{ }{
Status: h.statusInfo, Birth: h.birth,
Version: h.versionInfo, Version: h.versionInfo,
}) })
} }
func (h *Handler) flags(w http.ResponseWriter, r *http.Request) {
h.executeTemplate(w, "flags.html", h.flagsMap)
}
func (h *Handler) config(w http.ResponseWriter, r *http.Request) {
h.mtx.RLock()
defer h.mtx.RUnlock()
h.executeTemplate(w, "config.html", h.configString)
}
func (h *Handler) rules(w http.ResponseWriter, r *http.Request) {
h.executeTemplate(w, "rules.html", h.ruleManager)
}
func (h *Handler) targets(w http.ResponseWriter, r *http.Request) {
h.executeTemplate(w, "targets.html", h.targetManager)
}
func (h *Handler) version(w http.ResponseWriter, r *http.Request) { func (h *Handler) version(w http.ResponseWriter, r *http.Request) {
dec := json.NewEncoder(w) dec := json.NewEncoder(w)
if err := dec.Encode(h.versionInfo); err != nil { if err := dec.Encode(h.versionInfo); err != nil {