mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-15 08:23:34 +01:00
app/vmagent: expose /api/v1/targets
page according to https://prometheus.io/docs/prometheus/latest/querying/api/#targets
This page is exposed by vmagent and by a single-node VictoriaMetrics Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/643
This commit is contained in:
parent
c4464594b7
commit
abdf22e0bb
@ -11,6 +11,8 @@
|
|||||||
See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/781
|
See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/781
|
||||||
* FEATURE: vmalert: add `-dryRun` command-line option for validating the provided config files without the need to start `vmalert` service.
|
* FEATURE: vmalert: add `-dryRun` command-line option for validating the provided config files without the need to start `vmalert` service.
|
||||||
* FEATURE: accept optional third argument of string type at `topk_*` and `bottomk_*` functions. This is label name for additional time series to return with the sum of time series outside top/bottom K. See [MetricsQL docs](https://victoriametrics.github.io/MetricsQL.html) for more details.
|
* FEATURE: accept optional third argument of string type at `topk_*` and `bottomk_*` functions. This is label name for additional time series to return with the sum of time series outside top/bottom K. See [MetricsQL docs](https://victoriametrics.github.io/MetricsQL.html) for more details.
|
||||||
|
* FEATURE: vmagent: expose `/api/v1/targets` page according to [the corresponding Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/#targets).
|
||||||
|
See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/643
|
||||||
|
|
||||||
* BUGFIX: vmagent: properly handle OpenStack endpoint ending with `v3.0` such as `https://ostack.example.com:5000/v3.0`
|
* BUGFIX: vmagent: properly handle OpenStack endpoint ending with `v3.0` such as `https://ostack.example.com:5000/v3.0`
|
||||||
in the same way as Prometheus does. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/728#issuecomment-709914803
|
in the same way as Prometheus does. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/728#issuecomment-709914803
|
||||||
|
@ -211,9 +211,13 @@ either via `vmagent` itself or via Prometheus, so the exported metrics could be
|
|||||||
Use official [Grafana dashboard](https://grafana.com/grafana/dashboards/12683) for `vmagent` state overview.
|
Use official [Grafana dashboard](https://grafana.com/grafana/dashboards/12683) for `vmagent` state overview.
|
||||||
If you have suggestions, improvements or found a bug - feel free to open an issue on github or add review to the dashboard.
|
If you have suggestions, improvements or found a bug - feel free to open an issue on github or add review to the dashboard.
|
||||||
|
|
||||||
`vmagent` also exports target statuses at `http://vmagent-host:8429/targets` page in plaintext format.
|
`vmagent` also exports target statuses at the following handlers:
|
||||||
`/targets` handler accepts optional `show_original_labels=1` query arg, which shows the original labels per each target
|
|
||||||
before applying relabeling. This information may be useful for debugging target relabeling.
|
* `http://vmagent-host:8429/targets`. This handler returns human-readable plaintext status for every active target.
|
||||||
|
This page is convenient to query from command line with `wget`, `curl` or similar tools.
|
||||||
|
It accepts optional `show_original_labels=1` query arg, which shows the original labels per each target before applying relabeling.
|
||||||
|
This information may be useful for debugging target relabeling.
|
||||||
|
* `http://vmagent-host:8429/api/v1/targets`. This handler returns data compatible with [the corresponding page from Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/#targets).
|
||||||
|
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
@ -224,7 +228,8 @@ before applying relabeling. This information may be useful for debugging target
|
|||||||
since `vmagent` establishes at least a single TCP connection per each target.
|
since `vmagent` establishes at least a single TCP connection per each target.
|
||||||
|
|
||||||
* When `vmagent` scrapes many unreliable targets, it can flood error log with scrape errors. These errors can be suppressed
|
* When `vmagent` scrapes many unreliable targets, it can flood error log with scrape errors. These errors can be suppressed
|
||||||
by passing `-promscrape.suppressScrapeErrors` command-line flag to `vmagent`. The most recent scrape error per each target can be observed at `http://vmagent-host:8429/targets`.
|
by passing `-promscrape.suppressScrapeErrors` command-line flag to `vmagent`. The most recent scrape error per each target can be observed at `http://vmagent-host:8429/targets`
|
||||||
|
and `http://vmagent-host:8429/api/v1/targets`.
|
||||||
|
|
||||||
* It is recommended to increase `-remoteWrite.queues` if `vmagent_remotewrite_pending_data_bytes` metric exported at `http://vmagent-host:8429/metrics` page constantly grows.
|
* It is recommended to increase `-remoteWrite.queues` if `vmagent_remotewrite_pending_data_bytes` metric exported at `http://vmagent-host:8429/metrics` page constantly grows.
|
||||||
|
|
||||||
|
@ -211,6 +211,12 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool {
|
|||||||
showOriginalLabels, _ := strconv.ParseBool(r.FormValue("show_original_labels"))
|
showOriginalLabels, _ := strconv.ParseBool(r.FormValue("show_original_labels"))
|
||||||
promscrape.WriteHumanReadableTargetsStatus(w, showOriginalLabels)
|
promscrape.WriteHumanReadableTargetsStatus(w, showOriginalLabels)
|
||||||
return true
|
return true
|
||||||
|
case "/api/v1/targets":
|
||||||
|
promscrapeAPIV1TargetsRequests.Inc()
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
state := r.FormValue("state")
|
||||||
|
promscrape.WriteAPIV1Targets(w, state)
|
||||||
|
return true
|
||||||
case "/-/reload":
|
case "/-/reload":
|
||||||
promscrapeConfigReloadRequests.Inc()
|
promscrapeConfigReloadRequests.Inc()
|
||||||
procutil.SelfSIGHUP()
|
procutil.SelfSIGHUP()
|
||||||
@ -241,7 +247,8 @@ var (
|
|||||||
|
|
||||||
influxQueryRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/query", protocol="influx"}`)
|
influxQueryRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/query", protocol="influx"}`)
|
||||||
|
|
||||||
promscrapeTargetsRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/targets"}`)
|
promscrapeTargetsRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/targets"}`)
|
||||||
|
promscrapeAPIV1TargetsRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/api/v1/targets"}`)
|
||||||
|
|
||||||
promscrapeConfigReloadRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/-/reload"}`)
|
promscrapeConfigReloadRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/-/reload"}`)
|
||||||
)
|
)
|
||||||
|
@ -495,6 +495,7 @@ VictoriaMetrics supports the following handlers from [Prometheus querying API](h
|
|||||||
* [/api/v1/labels](https://prometheus.io/docs/prometheus/latest/querying/api/#getting-label-names)
|
* [/api/v1/labels](https://prometheus.io/docs/prometheus/latest/querying/api/#getting-label-names)
|
||||||
* [/api/v1/label/.../values](https://prometheus.io/docs/prometheus/latest/querying/api/#querying-label-values)
|
* [/api/v1/label/.../values](https://prometheus.io/docs/prometheus/latest/querying/api/#querying-label-values)
|
||||||
* [/api/v1/status/tsdb](https://prometheus.io/docs/prometheus/latest/querying/api/#tsdb-stats)
|
* [/api/v1/status/tsdb](https://prometheus.io/docs/prometheus/latest/querying/api/#tsdb-stats)
|
||||||
|
* [/api/v1/targets](https://prometheus.io/docs/prometheus/latest/querying/api/#targets) - see [these docs](#how-to-scrape-prometheus-exporters-such-as-node-exporter) for more details.
|
||||||
|
|
||||||
These handlers can be queried from Prometheus-compatible clients such as Grafana or curl.
|
These handlers can be queried from Prometheus-compatible clients such as Grafana or curl.
|
||||||
|
|
||||||
|
@ -211,9 +211,13 @@ either via `vmagent` itself or via Prometheus, so the exported metrics could be
|
|||||||
Use official [Grafana dashboard](https://grafana.com/grafana/dashboards/12683) for `vmagent` state overview.
|
Use official [Grafana dashboard](https://grafana.com/grafana/dashboards/12683) for `vmagent` state overview.
|
||||||
If you have suggestions, improvements or found a bug - feel free to open an issue on github or add review to the dashboard.
|
If you have suggestions, improvements or found a bug - feel free to open an issue on github or add review to the dashboard.
|
||||||
|
|
||||||
`vmagent` also exports target statuses at `http://vmagent-host:8429/targets` page in plaintext format.
|
`vmagent` also exports target statuses at the following handlers:
|
||||||
`/targets` handler accepts optional `show_original_labels=1` query arg, which shows the original labels per each target
|
|
||||||
before applying relabeling. This information may be useful for debugging target relabeling.
|
* `http://vmagent-host:8429/targets`. This handler returns human-readable plaintext status for every active target.
|
||||||
|
This page is convenient to query from command line with `wget`, `curl` or similar tools.
|
||||||
|
It accepts optional `show_original_labels=1` query arg, which shows the original labels per each target before applying relabeling.
|
||||||
|
This information may be useful for debugging target relabeling.
|
||||||
|
* `http://vmagent-host:8429/api/v1/targets`. This handler returns data compatible with [the corresponding page from Prometheus API](https://prometheus.io/docs/prometheus/latest/querying/api/#targets).
|
||||||
|
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
@ -224,7 +228,8 @@ before applying relabeling. This information may be useful for debugging target
|
|||||||
since `vmagent` establishes at least a single TCP connection per each target.
|
since `vmagent` establishes at least a single TCP connection per each target.
|
||||||
|
|
||||||
* When `vmagent` scrapes many unreliable targets, it can flood error log with scrape errors. These errors can be suppressed
|
* When `vmagent` scrapes many unreliable targets, it can flood error log with scrape errors. These errors can be suppressed
|
||||||
by passing `-promscrape.suppressScrapeErrors` command-line flag to `vmagent`. The most recent scrape error per each target can be observed at `http://vmagent-host:8429/targets`.
|
by passing `-promscrape.suppressScrapeErrors` command-line flag to `vmagent`. The most recent scrape error per each target can be observed at `http://vmagent-host:8429/targets`
|
||||||
|
and `http://vmagent-host:8429/api/v1/targets`.
|
||||||
|
|
||||||
* It is recommended to increase `-remoteWrite.queues` if `vmagent_remotewrite_pending_data_bytes` metric exported at `http://vmagent-host:8429/metrics` page constantly grows.
|
* It is recommended to increase `-remoteWrite.queues` if `vmagent_remotewrite_pending_data_bytes` metric exported at `http://vmagent-host:8429/metrics` page constantly grows.
|
||||||
|
|
||||||
|
@ -642,6 +642,7 @@ func appendScrapeWork(dst []ScrapeWork, swc *scrapeWorkConfig, target string, ex
|
|||||||
labels = promrelabel.RemoveMetaLabels(labels[:0], labels)
|
labels = promrelabel.RemoveMetaLabels(labels[:0], labels)
|
||||||
if len(labels) == 0 {
|
if len(labels) == 0 {
|
||||||
// Drop target without labels.
|
// Drop target without labels.
|
||||||
|
droppedTargetsMap.Register(originalLabels)
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
// See https://www.robustperception.io/life-of-a-label
|
// See https://www.robustperception.io/life-of-a-label
|
||||||
@ -652,10 +653,12 @@ func appendScrapeWork(dst []ScrapeWork, swc *scrapeWorkConfig, target string, ex
|
|||||||
addressRelabeled := promrelabel.GetLabelValueByName(labels, "__address__")
|
addressRelabeled := promrelabel.GetLabelValueByName(labels, "__address__")
|
||||||
if len(addressRelabeled) == 0 {
|
if len(addressRelabeled) == 0 {
|
||||||
// Drop target without scrape address.
|
// Drop target without scrape address.
|
||||||
|
droppedTargetsMap.Register(originalLabels)
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
if strings.Contains(addressRelabeled, "/") {
|
if strings.Contains(addressRelabeled, "/") {
|
||||||
// Drop target with '/'
|
// Drop target with '/'
|
||||||
|
droppedTargetsMap.Register(originalLabels)
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
addressRelabeled = addMissingPort(schemeRelabeled, addressRelabeled)
|
addressRelabeled = addMissingPort(schemeRelabeled, addressRelabeled)
|
||||||
|
@ -284,6 +284,7 @@ func (sg *scraperGroup) update(sws []ScrapeWork) {
|
|||||||
"original labels for target1: %s; original labels for target2: %s",
|
"original labels for target1: %s; original labels for target2: %s",
|
||||||
sw.ScrapeURL, sw.LabelsString(), promLabelsString(originalLabels), promLabelsString(sw.OriginalLabels))
|
sw.ScrapeURL, sw.LabelsString(), promLabelsString(originalLabels), promLabelsString(sw.OriginalLabels))
|
||||||
}
|
}
|
||||||
|
droppedTargetsMap.Register(sw.OriginalLabels)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
swsMap[key] = sw.OriginalLabels
|
swsMap[key] = sw.OriginalLabels
|
||||||
|
@ -6,6 +6,10 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime"
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel"
|
||||||
)
|
)
|
||||||
|
|
||||||
var tsmGlobal = newTargetStatusMap()
|
var tsmGlobal = newTargetStatusMap()
|
||||||
@ -15,6 +19,26 @@ func WriteHumanReadableTargetsStatus(w io.Writer, showOriginalLabels bool) {
|
|||||||
tsmGlobal.WriteHumanReadable(w, showOriginalLabels)
|
tsmGlobal.WriteHumanReadable(w, showOriginalLabels)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteAPIV1Targets writes /api/v1/targets to w according to https://prometheus.io/docs/prometheus/latest/querying/api/#targets
|
||||||
|
func WriteAPIV1Targets(w io.Writer, state string) {
|
||||||
|
if state == "" {
|
||||||
|
state = "any"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, `{"status":"success","data":{"activeTargets":`)
|
||||||
|
if state == "active" || state == "any" {
|
||||||
|
tsmGlobal.WriteActiveTargetsJSON(w)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, `[]`)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, `,"droppedTargets":`)
|
||||||
|
if state == "dropped" || state == "any" {
|
||||||
|
droppedTargetsMap.WriteDroppedTargetsJSON(w)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, `[]`)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, `}}`)
|
||||||
|
}
|
||||||
|
|
||||||
type targetStatusMap struct {
|
type targetStatusMap struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
m map[uint64]targetStatus
|
m map[uint64]targetStatus
|
||||||
@ -73,6 +97,66 @@ func (tsm *targetStatusMap) StatusByGroup(group string, up bool) int {
|
|||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteActiveTargetsJSON writes `activeTargets` contents to w according to https://prometheus.io/docs/prometheus/latest/querying/api/#targets
|
||||||
|
func (tsm *targetStatusMap) WriteActiveTargetsJSON(w io.Writer) {
|
||||||
|
tsm.mu.Lock()
|
||||||
|
type keyStatus struct {
|
||||||
|
key string
|
||||||
|
st targetStatus
|
||||||
|
}
|
||||||
|
kss := make([]keyStatus, 0, len(tsm.m))
|
||||||
|
for _, st := range tsm.m {
|
||||||
|
key := promLabelsString(st.sw.OriginalLabels)
|
||||||
|
kss = append(kss, keyStatus{
|
||||||
|
key: key,
|
||||||
|
st: st,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
tsm.mu.Unlock()
|
||||||
|
|
||||||
|
sort.Slice(kss, func(i, j int) bool {
|
||||||
|
return kss[i].key < kss[j].key
|
||||||
|
})
|
||||||
|
fmt.Fprintf(w, `[`)
|
||||||
|
for i, ks := range kss {
|
||||||
|
st := ks.st
|
||||||
|
fmt.Fprintf(w, `{"discoveredLabels":`)
|
||||||
|
writeLabelsJSON(w, st.sw.OriginalLabels)
|
||||||
|
fmt.Fprintf(w, `,"labels":`)
|
||||||
|
labelsFinalized := promrelabel.FinalizeLabels(nil, st.sw.Labels)
|
||||||
|
writeLabelsJSON(w, labelsFinalized)
|
||||||
|
fmt.Fprintf(w, `,"scrapePool":%q`, st.sw.Job())
|
||||||
|
fmt.Fprintf(w, `,"scrapeUrl":%q`, st.sw.ScrapeURL)
|
||||||
|
errMsg := ""
|
||||||
|
if st.err != nil {
|
||||||
|
errMsg = st.err.Error()
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, `,"lastError":%q`, errMsg)
|
||||||
|
fmt.Fprintf(w, `,"lastScrape":%q`, time.Unix(st.scrapeTime/1000, (st.scrapeTime%1000)*1e6).Format(time.RFC3339Nano))
|
||||||
|
fmt.Fprintf(w, `,"lastScrapeDuration":%g`, (time.Millisecond * time.Duration(st.scrapeDuration)).Seconds())
|
||||||
|
state := "up"
|
||||||
|
if !st.up {
|
||||||
|
state = "down"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, `,"health":%q}`, state)
|
||||||
|
if i+1 < len(kss) {
|
||||||
|
fmt.Fprintf(w, `,`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, `]`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeLabelsJSON(w io.Writer, labels []prompbmarshal.Label) {
|
||||||
|
fmt.Fprintf(w, `{`)
|
||||||
|
for i, label := range labels {
|
||||||
|
fmt.Fprintf(w, "%q:%q", label.Name, label.Value)
|
||||||
|
if i+1 < len(labels) {
|
||||||
|
fmt.Fprintf(w, `,`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, `}`)
|
||||||
|
}
|
||||||
|
|
||||||
func (tsm *targetStatusMap) WriteHumanReadable(w io.Writer, showOriginalLabels bool) {
|
func (tsm *targetStatusMap) WriteHumanReadable(w io.Writer, showOriginalLabels bool) {
|
||||||
byJob := make(map[string][]targetStatus)
|
byJob := make(map[string][]targetStatus)
|
||||||
tsm.mu.Lock()
|
tsm.mu.Lock()
|
||||||
@ -143,3 +227,69 @@ type targetStatus struct {
|
|||||||
func (st *targetStatus) getDurationFromLastScrape() time.Duration {
|
func (st *targetStatus) getDurationFromLastScrape() time.Duration {
|
||||||
return time.Since(time.Unix(st.scrapeTime/1000, (st.scrapeTime%1000)*1e6))
|
return time.Since(time.Unix(st.scrapeTime/1000, (st.scrapeTime%1000)*1e6))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type droppedTargets struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
m map[string]droppedTarget
|
||||||
|
lastCleanupTime uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type droppedTarget struct {
|
||||||
|
originalLabels []prompbmarshal.Label
|
||||||
|
deadline uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dt *droppedTargets) Register(originalLabels []prompbmarshal.Label) {
|
||||||
|
key := promLabelsString(originalLabels)
|
||||||
|
currentTime := fasttime.UnixTimestamp()
|
||||||
|
dt.mu.Lock()
|
||||||
|
dt.m[key] = droppedTarget{
|
||||||
|
originalLabels: originalLabels,
|
||||||
|
deadline: currentTime + 10*60,
|
||||||
|
}
|
||||||
|
if currentTime-dt.lastCleanupTime > 60 {
|
||||||
|
for k, v := range dt.m {
|
||||||
|
if currentTime > v.deadline {
|
||||||
|
delete(dt.m, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dt.lastCleanupTime = currentTime
|
||||||
|
}
|
||||||
|
dt.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteDroppedTargetsJSON writes `droppedTargets` contents to w according to https://prometheus.io/docs/prometheus/latest/querying/api/#targets
|
||||||
|
func (dt *droppedTargets) WriteDroppedTargetsJSON(w io.Writer) {
|
||||||
|
dt.mu.Lock()
|
||||||
|
type keyStatus struct {
|
||||||
|
key string
|
||||||
|
originalLabels []prompbmarshal.Label
|
||||||
|
}
|
||||||
|
kss := make([]keyStatus, 0, len(dt.m))
|
||||||
|
for _, v := range dt.m {
|
||||||
|
key := promLabelsString(v.originalLabels)
|
||||||
|
kss = append(kss, keyStatus{
|
||||||
|
key: key,
|
||||||
|
originalLabels: v.originalLabels,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
dt.mu.Unlock()
|
||||||
|
|
||||||
|
sort.Slice(kss, func(i, j int) bool {
|
||||||
|
return kss[i].key < kss[j].key
|
||||||
|
})
|
||||||
|
fmt.Fprintf(w, `[`)
|
||||||
|
for i, ks := range kss {
|
||||||
|
fmt.Fprintf(w, `{"discoveredLabels":`)
|
||||||
|
writeLabelsJSON(w, ks.originalLabels)
|
||||||
|
fmt.Fprintf(w, `}`)
|
||||||
|
if i+1 < len(kss) {
|
||||||
|
fmt.Fprintf(w, `,`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, `]`)
|
||||||
|
}
|
||||||
|
|
||||||
|
var droppedTargetsMap = &droppedTargets{
|
||||||
|
m: make(map[string]droppedTarget),
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user