mirror of
https://github.com/prometheus/prometheus
synced 2024-12-27 00:53:12 +00:00
Merge pull request #118 from prometheus/feature/navigation
Add navigation bar to web ui
This commit is contained in:
commit
ca2a31c48b
26
web/handler.go
Normal file
26
web/handler.go
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2013 Prometheus Team
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func graphHandler(w http.ResponseWriter, r *http.Request) {
|
||||
executeTemplate(w, "graph", nil)
|
||||
}
|
||||
|
||||
func consoleHandler(w http.ResponseWriter, r *http.Request) {
|
||||
executeTemplate(w, "console", nil)
|
||||
}
|
@ -2,8 +2,8 @@ body {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
color: #333333;
|
||||
background-color: #eee;
|
||||
color: #333;
|
||||
background-color: #f2f2f2;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
@ -49,3 +49,15 @@ input[name=end_input], input[name=range_input] {
|
||||
margin-left: -4px;
|
||||
margin-right: -4px;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
font-size: 20pt;
|
||||
background-color: #333;
|
||||
color: #F2F2F2;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
#navigation a { padding-right: 1em; text-decoration: none }
|
||||
#navigation a:visited { color: #F2F2F2 }
|
||||
#navigation a:active { color: #F60 }
|
||||
#navigation a:hover { color: #06C }
|
||||
|
@ -1,79 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Prometheus Expression Browser</title>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.1/jquery-ui.min.js"></script>
|
||||
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.1/themes/base/jquery-ui.css" />
|
||||
|
||||
<link type="text/css" rel="stylesheet" href="css/prometheus.css">
|
||||
<link type="text/css" rel="stylesheet" href="css/graph.css">
|
||||
|
||||
<script src="vendor/jquery-simple-datetimepicker/jquery.simple-dtpicker.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="vendor/jquery-simple-datetimepicker/jquery.simple-dtpicker.css">
|
||||
|
||||
<!-- copy all these CSSen/JSen to our own location -->
|
||||
<link type="text/css" rel="stylesheet" href="http://code.shutterstock.com/rickshaw/rickshaw.min.css">
|
||||
<script src="http://code.shutterstock.com/rickshaw/vendor/d3.min.js"></script>
|
||||
<script src="http://code.shutterstock.com/rickshaw/vendor/d3.layout.min.js"></script>
|
||||
<script src="http://code.shutterstock.com/rickshaw/rickshaw.min.js"></script>
|
||||
|
||||
<script src="vendor/js/handlebars.js"></script>
|
||||
<script src="vendor/js/jquery.selection.js"></script>
|
||||
<script src="vendor/js/jquery.hotkeys.js"></script>
|
||||
|
||||
<script src="js/graph.js"></script>
|
||||
|
||||
<script id="graph_template" type="text/x-handlebars-template">
|
||||
<div id="graph_wrapper{{id}}">
|
||||
<div class="grouping_box">
|
||||
<form action="/api/query_range" method="GET" class="query_form">
|
||||
<div class="head">
|
||||
<div class="expr">
|
||||
<input placeholder="Expression" type="text" name="expr" id="expr{{id}}" size="80" value="{{expr}}">
|
||||
<select name="insert_metric">
|
||||
<option value="">- Insert Metric -</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="config">
|
||||
<label for="range_input{{id}}">Range:</label>
|
||||
<input type="button" value="-" name="dec_range">
|
||||
<input type="text" name="range_input" id="range_input{{id}}" size="2" value="{{range_input}}">
|
||||
<input type="hidden" name="range">
|
||||
<input type="button" value="+" name="inc_range">
|
||||
|
||||
<input type="button" value="←" name="dec_end">
|
||||
<input placeholder="Until" type="text" name="end_input" id="end{{id}}" size="16" value="{{end}}">
|
||||
<input type="button" value="→" name="inc_end">
|
||||
<input type="hidden" name="end">
|
||||
|
||||
<input placeholder="Resolution (s)" type="text" name="step_input" id="step_input{{id}}" value="{{step_input}}" size="10">
|
||||
<input type="hidden" name="step">
|
||||
<span class="opts">
|
||||
<input type="checkbox" name="stacked" id="stacked{{id}}" {{stacked_checked}}>
|
||||
<label for="stacked{{id}}">Stacked</label>
|
||||
<input type="submit" value="Graph" name="submit">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="eval_stats"></div>
|
||||
</form>
|
||||
<img src="img/ajax-loader.gif" class="spinner" alt="ajax_spinner">
|
||||
</div>
|
||||
<div class="grouping_box">
|
||||
<div class="graph_container">
|
||||
<div class="graph"></div>
|
||||
<div class="legend"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="graph_container">
|
||||
</div>
|
||||
<div><input type="button" value="Add Graph" id="add_graph"></div>
|
||||
</body>
|
||||
</html>
|
@ -20,6 +20,7 @@
|
||||
|
||||
var Prometheus = Prometheus || {};
|
||||
var graphs = [];
|
||||
var graphTemplate;
|
||||
|
||||
Prometheus.Graph = function(element, options) {
|
||||
this.el = element;
|
||||
@ -57,9 +58,8 @@ Prometheus.Graph.prototype.initialize = function() {
|
||||
self.options['stacked_checked'] = self.options['stacked'] ? "checked" : "";
|
||||
|
||||
// Draw graph controls and container from Handlebars template.
|
||||
var source = $("#graph_template").html();
|
||||
var template = Handlebars.compile(source);
|
||||
var graphHtml = template(self.options);
|
||||
|
||||
var graphHtml = graphTemplate(self.options);
|
||||
self.el.append(graphHtml);
|
||||
|
||||
// Get references to all the interesting elements in the graph container and
|
||||
@ -428,15 +428,20 @@ function init() {
|
||||
cache: false
|
||||
});
|
||||
|
||||
var options = parseGraphOptionsFromUrl();
|
||||
if (options.length == 0) {
|
||||
options.push({});
|
||||
}
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
addGraph(options[i]);
|
||||
}
|
||||
|
||||
$("#add_graph").click(function() { addGraph({}); });
|
||||
$.ajax({
|
||||
url: "/static/js/graph_template.handlebar",
|
||||
success: function(data) {
|
||||
graphTemplate = Handlebars.compile(data);
|
||||
var options = parseGraphOptionsFromUrl();
|
||||
if (options.length == 0) {
|
||||
options.push({});
|
||||
}
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
addGraph(options[i]);
|
||||
}
|
||||
$("#add_graph").click(function() { addGraph({}); });
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$(init);
|
||||
|
42
web/static/js/graph_template.handlebar
Normal file
42
web/static/js/graph_template.handlebar
Normal file
@ -0,0 +1,42 @@
|
||||
<div id="graph_wrapper{{id}}">
|
||||
<div class="grouping_box">
|
||||
<form action="/api/query_range" method="GET" class="query_form">
|
||||
<div class="head">
|
||||
<div class="expr">
|
||||
<input placeholder="Expression" type="text" name="expr" id="expr{{id}}" size="80" value="{{expr}}">
|
||||
<select name="insert_metric">
|
||||
<option value="">- Insert Metric -</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="config">
|
||||
<label for="range_input{{id}}">Range:</label>
|
||||
<input type="button" value="-" name="dec_range">
|
||||
<input type="text" name="range_input" id="range_input{{id}}" size="2" value="{{range_input}}" >
|
||||
<input type="hidden" name="range">
|
||||
<input type="button" value="+" name="inc_range">
|
||||
|
||||
<input type="button" value="←" name="dec_end">
|
||||
<input placeholder="Until" type="text" name="end_input" id="end{{id}}" size="16" value="{{end }}">
|
||||
<input type="button" value="→" name="inc_end">
|
||||
<input type="hidden" name="end">
|
||||
|
||||
<input placeholder="Resolution (s)" type="text" name="step_input" id="step_input{{id}}" value ="{{step_input}}" size="10">
|
||||
<input type="hidden" name="step">
|
||||
<span class="opts">
|
||||
<input type="checkbox" name="stacked" id="stacked{{id}}" {{stacked_checked}}>
|
||||
<label for="stacked{{id}}">Stacked</label>
|
||||
<input type="submit" value="Graph" name="submit">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="eval_stats"></div>
|
||||
</form>
|
||||
<img src="img/ajax-loader.gif" class="spinner" alt="ajax_spinner">
|
||||
</div>
|
||||
<div class="grouping_box">
|
||||
<div class="graph_container">
|
||||
<div class="graph"></div>
|
||||
<div class="legend"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -16,9 +16,6 @@ package web
|
||||
import (
|
||||
"github.com/prometheus/prometheus/appstate"
|
||||
"github.com/prometheus/prometheus/retrieval"
|
||||
"github.com/prometheus/prometheus/web/blob"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@ -40,18 +37,5 @@ func (h *StatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
Status: "TODO: add status information here",
|
||||
TargetPools: h.appState.TargetManager.Pools(),
|
||||
}
|
||||
|
||||
var t *template.Template
|
||||
if *useLocalAssets {
|
||||
t, _ = template.ParseFiles("web/templates/status.html")
|
||||
} else {
|
||||
templateFile, err := blob.GetFile(blob.TemplateFiles, "status.html")
|
||||
if err != nil {
|
||||
log.Fatalf("Could not read template: %s", err)
|
||||
}
|
||||
|
||||
t, _ = template.New("status").Parse(string(templateFile))
|
||||
}
|
||||
|
||||
t.Execute(w, status)
|
||||
executeTemplate(w, "status", status)
|
||||
}
|
||||
|
19
web/templates/_base.html
Normal file
19
web/templates/_base.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Prometheus</title>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/prometheus.css">
|
||||
{{template "head" .}}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="navigation">
|
||||
<a href="/console">Console</a>
|
||||
<a href="/graph">Graph</a>
|
||||
<a href="/">Status</a>
|
||||
</div>
|
||||
{{template "content" .}}
|
||||
</body>
|
||||
</html>
|
@ -1,15 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Prometheus Expression Browser</title>
|
||||
<link type="text/css" rel="stylesheet" href="css/prometheus.css">
|
||||
<link type="text/css" rel="stylesheet" href="css/graph.css">
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
|
||||
{{define "head"}}
|
||||
<script src="js/exprBrowser.js"></script>
|
||||
</head>
|
||||
{{end}}
|
||||
|
||||
<body>
|
||||
{{define "content"}}
|
||||
<div class="grouping_box">
|
||||
<form action="/api/query" method="GET" id="queryForm">
|
||||
<label for="expr">Expression:</label>
|
||||
@ -23,5 +16,4 @@
|
||||
<div id="result"></div>
|
||||
</pre>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
30
web/templates/graph.html
Normal file
30
web/templates/graph.html
Normal file
@ -0,0 +1,30 @@
|
||||
{{define "head"}}
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.1/jquery-ui.min.js"></script>
|
||||
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.9.1/themes/base/jquery-ui.css" />
|
||||
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/prometheus.css">
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/graph.css">
|
||||
|
||||
<script src="/static/vendor/jquery-simple-datetimepicker/jquery.simple-dtpicker.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="/static/vendor/jquery-simple-datetimepicker/jquery.simple-dtpicker.css">
|
||||
|
||||
<!-- copy all these CSSen/JSen to our own location -->
|
||||
<link type="text/css" rel="stylesheet" href="http://code.shutterstock.com/rickshaw/rickshaw.min.css">
|
||||
<script src="http://code.shutterstock.com/rickshaw/vendor/d3.min.js"></script>
|
||||
<script src="http://code.shutterstock.com/rickshaw/vendor/d3.layout.min.js"></script>
|
||||
<script src="http://code.shutterstock.com/rickshaw/rickshaw.min.js"></script>
|
||||
|
||||
<script src="/static/vendor/js/handlebars.js"></script>
|
||||
<script src="/static/vendor/js/jquery.selection.js"></script>
|
||||
<script src="/static/vendor/js/jquery.hotkeys.js"></script>
|
||||
|
||||
<script src="/static/js/graph.js"></script>
|
||||
|
||||
<script id="graph_template" type="text/x-handlebars-template"></script>
|
||||
{{end}}
|
||||
|
||||
{{define "content"}}
|
||||
<div id="graph_container">
|
||||
</div>
|
||||
<div><input type="button" value="Add Graph" id="add_graph"></div>
|
||||
{{end}}
|
@ -1,14 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Prometheus Status</title>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="static/css/prometheus.css">
|
||||
</head>
|
||||
{{define "head"}}<!-- nix -->{{end}}
|
||||
|
||||
<body>
|
||||
<h2>Status</h2>
|
||||
{{define "content"}}
|
||||
<h2>Status</h2>
|
||||
<div class="grouping_box">
|
||||
{{.Status}}
|
||||
</div>
|
||||
@ -41,5 +34,4 @@
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
|
52
web/web.go
52
web/web.go
@ -16,10 +16,14 @@ package web
|
||||
import (
|
||||
"code.google.com/p/gorest"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/prometheus/client_golang"
|
||||
"github.com/prometheus/client_golang/exp"
|
||||
"github.com/prometheus/prometheus/appstate"
|
||||
"github.com/prometheus/prometheus/web/api"
|
||||
"github.com/prometheus/prometheus/web/blob"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
)
|
||||
@ -33,14 +37,50 @@ var (
|
||||
func StartServing(appState *appstate.ApplicationState) {
|
||||
gorest.RegisterService(api.NewMetricsService(appState))
|
||||
|
||||
http.Handle("/status", &StatusHandler{appState: appState})
|
||||
http.Handle("/api/", gorest.Handle())
|
||||
http.Handle("/metrics.json", registry.DefaultHandler)
|
||||
exp.Handle("/", &StatusHandler{appState: appState})
|
||||
exp.HandleFunc("/graph", graphHandler)
|
||||
exp.HandleFunc("/console", consoleHandler)
|
||||
|
||||
exp.Handle("/api/", gorest.Handle())
|
||||
exp.Handle("/metrics.json", registry.DefaultHandler)
|
||||
if *useLocalAssets {
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static"))))
|
||||
exp.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("web/static"))))
|
||||
} else {
|
||||
http.Handle("/static/", http.StripPrefix("/static/", new(blob.Handler)))
|
||||
exp.Handle("/static/", http.StripPrefix("/static/", new(blob.Handler)))
|
||||
}
|
||||
|
||||
go http.ListenAndServe(*listenAddress, nil)
|
||||
go http.ListenAndServe(*listenAddress, exp.DefaultCoarseMux)
|
||||
}
|
||||
|
||||
func getTemplate(name string) (t *template.Template, err error) {
|
||||
if *useLocalAssets {
|
||||
return template.ParseFiles("web/templates/_base.html", fmt.Sprintf("web/templates/%s.html", name))
|
||||
}
|
||||
|
||||
t = template.New("_base")
|
||||
|
||||
file, err := blob.GetFile(blob.TemplateFiles, "_base.html")
|
||||
if err != nil {
|
||||
log.Printf("Could not read base template: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
t.Parse(string(file))
|
||||
|
||||
file, err = blob.GetFile(blob.TemplateFiles, name+".html")
|
||||
if err != nil {
|
||||
log.Printf("Could not read %s template: %s", name, err)
|
||||
return nil, err
|
||||
}
|
||||
t.Parse(string(file))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func executeTemplate(w http.ResponseWriter, name string, data interface{}) {
|
||||
tpl, err := getTemplate(name)
|
||||
if err != nil {
|
||||
log.Printf("Error preparing layout template: %s", err)
|
||||
return
|
||||
}
|
||||
tpl.Execute(w, data)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user