diff --git a/app/vmagent/main.go b/app/vmagent/main.go index 866dce3292..dcfcf845ec 100644 --- a/app/vmagent/main.go +++ b/app/vmagent/main.go @@ -262,6 +262,14 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool { promscrapeTargetsRequests.Inc() promscrape.WriteHumanReadableTargetsStatus(w, r) return true + case "/target_response": + promscrapeTargetResponseRequests.Inc() + if err := promscrape.WriteTargetResponse(w, r); err != nil { + promscrapeTargetResponseErrors.Inc() + httpserver.Errorf(w, r, "%s", err) + return true + } + return true case "/config": if *configAuthKey != "" && r.FormValue("authKey") != *configAuthKey { err := &httpserver.ErrorWithStatusCode{ @@ -443,6 +451,9 @@ var ( promscrapeTargetsRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/targets"}`) promscrapeAPIV1TargetsRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/api/v1/targets"}`) + promscrapeTargetResponseRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/target_response"}`) + promscrapeTargetResponseErrors = metrics.NewCounter(`vmagent_http_request_errors_total{path="/target_response"}`) + promscrapeConfigRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/config"}`) promscrapeConfigReloadRequests = metrics.NewCounter(`vmagent_http_requests_total{path="/-/reload"}`) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 0e22c225fe..61d16bcdf4 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -16,6 +16,7 @@ sort: 15 * FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): add ability to configure notifiers (e.g. alertmanager) via a file in the way similar to Prometheus. See [these docs](https://docs.victoriametrics.com/vmalert.html#notifier-configuration-file), [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2127). * FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): add support for Consul service discovery for notifiers. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1947). * FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): add support for specifying Basic Auth password for notifiers via a file. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1567). +* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): provide the ability to fetch target responses on behalf of `vmagent`. Click `fetch response` link for the needed target at `/targets` page. This feature may be useful for debugging responses from targets located in isolated environments. * BUGFIX: return proper results from `highestMax()` function at [Graphite render API](https://docs.victoriametrics.com/#graphite-render-api-usage). Previously it was incorrectly returning timeseries with min peaks instead of max peaks. * BUGFIX: properly limit indexdb cache sizes. Previously they could exceed values set via `-memory.allowedPercent` and/or `-memory.allowedBytes` when `indexdb` contained many data parts. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2007). diff --git a/lib/promscrape/scraper.go b/lib/promscrape/scraper.go index 6ae795575e..d02c09b5ce 100644 --- a/lib/promscrape/scraper.go +++ b/lib/promscrape/scraper.go @@ -373,14 +373,14 @@ func (sg *scraperGroup) update(sws []*ScrapeWork) { sg.activeScrapers.Inc() sg.scrapersStarted.Inc() sg.wg.Add(1) - tsmGlobal.Register(sw) + tsmGlobal.Register(&sc.sw) go func(sw *ScrapeWork) { defer func() { sg.wg.Done() close(sc.stoppedCh) }() sc.sw.run(sc.stopCh, sg.globalStopCh) - tsmGlobal.Unregister(sw) + tsmGlobal.Unregister(&sc.sw) sg.activeScrapers.Dec() sg.scrapersStopped.Inc() }(sw) diff --git a/lib/promscrape/scrapework.go b/lib/promscrape/scrapework.go index 13a08a43a5..aef8f85b18 100644 --- a/lib/promscrape/scrapework.go +++ b/lib/promscrape/scrapework.go @@ -3,6 +3,7 @@ package promscrape import ( "flag" "fmt" + "io/ioutil" "math" "math/bits" "strconv" @@ -371,6 +372,22 @@ func (sw *scrapeWork) mustSwitchToStreamParseMode(responseSize int) bool { return sw.Config.canSwitchToStreamParseMode() && responseSize >= minResponseSizeForStreamParse.N } +// getTargetResponse() fetches response from sw target in the same way as when scraping the target. +func (sw *scrapeWork) getTargetResponse() ([]byte, error) { + if *streamParse || sw.Config.StreamParse || sw.mustSwitchToStreamParseMode(sw.prevBodyLen) { + // Read the response in stream mode. + sr, err := sw.GetStreamReader() + if err != nil { + return nil, err + } + data, err := ioutil.ReadAll(sr) + sr.MustClose() + return data, err + } + // Read the response in usual mode. + return sw.ReadData(nil) +} + func (sw *scrapeWork) scrapeInternal(scrapeTimestamp, realTimestamp int64) error { if *streamParse || sw.Config.StreamParse || sw.mustSwitchToStreamParseMode(sw.prevBodyLen) { // Read data from scrape targets in streaming manner. @@ -455,7 +472,7 @@ func (sw *scrapeWork) scrapeInternal(scrapeTimestamp, realTimestamp int64) error // This should reduce memory usage when scraping targets which return big responses. leveledbytebufferpool.Put(body) } - tsmGlobal.Update(sw.Config, sw.ScrapeGroup, up == 1, realTimestamp, int64(duration*1000), samplesScraped, err) + tsmGlobal.Update(sw, sw.ScrapeGroup, up == 1, realTimestamp, int64(duration*1000), samplesScraped, err) return err } @@ -558,7 +575,7 @@ func (sw *scrapeWork) scrapeStream(scrapeTimestamp, realTimestamp int64) error { sw.storeLastScrape(sbr.body) } sw.finalizeLastScrape() - tsmGlobal.Update(sw.Config, sw.ScrapeGroup, up == 1, realTimestamp, int64(duration*1000), samplesScraped, err) + tsmGlobal.Update(sw, sw.ScrapeGroup, up == 1, realTimestamp, int64(duration*1000), samplesScraped, err) // Do not track active series in streaming mode, since this may need too big amounts of memory // when the target exports too big number of metrics. return err diff --git a/lib/promscrape/targets_response.qtpl b/lib/promscrape/targets_response.qtpl index b85bc8b8c1..9e50fbff74 100644 --- a/lib/promscrape/targets_response.qtpl +++ b/lib/promscrape/targets_response.qtpl @@ -76,7 +76,9 @@ job={%q= jobName %} (0/0 up) {% for j, ts := range js.targetsStatus %} {% if onlyUnhealthy && ts.up %}{% continue %}{% endif %}
Endpoint | State | Labels | Last Scrape | Scrape Duration | Samples Scraped | Error |
---|