app/victoria-metrics: add -selfScrapeInterval flag for self-scraping /metrics page

Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/30
Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/180
This commit is contained in:
Aliaksandr Valialkin 2020-01-25 19:19:23 +02:00
parent 2a0a0ed14d
commit 9df5b2d1c3
5 changed files with 110 additions and 4 deletions

View file

@ -786,8 +786,11 @@ mkfs.ext4 ... -O 64bit,huge_file,extent -T huge
### Monitoring ### Monitoring
VictoriaMetrics exports internal metrics in Prometheus format on the `/metrics` page. VictoriaMetrics exports internal metrics in Prometheus format at `/metrics` page.
Add this page to Prometheus' scrape config in order to collect VictoriaMetrics metrics. These metrics may be collected either via Prometheus by adding the corresponding scrape config to it.
Alternatively they can be self-scraped by setting `-selfScrapeInterval` command-line flag to duration greater than 0.
For example, `-scrapeInterval=10s` would enable self-scraping of `/metrics` page with 10 seconds interval.
There are officials Grafana dashboards for [single-node VictoriaMetrics](https://grafana.com/dashboards/10229) and [clustered VictoriaMetrics](https://grafana.com/grafana/dashboards/11176). There are officials Grafana dashboards for [single-node VictoriaMetrics](https://grafana.com/dashboards/10229) and [clustered VictoriaMetrics](https://grafana.com/grafana/dashboards/11176).
The most interesting metrics are: The most interesting metrics are:

View file

@ -26,6 +26,7 @@ func main() {
vmstorage.Init() vmstorage.Init()
vmselect.Init() vmselect.Init()
vminsert.Init() vminsert.Init()
startSelfScraper()
go httpserver.Serve(*httpListenAddr, requestHandler) go httpserver.Serve(*httpListenAddr, requestHandler)
logger.Infof("started VictoriaMetrics in %.3f seconds", time.Since(startTime).Seconds()) logger.Infof("started VictoriaMetrics in %.3f seconds", time.Since(startTime).Seconds())
@ -33,6 +34,8 @@ func main() {
sig := procutil.WaitForSigterm() sig := procutil.WaitForSigterm()
logger.Infof("received signal %s", sig) logger.Infof("received signal %s", sig)
stopSelfScraper()
logger.Infof("gracefully shutting down webservice at %q", *httpListenAddr) logger.Infof("gracefully shutting down webservice at %q", *httpListenAddr)
startTime = time.Now() startTime = time.Now()
if err := httpserver.Stop(*httpListenAddr); err != nil { if err := httpserver.Stop(*httpListenAddr); err != nil {

View file

@ -0,0 +1,99 @@
package main
import (
"flag"
"sync"
"time"
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmstorage"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompb"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/prometheus"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
)
var selfScrapeInterval = flag.Duration("selfScrapeInterval", 0, "Interval for self-scraping own metrics at `/metrics` page")
var selfScraperStopCh chan struct{}
var selfScraperWG sync.WaitGroup
func startSelfScraper() {
selfScraperStopCh = make(chan struct{})
selfScraperWG.Add(1)
go func() {
defer selfScraperWG.Done()
selfScraper(*selfScrapeInterval)
}()
}
func stopSelfScraper() {
close(selfScraperStopCh)
selfScraperWG.Wait()
}
func selfScraper(scrapeInterval time.Duration) {
if scrapeInterval <= 0 {
// Self-scrape is disabled.
return
}
logger.Infof("started self-scraping `/metrics` page with interval %.3f seconds", scrapeInterval.Seconds())
var bb bytesutil.ByteBuffer
var rows prometheus.Rows
var mrs []storage.MetricRow
var labels []prompb.Label
t := time.NewTicker(scrapeInterval)
var currentTimestamp int64
for {
select {
case <-selfScraperStopCh:
t.Stop()
logger.Infof("stopped self-scraping `/metrics` page")
return
case currentTime := <-t.C:
currentTimestamp = currentTime.UnixNano() / 1e6
}
bb.Reset()
httpserver.WritePrometheusMetrics(&bb)
s := bytesutil.ToUnsafeString(bb.B)
rows.Reset()
rows.Unmarshal(s)
mrs = mrs[:0]
for i := range rows.Rows {
r := &rows.Rows[i]
labels = labels[:0]
labels = addLabel(labels, "", r.Metric)
labels = addLabel(labels, "job", "victoria-metrics")
labels = addLabel(labels, "instance", "self")
for j := range r.Tags {
t := &r.Tags[j]
labels = addLabel(labels, t.Key, t.Value)
}
if len(mrs) < cap(mrs) {
mrs = mrs[:len(mrs)+1]
} else {
mrs = append(mrs, storage.MetricRow{})
}
mr := &mrs[len(mrs)-1]
mr.MetricNameRaw = storage.MarshalMetricNameRaw(mr.MetricNameRaw[:0], labels)
mr.Timestamp = currentTimestamp
mr.Value = r.Value
}
logger.Infof("writing %d rows at timestamp %d", len(mrs), currentTimestamp)
vmstorage.AddRows(mrs)
}
}
func addLabel(dst []prompb.Label, key, value string) []prompb.Label {
if len(dst) < cap(dst) {
dst = dst[:len(dst)+1]
} else {
dst = append(dst, prompb.Label{})
}
lb := &dst[len(dst)-1]
lb.Name = bytesutil.ToUnsafeBytes(key)
lb.Value = bytesutil.ToUnsafeBytes(value)
return dst
}

View file

@ -181,7 +181,7 @@ func handlerWrapper(w http.ResponseWriter, r *http.Request, rh RequestHandler) {
} }
startTime := time.Now() startTime := time.Now()
w.Header().Set("Content-Type", "text/plain") w.Header().Set("Content-Type", "text/plain")
writePrometheusMetrics(w) WritePrometheusMetrics(w)
metricsHandlerDuration.UpdateDuration(startTime) metricsHandlerDuration.UpdateDuration(startTime)
return return
default: default:

View file

@ -12,7 +12,8 @@ import (
"github.com/VictoriaMetrics/metrics" "github.com/VictoriaMetrics/metrics"
) )
func writePrometheusMetrics(w io.Writer) { // WritePrometheusMetrics writes all the registered metrics to w in Prometheus exposition format.
func WritePrometheusMetrics(w io.Writer) {
metrics.WritePrometheus(w, true) metrics.WritePrometheus(w, true)
fmt.Fprintf(w, "vm_app_version{version=%q} 1\n", buildinfo.Version) fmt.Fprintf(w, "vm_app_version{version=%q} 1\n", buildinfo.Version)