mirror of
https://github.com/prometheus/prometheus.git
synced 2025-01-12 06:17:27 -08:00
web/api: add targets endpoint
This commit is contained in:
parent
8f8cea4fbd
commit
33b583d50e
|
@ -45,7 +45,7 @@ const (
|
|||
// Target refers to a singular HTTP or HTTPS endpoint.
|
||||
type Target struct {
|
||||
// Labels before any processing.
|
||||
metaLabels model.LabelSet
|
||||
discoveredLabels model.LabelSet
|
||||
// Any labels that are added to this target and its metrics.
|
||||
labels model.LabelSet
|
||||
// Additional URL parmeters that are part of the target URL.
|
||||
|
@ -58,12 +58,12 @@ type Target struct {
|
|||
}
|
||||
|
||||
// NewTarget creates a reasonably configured target for querying.
|
||||
func NewTarget(labels, metaLabels model.LabelSet, params url.Values) *Target {
|
||||
func NewTarget(labels, discoveredLabels model.LabelSet, params url.Values) *Target {
|
||||
return &Target{
|
||||
labels: labels,
|
||||
metaLabels: metaLabels,
|
||||
params: params,
|
||||
health: HealthUnknown,
|
||||
labels: labels,
|
||||
discoveredLabels: discoveredLabels,
|
||||
params: params,
|
||||
health: HealthUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,9 +144,9 @@ func (t *Target) Labels() model.LabelSet {
|
|||
return lset
|
||||
}
|
||||
|
||||
// MetaLabels returns a copy of the target's labels before any processing.
|
||||
func (t *Target) MetaLabels() model.LabelSet {
|
||||
return t.metaLabels.Clone()
|
||||
// DiscoveredLabels returns a copy of the target's labels before any processing.
|
||||
func (t *Target) DiscoveredLabels() model.LabelSet {
|
||||
return t.discoveredLabels.Clone()
|
||||
}
|
||||
|
||||
// URL returns a copy of the target's URL.
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
"github.com/prometheus/prometheus/retrieval"
|
||||
"github.com/prometheus/prometheus/storage/local"
|
||||
"github.com/prometheus/prometheus/storage/metric"
|
||||
"github.com/prometheus/prometheus/util/httputil"
|
||||
|
@ -66,6 +67,10 @@ func (e *apiError) Error() string {
|
|||
return fmt.Sprintf("%s: %s", e.typ, e.err)
|
||||
}
|
||||
|
||||
type targetRetriever interface {
|
||||
Targets() []retrieval.Target
|
||||
}
|
||||
|
||||
type response struct {
|
||||
Status status `json:"status"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
|
@ -88,17 +93,20 @@ type API struct {
|
|||
Storage local.Storage
|
||||
QueryEngine *promql.Engine
|
||||
|
||||
targetRetriever targetRetriever
|
||||
|
||||
context func(r *http.Request) context.Context
|
||||
now func() model.Time
|
||||
}
|
||||
|
||||
// NewAPI returns an initialized API type.
|
||||
func NewAPI(qe *promql.Engine, st local.Storage) *API {
|
||||
func NewAPI(qe *promql.Engine, st local.Storage, tr targetRetriever) *API {
|
||||
return &API{
|
||||
QueryEngine: qe,
|
||||
Storage: st,
|
||||
context: route.Context,
|
||||
now: model.Now,
|
||||
QueryEngine: qe,
|
||||
Storage: st,
|
||||
targetRetriever: tr,
|
||||
context: route.Context,
|
||||
now: model.Now,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,6 +137,8 @@ func (api *API) Register(r *route.Router) {
|
|||
|
||||
r.Get("/series", instr("series", api.series))
|
||||
r.Del("/series", instr("drop_series", api.dropSeries))
|
||||
|
||||
r.Get("/targets", instr("targets", api.targets))
|
||||
}
|
||||
|
||||
type queryData struct {
|
||||
|
@ -328,6 +338,43 @@ func (api *API) dropSeries(r *http.Request) (interface{}, *apiError) {
|
|||
return res, nil
|
||||
}
|
||||
|
||||
type Target struct {
|
||||
// Labels before any processing.
|
||||
DiscoveredLabels model.LabelSet `json:"discoveredLabels"`
|
||||
// Any labels that are added to this target and its metrics.
|
||||
Labels model.LabelSet `json:"labels"`
|
||||
|
||||
ScrapeUrl string `json:"scrapeUrl"`
|
||||
|
||||
LastError string `json:"lastError"`
|
||||
LastScrape time.Time `json:"lastScrape"`
|
||||
Health retrieval.TargetHealth `json:"health"`
|
||||
}
|
||||
|
||||
func (api *API) targets(r *http.Request) (interface{}, *apiError) {
|
||||
targets := api.targetRetriever.Targets()
|
||||
res := make([]*Target, len(targets))
|
||||
|
||||
for i, t := range targets {
|
||||
lastErrStr := ""
|
||||
lastErr := t.LastError()
|
||||
if lastErr != nil {
|
||||
lastErrStr = lastErr.Error()
|
||||
}
|
||||
|
||||
res[i] = &Target{
|
||||
DiscoveredLabels: t.DiscoveredLabels(),
|
||||
Labels: t.Labels(),
|
||||
ScrapeUrl: t.URL().String(),
|
||||
LastError: lastErrStr,
|
||||
LastScrape: t.LastScrape(),
|
||||
Health: t.Health(),
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func respond(w http.ResponseWriter, data interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
|
|
@ -30,8 +30,15 @@ import (
|
|||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
"github.com/prometheus/prometheus/retrieval"
|
||||
)
|
||||
|
||||
type targetRetrieverFunc func() []retrieval.Target
|
||||
|
||||
func (f targetRetrieverFunc) Targets() []retrieval.Target {
|
||||
return f()
|
||||
}
|
||||
|
||||
func TestEndpoints(t *testing.T) {
|
||||
suite, err := promql.NewTest(t, `
|
||||
load 1m
|
||||
|
@ -49,10 +56,26 @@ func TestEndpoints(t *testing.T) {
|
|||
}
|
||||
|
||||
now := model.Now()
|
||||
|
||||
tr := targetRetrieverFunc(func() []retrieval.Target {
|
||||
return []retrieval.Target{
|
||||
*retrieval.NewTarget(
|
||||
model.LabelSet{
|
||||
model.SchemeLabel: "http",
|
||||
model.AddressLabel: "example.com:8080",
|
||||
model.MetricsPathLabel: "/metrics",
|
||||
},
|
||||
model.LabelSet{},
|
||||
url.Values{},
|
||||
),
|
||||
}
|
||||
})
|
||||
|
||||
api := &API{
|
||||
Storage: suite.Storage(),
|
||||
QueryEngine: suite.QueryEngine(),
|
||||
now: func() model.Time { return now },
|
||||
Storage: suite.Storage(),
|
||||
QueryEngine: suite.QueryEngine(),
|
||||
targetRetriever: tr,
|
||||
now: func() model.Time { return now },
|
||||
}
|
||||
|
||||
start := model.Time(0)
|
||||
|
@ -404,6 +427,16 @@ func TestEndpoints(t *testing.T) {
|
|||
response: struct {
|
||||
NumDeleted int `json:"numDeleted"`
|
||||
}{2},
|
||||
}, {
|
||||
endpoint: api.targets,
|
||||
response: []*Target{
|
||||
&Target{
|
||||
DiscoveredLabels: model.LabelSet{},
|
||||
Labels: model.LabelSet{},
|
||||
ScrapeUrl: "http://example.com:8080/metrics",
|
||||
Health: "unknown",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -245,7 +245,7 @@ func webUiTemplatesStatusHtml() (*asset, error) {
|
|||
return a, nil
|
||||
}
|
||||
|
||||
var _webUiTemplatesTargetsHtml = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x55\xcd\x8e\xdb\x36\x10\xbe\xfb\x29\xa6\xec\xa2\xa7\xc8\x02\x02\xf4\xb2\xa5\x74\x68\x11\x20\x05\xb6\x45\x9a\x4d\x2e\xbd\x04\x94\x38\x96\xb8\xe1\x92\x2a\x39\x32\xb2\x60\xf8\xee\x05\x29\xc9\xb1\x65\xbb\x48\xba\x40\x7c\xa0\x35\xff\x7f\xdf\x90\x21\x48\xdc\x29\x83\xc0\x7a\x14\x92\xc5\xc8\x7f\x28\x0a\x30\xea\x13\x14\x45\x1d\x02\x1a\x19\xe3\x66\xf3\x45\xab\xb5\x86\xd0\x10\x8b\x71\x03\xc0\xa5\xda\x43\xab\x85\xf7\x55\x16\x08\x65\xd0\x15\x3b\x3d\x2a\xc9\xea\x0d\x00\x00\xef\x5f\x82\x92\x15\x23\xe1\x3a\x24\xcf\xea\x77\xd3\x07\x2f\xfb\x97\x93\x06\x00\x27\xd1\x68\x5c\xfc\x4c\x44\x3e\x8b\xd6\x1a\x89\xc6\xa3\x9c\xe9\xc6\x3a\x89\xee\x40\x7a\x72\x6a\x38\x50\xbd\xdd\xa3\x63\x8b\x53\x80\x10\x9c\x30\x1d\xc2\xcd\x83\x6d\x5e\xc0\xcd\x60\xad\x86\xdb\x0a\xb6\x53\x06\x6f\xac\xd5\x1e\x72\x15\xcb\x8f\x53\x6a\x41\x7d\xc4\x49\x3c\x57\x73\xea\xa1\xb5\xda\x0f\xc2\x54\xec\x67\xb6\x24\xfa\x60\x9b\x0f\xc9\x20\x05\xe5\x22\x57\xf9\x60\x9b\x22\x84\x14\x30\x46\x06\xbd\xc3\x5d\xc5\x7e\x3c\x61\xd6\xcb\x17\x2f\x45\xcd\x4b\xea\xd3\xe1\xce\x63\x9e\x30\x72\x6a\xf5\x2b\x23\x07\xab\x0c\x65\xab\x0b\xf2\x7b\x12\x84\xd7\x84\x77\xa2\x41\xed\xaf\x4b\x3d\xc1\x7d\xeb\xc4\x70\xd5\xc1\x2b\xe7\xac\x3b\x17\xae\xb3\x4f\x1a\xab\x26\x72\x6a\xac\x7c\x3a\xe6\x1c\x26\x93\x66\x72\x32\x82\x2b\xc5\xcb\x33\x96\x98\xbb\x1b\xc2\xf6\xfd\xdb\x3b\xf8\x0c\x9d\xb6\x8d\xd0\xef\xdf\xde\x4d\x4d\x4e\xdc\xed\x7d\xdb\xe3\x23\xc6\x78\x5b\x96\x33\xe7\xb5\xf5\x14\xe3\x4c\xbc\x11\xd4\xcf\x83\x68\xce\x82\x1e\x65\xa9\x53\xef\x5e\xc0\xcd\x5e\xe8\x11\x7d\xc6\x50\x32\xff\x6b\x44\xf7\x04\xab\xf4\x57\xa6\x6a\x31\x4b\x56\xb3\x83\x8b\x16\x00\x3c\xe1\x6b\xc1\x56\x0e\x09\xf9\x2c\x06\xa7\x1e\x85\x7b\xca\xd0\xc9\x9c\x18\x53\xdd\x93\xb7\x18\x19\x2f\x93\xe5\x79\xfe\x29\x8d\x69\x7d\xbf\x8e\xcf\xcb\x0b\x7d\x3e\x67\xad\x32\x15\x1a\x1d\x41\x3e\x8b\x10\x60\xfb\x1a\x85\xa6\x1e\x3e\x43\x9f\x3f\xde\xd9\xdf\x92\x1e\xc4\x08\x3e\xe1\xf3\x83\x32\x52\xb5\x82\xac\x03\xc2\x4f\x54\x8c\xc3\x80\xae\x15\x1e\xd9\xe5\x02\x66\x7f\x17\x8a\xb8\x5c\xf6\xff\x2b\xa2\x1d\x9d\xb7\xae\xc8\xeb\x85\x8e\x81\x14\x24\x0a\xb2\x5d\xa7\xb1\x62\x64\xad\x26\x35\x30\x20\x45\x89\x9e\xc5\x3d\x3d\xea\x8a\xdc\x88\x13\x69\x9d\xea\x94\x11\xba\x98\xb5\x78\x53\xff\x8a\x3b\xeb\x10\x1c\xe6\xa9\x29\xd3\xdd\xf2\xb2\xa9\x0f\xd8\xf8\x98\xb0\x91\xd1\xf4\x07\x92\x98\x16\x34\xc6\x04\xc5\x10\x6e\x3e\xa6\x0e\xd2\xa3\x9e\xff\x62\xac\x7e\xfa\x67\xb4\xf4\x4b\x9a\xfb\x5a\xb4\x48\xf2\x50\xaf\xf4\x71\x42\x4e\x06\x6f\xbe\x35\xa7\x70\xb0\x9d\xff\xd3\xcd\xc5\xfe\x1b\xca\x27\x5b\x90\xe1\xac\xe7\x94\xbf\x23\x9c\xb5\xc7\x6f\x8d\x27\x71\x27\x46\x4d\xac\x36\xd6\xe0\xb7\xef\xca\x33\x61\x16\x82\xda\xa5\x2e\x7b\x9a\xae\xd7\xed\xef\xfe\x6f\x74\x36\xc6\x3f\x71\x8f\x6e\xa9\x28\x04\xaf\x4c\x8b\xc7\x8a\x31\x82\xe8\xec\x33\x37\xf5\x4b\xf4\x7c\x7d\x5f\x2a\xef\xda\x2e\xcb\x34\x75\xb7\x5e\xda\x7c\xb5\x1e\xf9\xbb\xd6\xcf\xaf\xcd\x7b\xfd\x7c\x9c\xdb\xf1\x72\xf5\x7c\x9c\xaa\xf0\x32\x3f\xfe\x49\xcc\x4b\xa9\xf6\xf5\x66\x91\xff\x1b\x00\x00\xff\xff\xbc\x7a\x79\x3b\xd9\x08\x00\x00")
|
||||
var _webUiTemplatesTargetsHtml = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xbc\x56\xcd\x8e\xdb\x36\x10\xbe\xfb\x29\xa6\xec\xa2\xa7\xc8\x02\x02\xf4\xb2\xa5\x74\x68\x1b\x20\x05\x16\x45\x9a\x4d\x2e\xbd\x04\x94\x38\x96\xb8\xe1\x92\x2a\x39\x32\xb2\x60\xf8\xee\x05\x29\xc9\xb1\x65\xbb\x48\xba\x40\x7c\xa0\x34\xff\x7f\xdf\x50\x0e\x41\xe2\x4e\x19\x04\xd6\xa3\x90\x2c\x46\xfe\x43\x51\x80\x51\x9f\xa0\x28\xea\x10\xd0\xc8\x18\x37\x9b\x2f\x5a\xad\x35\x84\x86\x58\x8c\x1b\x00\x2e\xd5\x1e\x5a\x2d\xbc\xaf\xb2\x40\x28\x83\xae\xd8\xe9\x51\x49\x56\x6f\x00\x00\x78\xff\x12\x94\xac\x18\x09\xd7\x21\x79\x56\xbf\x9b\x5e\x78\xd9\xbf\x9c\x34\x00\x38\x89\x46\xe3\xe2\x67\x22\xf2\x59\xb4\xd6\x48\x34\x1e\xe5\x4c\x37\xd6\x49\x74\x07\xd2\x93\x53\xc3\x81\xea\xed\x1e\x1d\x5b\x9c\x02\x84\xe0\x84\xe9\x10\x6e\x1e\x6c\xf3\x02\x6e\x06\x6b\x35\xdc\x56\xb0\x9d\x32\x78\x63\xad\xf6\x90\xab\x58\x7e\x9c\x52\x0b\xea\x23\x4e\xe2\xb9\x9a\x53\x0f\xad\xd5\x7e\x10\xa6\x62\x3f\xb3\x25\xd1\x07\xdb\x7c\x48\x06\x29\x28\x17\xb9\xca\x07\xdb\x14\x21\xa4\x80\x31\x32\xe8\x1d\xee\x2a\xf6\xe3\x09\xb3\x5e\xde\x78\x29\x6a\x5e\x52\x9f\x0e\x77\x1e\xf3\x84\x91\x53\xab\x5f\x19\x39\x58\x65\x28\x5b\x5d\x90\xdf\x93\x20\xbc\x26\xbc\x13\x0d\x6a\x7f\x5d\xea\x09\xee\x5b\x27\x86\xab\x0e\x5e\x39\x67\xdd\xb9\x70\x9d\x7d\xd2\x58\x35\x91\x53\x63\xe5\xd3\x31\xe7\x30\x99\x34\x93\x93\x11\x5c\x29\x5e\x9e\xb1\xc4\xdc\xdd\x10\xb6\xef\xdf\xde\xc1\x67\xe8\xb4\x6d\x84\x7e\xff\xf6\x6e\x6a\x72\xe2\x6e\xef\xdb\x1e\x1f\x31\xc6\xdb\xb2\x9c\x39\xaf\xad\xa7\x18\x67\xe2\x8d\xa0\x7e\x1e\x44\x73\x16\xf4\x28\x4b\x9d\x7a\xf7\x02\x6e\xf6\x42\x8f\xe8\x33\x86\x92\xf9\x5f\x23\xba\x27\x58\xa5\xbf\x32\x55\x8b\x59\xb2\x9a\x1d\x5c\xb4\x00\xe0\x09\x5f\x0b\xb6\x72\x48\xc8\x67\x31\x38\xf5\x28\xdc\x53\x86\x4e\xe6\xc4\x98\xea\x9e\xbc\xc5\xc8\x78\x99\x2c\xcf\xf3\x4f\x69\x4c\xeb\xfb\x75\x7c\x5e\x5e\xe8\xf3\x39\x6b\x95\xa9\xd0\xe8\x08\xf2\x59\x84\x00\xdb\xd7\x28\x34\xf5\xf0\x19\xfa\xfc\xf2\xce\xfe\x96\xf4\x20\x46\xf0\x09\x9f\x1f\x94\x91\xaa\x15\x64\x1d\x10\x7e\xa2\x62\x1c\x06\x74\xad\xf0\xc8\x2e\x17\x30\xfb\xbb\x50\xc4\xe5\xb2\xff\x5f\x11\xed\xe8\xbc\x75\x45\x5e\x2f\x74\x0c\xa4\x20\x51\x90\xed\x3a\x8d\x15\x23\x6b\x35\xa9\x81\x01\x29\x4a\xf4\x2c\xee\xe9\x51\x57\xe4\x46\x9c\x48\xeb\x54\xa7\x8c\xd0\xc5\xac\xc5\x9b\xfa\x57\xdc\x59\x87\xe0\x30\x4f\x4d\x99\xee\x96\x97\x4d\x7d\xc0\xc6\xc7\x84\x8d\x8c\xa6\xdf\x95\x6f\xd3\xe5\x85\x72\x5a\xd3\x18\x13\x20\x43\xb8\xf9\x98\xfa\x48\x8f\x7a\x7e\xc4\x58\xfd\xf4\xcf\x68\xe9\x97\x34\xfd\xb5\x68\x91\xe4\xd1\x5e\xe9\xe6\x84\x9f\x0c\xe1\x7c\x77\x4e\xe1\x60\x3b\x3f\xd3\xfd\xc5\xfe\x1b\xd0\x27\xbb\x90\x41\xad\xe7\x94\xbf\x23\xa8\xb5\xc7\x6f\x8d\x27\x71\x27\x46\x4d\xac\x36\xd6\xe0\xb7\x6f\xcc\x33\xc1\x16\x82\xda\xa5\x2e\x7b\x9a\x2e\xd9\xed\x1f\xfe\x6f\x74\x36\xc6\x3f\x71\x8f\x6e\xa9\x28\x04\xaf\x4c\x8b\xc7\x8a\x31\x82\xe8\xec\x33\xf7\xf5\x4b\xf4\x7c\x89\x5f\x2a\xef\xda\x46\xcb\x34\x75\xb7\x5e\xdd\x7c\xc1\x1e\xf9\xbb\xd6\xcf\xaf\xcd\x7b\xfd\x11\x39\xb7\xe3\xe5\xea\x23\x72\xaa\xc2\xcb\xfc\x17\x20\x89\x79\x29\xd5\xbe\xde\x2c\xf2\x7f\x03\x00\x00\xff\xff\x46\xf7\xdd\x21\xdf\x08\x00\x00")
|
||||
|
||||
func webUiTemplatesTargetsHtmlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
|
@ -260,7 +260,7 @@ func webUiTemplatesTargetsHtml() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "web/ui/templates/targets.html", size: 2265, mode: os.FileMode(420), modTime: time.Unix(1480677405, 0)}
|
||||
info := bindataFileInfo{name: "web/ui/templates/targets.html", size: 2271, mode: os.FileMode(420), modTime: time.Unix(1480939468, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
</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}}="{{$v | html | html}}"{{end}}">
|
||||
<span class="cursor-pointer" data-toggle="tooltip" title="" data-html=true data-original-title="<b>Before relabeling:</b>{{range $k, $v := .DiscoveredLabels}}<br>{{$k | html | html}}="{{$v | html | html}}"{{end}}">
|
||||
{{$labels := stripLabels .Labels "job"}}
|
||||
{{range $label, $value := $labels}}
|
||||
<span class="label label-primary">{{$label}}="{{$value}}"</span>
|
||||
|
|
|
@ -144,7 +144,7 @@ func New(o *Options) *Handler {
|
|||
storage: o.Storage,
|
||||
notifier: o.Notifier,
|
||||
|
||||
apiV1: api_v1.NewAPI(o.QueryEngine, o.Storage),
|
||||
apiV1: api_v1.NewAPI(o.QueryEngine, o.Storage, o.TargetManager),
|
||||
now: model.Now,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue