allow exposing meta information for registered metrics (#61)
* allow exposing meta information for registered metrics New public method `ExposeMetadata` allows enabling exposition of dummy meta-info for all exposed metrics across all Sets. This feature is needed to improve compatibility with 3rd-party scrapers that require meta information to be present. This commit doesn't update exposition of default system/process metrics to keep the list of changes small. This change should be added in a follow-up commit. https://github.com/VictoriaMetrics/metrics/issues/48 * cleanup * wip * wip * wip * wip --------- Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
fd25889711
commit
9dc7358869
@ -73,8 +73,11 @@ http.HandleFunc("/metrics", func(w http.ResponseWriter, req *http.Request) {
|
|||||||
metrics.InitPush("http://victoria-metrics:8428/api/v1/import/prometheus", 10*time.Second, `instance="foobar"`, true)
|
metrics.InitPush("http://victoria-metrics:8428/api/v1/import/prometheus", 10*time.Second, `instance="foobar"`, true)
|
||||||
```
|
```
|
||||||
|
|
||||||
See [docs](http://godoc.org/github.com/VictoriaMetrics/metrics) for more info.
|
By default, exposed metrics [do not have](https://github.com/VictoriaMetrics/metrics/issues/48#issuecomment-1620765811)
|
||||||
|
`TYPE` or `HELP` meta information. Call [`ExposeMetadata(true)`](https://pkg.go.dev/github.com/VictoriaMetrics/metrics#ExposeMetadata)
|
||||||
|
in order to generate `TYPE` and `HELP` meta information per each metric.
|
||||||
|
|
||||||
|
See [docs](https://pkg.go.dev/github.com/VictoriaMetrics/metrics) for more info.
|
||||||
|
|
||||||
### Users
|
### Users
|
||||||
|
|
||||||
|
@ -58,6 +58,10 @@ func (c *Counter) marshalTo(prefix string, w io.Writer) {
|
|||||||
fmt.Fprintf(w, "%s %d\n", prefix, v)
|
fmt.Fprintf(w, "%s %d\n", prefix, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Counter) metricType() string {
|
||||||
|
return "counter"
|
||||||
|
}
|
||||||
|
|
||||||
// GetOrCreateCounter returns registered counter with the given name
|
// GetOrCreateCounter returns registered counter with the given name
|
||||||
// or creates new counter if the registry doesn't contain counter with
|
// or creates new counter if the registry doesn't contain counter with
|
||||||
// the given name.
|
// the given name.
|
||||||
|
@ -63,6 +63,10 @@ func (fc *FloatCounter) marshalTo(prefix string, w io.Writer) {
|
|||||||
fmt.Fprintf(w, "%s %g\n", prefix, v)
|
fmt.Fprintf(w, "%s %g\n", prefix, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fc *FloatCounter) metricType() string {
|
||||||
|
return "counter"
|
||||||
|
}
|
||||||
|
|
||||||
// GetOrCreateFloatCounter returns registered FloatCounter with the given name
|
// GetOrCreateFloatCounter returns registered FloatCounter with the given name
|
||||||
// or creates new FloatCounter if the registry doesn't contain FloatCounter with
|
// or creates new FloatCounter if the registry doesn't contain FloatCounter with
|
||||||
// the given name.
|
// the given name.
|
||||||
|
4
gauge.go
4
gauge.go
@ -46,6 +46,10 @@ func (g *Gauge) marshalTo(prefix string, w io.Writer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Gauge) metricType() string {
|
||||||
|
return "gauge"
|
||||||
|
}
|
||||||
|
|
||||||
// GetOrCreateGauge returns registered gauge with the given name
|
// GetOrCreateGauge returns registered gauge with the given name
|
||||||
// or creates new gauge if the registry doesn't contain gauge with
|
// or creates new gauge if the registry doesn't contain gauge with
|
||||||
// the given name.
|
// the given name.
|
||||||
|
4
go.mod
4
go.mod
@ -5,4 +5,6 @@ require (
|
|||||||
golang.org/x/sys v0.15.0
|
golang.org/x/sys v0.15.0
|
||||||
)
|
)
|
||||||
|
|
||||||
go 1.16
|
require github.com/valyala/fastrand v1.1.0 // indirect
|
||||||
|
|
||||||
|
go 1.17
|
||||||
|
@ -47,34 +47,34 @@ func writeGoMetrics(w io.Writer) {
|
|||||||
|
|
||||||
var ms runtime.MemStats
|
var ms runtime.MemStats
|
||||||
runtime.ReadMemStats(&ms)
|
runtime.ReadMemStats(&ms)
|
||||||
fmt.Fprintf(w, "go_memstats_alloc_bytes %d\n", ms.Alloc)
|
WriteGaugeUint64(w, "go_memstats_alloc_bytes", ms.Alloc)
|
||||||
fmt.Fprintf(w, "go_memstats_alloc_bytes_total %d\n", ms.TotalAlloc)
|
WriteCounterUint64(w, "go_memstats_alloc_bytes_total", ms.TotalAlloc)
|
||||||
fmt.Fprintf(w, "go_memstats_buck_hash_sys_bytes %d\n", ms.BuckHashSys)
|
WriteGaugeUint64(w, "go_memstats_buck_hash_sys_bytes", ms.BuckHashSys)
|
||||||
fmt.Fprintf(w, "go_memstats_frees_total %d\n", ms.Frees)
|
WriteCounterUint64(w, "go_memstats_frees_total", ms.Frees)
|
||||||
fmt.Fprintf(w, "go_memstats_gc_cpu_fraction %g\n", ms.GCCPUFraction)
|
WriteGaugeFloat64(w, "go_memstats_gc_cpu_fraction", ms.GCCPUFraction)
|
||||||
fmt.Fprintf(w, "go_memstats_gc_sys_bytes %d\n", ms.GCSys)
|
WriteGaugeUint64(w, "go_memstats_gc_sys_bytes", ms.GCSys)
|
||||||
|
|
||||||
fmt.Fprintf(w, "go_memstats_heap_alloc_bytes %d\n", ms.HeapAlloc)
|
WriteGaugeUint64(w, "go_memstats_heap_alloc_bytes", ms.HeapAlloc)
|
||||||
fmt.Fprintf(w, "go_memstats_heap_idle_bytes %d\n", ms.HeapIdle)
|
WriteGaugeUint64(w, "go_memstats_heap_idle_bytes", ms.HeapIdle)
|
||||||
fmt.Fprintf(w, "go_memstats_heap_inuse_bytes %d\n", ms.HeapInuse)
|
WriteGaugeUint64(w, "go_memstats_heap_inuse_bytes", ms.HeapInuse)
|
||||||
fmt.Fprintf(w, "go_memstats_heap_objects %d\n", ms.HeapObjects)
|
WriteGaugeUint64(w, "go_memstats_heap_objects", ms.HeapObjects)
|
||||||
fmt.Fprintf(w, "go_memstats_heap_released_bytes %d\n", ms.HeapReleased)
|
WriteGaugeUint64(w, "go_memstats_heap_released_bytes", ms.HeapReleased)
|
||||||
fmt.Fprintf(w, "go_memstats_heap_sys_bytes %d\n", ms.HeapSys)
|
WriteGaugeUint64(w, "go_memstats_heap_sys_bytes", ms.HeapSys)
|
||||||
fmt.Fprintf(w, "go_memstats_last_gc_time_seconds %g\n", float64(ms.LastGC)/1e9)
|
WriteGaugeFloat64(w, "go_memstats_last_gc_time_seconds", float64(ms.LastGC)/1e9)
|
||||||
fmt.Fprintf(w, "go_memstats_lookups_total %d\n", ms.Lookups)
|
WriteCounterUint64(w, "go_memstats_lookups_total", ms.Lookups)
|
||||||
fmt.Fprintf(w, "go_memstats_mallocs_total %d\n", ms.Mallocs)
|
WriteCounterUint64(w, "go_memstats_mallocs_total", ms.Mallocs)
|
||||||
fmt.Fprintf(w, "go_memstats_mcache_inuse_bytes %d\n", ms.MCacheInuse)
|
WriteGaugeUint64(w, "go_memstats_mcache_inuse_bytes", ms.MCacheInuse)
|
||||||
fmt.Fprintf(w, "go_memstats_mcache_sys_bytes %d\n", ms.MCacheSys)
|
WriteGaugeUint64(w, "go_memstats_mcache_sys_bytes", ms.MCacheSys)
|
||||||
fmt.Fprintf(w, "go_memstats_mspan_inuse_bytes %d\n", ms.MSpanInuse)
|
WriteGaugeUint64(w, "go_memstats_mspan_inuse_bytes", ms.MSpanInuse)
|
||||||
fmt.Fprintf(w, "go_memstats_mspan_sys_bytes %d\n", ms.MSpanSys)
|
WriteGaugeUint64(w, "go_memstats_mspan_sys_bytes", ms.MSpanSys)
|
||||||
fmt.Fprintf(w, "go_memstats_next_gc_bytes %d\n", ms.NextGC)
|
WriteGaugeUint64(w, "go_memstats_next_gc_bytes", ms.NextGC)
|
||||||
fmt.Fprintf(w, "go_memstats_other_sys_bytes %d\n", ms.OtherSys)
|
WriteGaugeUint64(w, "go_memstats_other_sys_bytes", ms.OtherSys)
|
||||||
fmt.Fprintf(w, "go_memstats_stack_inuse_bytes %d\n", ms.StackInuse)
|
WriteGaugeUint64(w, "go_memstats_stack_inuse_bytes", ms.StackInuse)
|
||||||
fmt.Fprintf(w, "go_memstats_stack_sys_bytes %d\n", ms.StackSys)
|
WriteGaugeUint64(w, "go_memstats_stack_sys_bytes", ms.StackSys)
|
||||||
fmt.Fprintf(w, "go_memstats_sys_bytes %d\n", ms.Sys)
|
WriteGaugeUint64(w, "go_memstats_sys_bytes", ms.Sys)
|
||||||
|
|
||||||
fmt.Fprintf(w, "go_cgo_calls_count %d\n", runtime.NumCgoCall())
|
WriteCounterUint64(w, "go_cgo_calls_count", uint64(runtime.NumCgoCall()))
|
||||||
fmt.Fprintf(w, "go_cpu_count %d\n", runtime.NumCPU())
|
WriteGaugeUint64(w, "go_cpu_count", uint64(runtime.NumCPU()))
|
||||||
|
|
||||||
gcPauses := histogram.NewFast()
|
gcPauses := histogram.NewFast()
|
||||||
for _, pauseNs := range ms.PauseNs[:] {
|
for _, pauseNs := range ms.PauseNs[:] {
|
||||||
@ -82,20 +82,25 @@ func writeGoMetrics(w io.Writer) {
|
|||||||
}
|
}
|
||||||
phis := []float64{0, 0.25, 0.5, 0.75, 1}
|
phis := []float64{0, 0.25, 0.5, 0.75, 1}
|
||||||
quantiles := make([]float64, 0, len(phis))
|
quantiles := make([]float64, 0, len(phis))
|
||||||
|
writeMetadataIfNeeded(w, "go_gc_duration_seconds", "summary")
|
||||||
for i, q := range gcPauses.Quantiles(quantiles[:0], phis) {
|
for i, q := range gcPauses.Quantiles(quantiles[:0], phis) {
|
||||||
fmt.Fprintf(w, `go_gc_duration_seconds{quantile="%g"} %g`+"\n", phis[i], q)
|
fmt.Fprintf(w, `go_gc_duration_seconds{quantile="%g"} %g`+"\n", phis[i], q)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, `go_gc_duration_seconds_sum %g`+"\n", float64(ms.PauseTotalNs)/1e9)
|
fmt.Fprintf(w, "go_gc_duration_seconds_sum %g\n", float64(ms.PauseTotalNs)/1e9)
|
||||||
fmt.Fprintf(w, `go_gc_duration_seconds_count %d`+"\n", ms.NumGC)
|
fmt.Fprintf(w, "go_gc_duration_seconds_count %d\n", ms.NumGC)
|
||||||
fmt.Fprintf(w, `go_gc_forced_count %d`+"\n", ms.NumForcedGC)
|
|
||||||
|
|
||||||
fmt.Fprintf(w, `go_gomaxprocs %d`+"\n", runtime.GOMAXPROCS(0))
|
WriteCounterUint64(w, "go_gc_forced_count", uint64(ms.NumForcedGC))
|
||||||
fmt.Fprintf(w, `go_goroutines %d`+"\n", runtime.NumGoroutine())
|
|
||||||
|
WriteGaugeUint64(w, "go_gomaxprocs", uint64(runtime.GOMAXPROCS(0)))
|
||||||
|
WriteGaugeUint64(w, "go_goroutines", uint64(runtime.NumGoroutine()))
|
||||||
numThread, _ := runtime.ThreadCreateProfile(nil)
|
numThread, _ := runtime.ThreadCreateProfile(nil)
|
||||||
fmt.Fprintf(w, `go_threads %d`+"\n", numThread)
|
WriteGaugeUint64(w, "go_threads", uint64(numThread))
|
||||||
|
|
||||||
// Export build details.
|
// Export build details.
|
||||||
|
writeMetadataIfNeeded(w, "go_info", "gauge")
|
||||||
fmt.Fprintf(w, "go_info{version=%q} 1\n", runtime.Version())
|
fmt.Fprintf(w, "go_info{version=%q} 1\n", runtime.Version())
|
||||||
|
|
||||||
|
writeMetadataIfNeeded(w, "go_info_ext", "gauge")
|
||||||
fmt.Fprintf(w, "go_info_ext{compiler=%q, GOARCH=%q, GOOS=%q, GOROOT=%q} 1\n",
|
fmt.Fprintf(w, "go_info_ext{compiler=%q, GOARCH=%q, GOOS=%q, GOROOT=%q} 1\n",
|
||||||
runtime.Compiler, runtime.GOARCH, runtime.GOOS, runtime.GOROOT())
|
runtime.Compiler, runtime.GOARCH, runtime.GOOS, runtime.GOROOT())
|
||||||
}
|
}
|
||||||
@ -117,11 +122,22 @@ func writeRuntimeMetric(w io.Writer, name string, sample *runtimemetrics.Sample)
|
|||||||
case runtimemetrics.KindBad:
|
case runtimemetrics.KindBad:
|
||||||
panic(fmt.Errorf("BUG: unexpected runtimemetrics.KindBad for sample.Name=%q", sample.Name))
|
panic(fmt.Errorf("BUG: unexpected runtimemetrics.KindBad for sample.Name=%q", sample.Name))
|
||||||
case runtimemetrics.KindUint64:
|
case runtimemetrics.KindUint64:
|
||||||
fmt.Fprintf(w, "%s %d\n", name, sample.Value.Uint64())
|
v := sample.Value.Uint64()
|
||||||
|
if strings.HasSuffix(name, "_total") {
|
||||||
|
WriteCounterUint64(w, name, v)
|
||||||
|
} else {
|
||||||
|
WriteGaugeUint64(w, name, v)
|
||||||
|
}
|
||||||
case runtimemetrics.KindFloat64:
|
case runtimemetrics.KindFloat64:
|
||||||
fmt.Fprintf(w, "%s %g\n", name, sample.Value.Float64())
|
v := sample.Value.Float64()
|
||||||
|
if isCounterName(name) {
|
||||||
|
WriteCounterFloat64(w, name, v)
|
||||||
|
} else {
|
||||||
|
WriteGaugeFloat64(w, name, v)
|
||||||
|
}
|
||||||
case runtimemetrics.KindFloat64Histogram:
|
case runtimemetrics.KindFloat64Histogram:
|
||||||
writeRuntimeHistogramMetric(w, name, sample.Value.Float64Histogram())
|
h := sample.Value.Float64Histogram()
|
||||||
|
writeRuntimeHistogramMetric(w, name, h)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("unexpected metric kind=%d", kind))
|
panic(fmt.Errorf("unexpected metric kind=%d", kind))
|
||||||
}
|
}
|
||||||
@ -149,6 +165,7 @@ func writeRuntimeHistogramMetric(w io.Writer, name string, h *runtimemetrics.Flo
|
|||||||
|
|
||||||
totalCount := uint64(0)
|
totalCount := uint64(0)
|
||||||
iNext := 0.0
|
iNext := 0.0
|
||||||
|
writeMetadataIfNeeded(w, name, "histogram")
|
||||||
for i, count := range counts {
|
for i, count := range counts {
|
||||||
totalCount += count
|
totalCount += count
|
||||||
if float64(i) >= iNext {
|
if float64(i) >= iNext {
|
||||||
|
@ -228,3 +228,7 @@ func (h *Histogram) getSum() float64 {
|
|||||||
h.mu.Unlock()
|
h.mu.Unlock()
|
||||||
return sum
|
return sum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Histogram) metricType() string {
|
||||||
|
return "histogram"
|
||||||
|
}
|
||||||
|
65
metrics.go
65
metrics.go
@ -13,9 +13,12 @@
|
|||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,8 +28,17 @@ type namedMetric struct {
|
|||||||
isAux bool
|
isAux bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (nm *namedMetric) family() string {
|
||||||
|
n := strings.IndexByte(nm.name, '{')
|
||||||
|
if n < 0 {
|
||||||
|
return nm.name
|
||||||
|
}
|
||||||
|
return nm.name[:n]
|
||||||
|
}
|
||||||
|
|
||||||
type metric interface {
|
type metric interface {
|
||||||
marshalTo(prefix string, w io.Writer)
|
marshalTo(prefix string, w io.Writer)
|
||||||
|
metricType() string
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultSet = NewSet()
|
var defaultSet = NewSet()
|
||||||
@ -241,3 +253,56 @@ func ListMetricNames() []string {
|
|||||||
func GetDefaultSet() *Set {
|
func GetDefaultSet() *Set {
|
||||||
return defaultSet
|
return defaultSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExposeMetadata allows enabling adding TYPE and HELP metadata to the exposed metrics globally.
|
||||||
|
//
|
||||||
|
// It is safe to call this method multiple times. It is allowed to change it in runtime.
|
||||||
|
// ExposeMetadata is set to false by default.
|
||||||
|
func ExposeMetadata(v bool) {
|
||||||
|
n := 0
|
||||||
|
if v {
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
atomic.StoreUint32(&exposeMetadata, uint32(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMetadataEnabled() bool {
|
||||||
|
n := atomic.LoadUint32(&exposeMetadata)
|
||||||
|
return n != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var exposeMetadata uint32
|
||||||
|
|
||||||
|
func isCounterName(name string) bool {
|
||||||
|
return strings.HasSuffix(name, "_total")
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteGaugeUint64 writes gauge metric with the given name and value to w in Prometheus text exposition format.
|
||||||
|
func WriteGaugeUint64(w io.Writer, name string, value uint64) {
|
||||||
|
writeMetricUint64(w, name, "gauge", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteGaugeFloat64 writes gauge metric with the given name and value to w in Prometheus text exposition format.
|
||||||
|
func WriteGaugeFloat64(w io.Writer, name string, value float64) {
|
||||||
|
writeMetricFloat64(w, name, "gauge", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteCounterUint64 writes counter metric with the given name and value to w in Prometheus text exposition format.
|
||||||
|
func WriteCounterUint64(w io.Writer, name string, value uint64) {
|
||||||
|
writeMetricUint64(w, name, "counter", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteCounterFloat64 writes counter metric with the given name and value to w in Prometheus text exposition format.
|
||||||
|
func WriteCounterFloat64(w io.Writer, name string, value float64) {
|
||||||
|
writeMetricFloat64(w, name, "counter", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeMetricUint64(w io.Writer, metricName, metricType string, value uint64) {
|
||||||
|
writeMetadataIfNeeded(w, metricName, metricType)
|
||||||
|
fmt.Fprintf(w, "%s %d\n", metricName, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeMetricFloat64(w io.Writer, metricName, metricType string, value float64) {
|
||||||
|
writeMetadataIfNeeded(w, metricName, metricType)
|
||||||
|
fmt.Fprintf(w, "%s %g\n", metricName, value)
|
||||||
|
}
|
||||||
|
@ -8,6 +8,81 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestWriteMetrics(t *testing.T) {
|
||||||
|
t.Run("gauge_uint64", func(t *testing.T) {
|
||||||
|
var bb bytes.Buffer
|
||||||
|
|
||||||
|
WriteGaugeUint64(&bb, "foo", 123)
|
||||||
|
sExpected := "foo 123\n"
|
||||||
|
if s := bb.String(); s != sExpected {
|
||||||
|
t.Fatalf("unexpected value; got\n%s\nwant\n%s", s, sExpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
ExposeMetadata(true)
|
||||||
|
bb.Reset()
|
||||||
|
WriteGaugeUint64(&bb, "foo", 123)
|
||||||
|
sExpected = "# HELP foo\n# TYPE foo gauge\nfoo 123\n"
|
||||||
|
ExposeMetadata(false)
|
||||||
|
if s := bb.String(); s != sExpected {
|
||||||
|
t.Fatalf("unexpected value; got\n%s\nwant\n%s", s, sExpected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("gauge_float64", func(t *testing.T) {
|
||||||
|
var bb bytes.Buffer
|
||||||
|
|
||||||
|
WriteGaugeFloat64(&bb, "foo", 1.23)
|
||||||
|
sExpected := "foo 1.23\n"
|
||||||
|
if s := bb.String(); s != sExpected {
|
||||||
|
t.Fatalf("unexpected value; got\n%s\nwant\n%s", s, sExpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
ExposeMetadata(true)
|
||||||
|
bb.Reset()
|
||||||
|
WriteGaugeFloat64(&bb, "foo", 1.23)
|
||||||
|
sExpected = "# HELP foo\n# TYPE foo gauge\nfoo 1.23\n"
|
||||||
|
ExposeMetadata(false)
|
||||||
|
if s := bb.String(); s != sExpected {
|
||||||
|
t.Fatalf("unexpected value; got\n%s\nwant\n%s", s, sExpected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("counter_uint64", func(t *testing.T) {
|
||||||
|
var bb bytes.Buffer
|
||||||
|
|
||||||
|
WriteCounterUint64(&bb, "foo_total", 123)
|
||||||
|
sExpected := "foo_total 123\n"
|
||||||
|
if s := bb.String(); s != sExpected {
|
||||||
|
t.Fatalf("unexpected value; got\n%s\nwant\n%s", s, sExpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
ExposeMetadata(true)
|
||||||
|
bb.Reset()
|
||||||
|
WriteCounterUint64(&bb, "foo_total", 123)
|
||||||
|
sExpected = "# HELP foo_total\n# TYPE foo_total counter\nfoo_total 123\n"
|
||||||
|
ExposeMetadata(false)
|
||||||
|
if s := bb.String(); s != sExpected {
|
||||||
|
t.Fatalf("unexpected value; got\n%s\nwant\n%s", s, sExpected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("counter_float64", func(t *testing.T) {
|
||||||
|
var bb bytes.Buffer
|
||||||
|
|
||||||
|
WriteCounterFloat64(&bb, "foo_total", 1.23)
|
||||||
|
sExpected := "foo_total 1.23\n"
|
||||||
|
if s := bb.String(); s != sExpected {
|
||||||
|
t.Fatalf("unexpected value; got\n%s\nwant\n%s", s, sExpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
ExposeMetadata(true)
|
||||||
|
bb.Reset()
|
||||||
|
WriteCounterFloat64(&bb, "foo_total", 1.23)
|
||||||
|
sExpected = "# HELP foo_total\n# TYPE foo_total counter\nfoo_total 1.23\n"
|
||||||
|
ExposeMetadata(false)
|
||||||
|
if s := bb.String(); s != sExpected {
|
||||||
|
t.Fatalf("unexpected value; got\n%s\nwant\n%s", s, sExpected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetDefaultSet(t *testing.T) {
|
func TestGetDefaultSet(t *testing.T) {
|
||||||
s := GetDefaultSet()
|
s := GetDefaultSet()
|
||||||
if s != defaultSet {
|
if s != defaultSet {
|
||||||
|
@ -74,15 +74,15 @@ func writeProcessMetrics(w io.Writer) {
|
|||||||
|
|
||||||
utime := float64(p.Utime) / userHZ
|
utime := float64(p.Utime) / userHZ
|
||||||
stime := float64(p.Stime) / userHZ
|
stime := float64(p.Stime) / userHZ
|
||||||
fmt.Fprintf(w, "process_cpu_seconds_system_total %g\n", stime)
|
WriteCounterFloat64(w, "process_cpu_seconds_system_total", stime)
|
||||||
fmt.Fprintf(w, "process_cpu_seconds_total %g\n", utime+stime)
|
WriteCounterFloat64(w, "process_cpu_seconds_total", utime+stime)
|
||||||
fmt.Fprintf(w, "process_cpu_seconds_user_total %g\n", utime)
|
WriteCounterFloat64(w, "process_cpu_seconds_user_total", utime)
|
||||||
fmt.Fprintf(w, "process_major_pagefaults_total %d\n", p.Majflt)
|
WriteCounterUint64(w, "process_major_pagefaults_total", uint64(p.Majflt))
|
||||||
fmt.Fprintf(w, "process_minor_pagefaults_total %d\n", p.Minflt)
|
WriteCounterUint64(w, "process_minor_pagefaults_total", uint64(p.Minflt))
|
||||||
fmt.Fprintf(w, "process_num_threads %d\n", p.NumThreads)
|
WriteGaugeUint64(w, "process_num_threads", uint64(p.NumThreads))
|
||||||
fmt.Fprintf(w, "process_resident_memory_bytes %d\n", p.Rss*4096)
|
WriteGaugeUint64(w, "process_resident_memory_bytes", uint64(p.Rss)*4096)
|
||||||
fmt.Fprintf(w, "process_start_time_seconds %d\n", startTimeSeconds)
|
WriteGaugeUint64(w, "process_start_time_seconds", uint64(startTimeSeconds))
|
||||||
fmt.Fprintf(w, "process_virtual_memory_bytes %d\n", p.Vsize)
|
WriteGaugeUint64(w, "process_virtual_memory_bytes", uint64(p.Vsize))
|
||||||
writeProcessMemMetrics(w)
|
writeProcessMemMetrics(w)
|
||||||
writeIOMetrics(w)
|
writeIOMetrics(w)
|
||||||
}
|
}
|
||||||
@ -133,12 +133,12 @@ func writeIOMetrics(w io.Writer) {
|
|||||||
writeBytes = getInt(s)
|
writeBytes = getInt(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "process_io_read_bytes_total %d\n", rchar)
|
WriteGaugeUint64(w, "process_io_read_bytes_total", uint64(rchar))
|
||||||
fmt.Fprintf(w, "process_io_written_bytes_total %d\n", wchar)
|
WriteGaugeUint64(w, "process_io_written_bytes_total", uint64(wchar))
|
||||||
fmt.Fprintf(w, "process_io_read_syscalls_total %d\n", syscr)
|
WriteGaugeUint64(w, "process_io_read_syscalls_total", uint64(syscr))
|
||||||
fmt.Fprintf(w, "process_io_write_syscalls_total %d\n", syscw)
|
WriteGaugeUint64(w, "process_io_write_syscalls_total", uint64(syscw))
|
||||||
fmt.Fprintf(w, "process_io_storage_read_bytes_total %d\n", readBytes)
|
WriteGaugeUint64(w, "process_io_storage_read_bytes_total", uint64(readBytes))
|
||||||
fmt.Fprintf(w, "process_io_storage_written_bytes_total %d\n", writeBytes)
|
WriteGaugeUint64(w, "process_io_storage_written_bytes_total", uint64(writeBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
var startTimeSeconds = time.Now().Unix()
|
var startTimeSeconds = time.Now().Unix()
|
||||||
@ -155,8 +155,8 @@ func writeFDMetrics(w io.Writer) {
|
|||||||
log.Printf("ERROR: metrics: cannot determine the limit on open file descritors: %s", err)
|
log.Printf("ERROR: metrics: cannot determine the limit on open file descritors: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "process_max_fds %d\n", maxOpenFDs)
|
WriteGaugeUint64(w, "process_max_fds", maxOpenFDs)
|
||||||
fmt.Fprintf(w, "process_open_fds %d\n", totalOpenFDs)
|
WriteGaugeUint64(w, "process_open_fds", totalOpenFDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOpenFDsCount(path string) (uint64, error) {
|
func getOpenFDsCount(path string) (uint64, error) {
|
||||||
@ -224,11 +224,11 @@ func writeProcessMemMetrics(w io.Writer) {
|
|||||||
log.Printf("ERROR: metrics: cannot determine memory status: %s", err)
|
log.Printf("ERROR: metrics: cannot determine memory status: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "process_virtual_memory_peak_bytes %d\n", ms.vmPeak)
|
WriteGaugeUint64(w, "process_virtual_memory_peak_bytes", ms.vmPeak)
|
||||||
fmt.Fprintf(w, "process_resident_memory_peak_bytes %d\n", ms.rssPeak)
|
WriteGaugeUint64(w, "process_resident_memory_peak_bytes", ms.rssPeak)
|
||||||
fmt.Fprintf(w, "process_resident_memory_anon_bytes %d\n", ms.rssAnon)
|
WriteGaugeUint64(w, "process_resident_memory_anon_bytes", ms.rssAnon)
|
||||||
fmt.Fprintf(w, "process_resident_memory_file_bytes %d\n", ms.rssFile)
|
WriteGaugeUint64(w, "process_resident_memory_file_bytes", ms.rssFile)
|
||||||
fmt.Fprintf(w, "process_resident_memory_shared_bytes %d\n", ms.rssShmem)
|
WriteGaugeUint64(w, "process_resident_memory_shared_bytes", ms.rssShmem)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
package metrics
|
package metrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"syscall"
|
"syscall"
|
||||||
@ -55,16 +54,16 @@ func writeProcessMetrics(w io.Writer) {
|
|||||||
log.Printf("ERROR: metrics: cannot read process memory information: %s", err)
|
log.Printf("ERROR: metrics: cannot read process memory information: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
stimeSeconds := (uint64(stime.HighDateTime)<<32 + uint64(stime.LowDateTime)) / 1e7
|
stimeSeconds := float64(uint64(stime.HighDateTime)<<32+uint64(stime.LowDateTime)) / 1e7
|
||||||
utimeSeconds := (uint64(utime.HighDateTime)<<32 + uint64(utime.LowDateTime)) / 1e7
|
utimeSeconds := float64(uint64(utime.HighDateTime)<<32+uint64(utime.LowDateTime)) / 1e7
|
||||||
fmt.Fprintf(w, "process_cpu_seconds_system_total %d\n", stimeSeconds)
|
WriteCounterFloat64(w, "process_cpu_seconds_system_total", stimeSeconds)
|
||||||
fmt.Fprintf(w, "process_cpu_seconds_total %d\n", stimeSeconds+utimeSeconds)
|
WriteCounterFloat64(w, "process_cpu_seconds_total", stimeSeconds+utimeSeconds)
|
||||||
fmt.Fprintf(w, "process_cpu_seconds_user_total %d\n", stimeSeconds)
|
WriteCounterFloat64(w, "process_cpu_seconds_user_total", stimeSeconds)
|
||||||
fmt.Fprintf(w, "process_pagefaults_total %d\n", mc.PageFaultCount)
|
WriteCounterUint64(w, "process_pagefaults_total", uint64(mc.PageFaultCount))
|
||||||
fmt.Fprintf(w, "process_start_time_seconds %d\n", startTime.Nanoseconds()/1e9)
|
WriteGaugeUint64(w, "process_start_time_seconds", uint64(startTime.Nanoseconds())/1e9)
|
||||||
fmt.Fprintf(w, "process_virtual_memory_bytes %d\n", mc.PrivateUsage)
|
WriteGaugeUint64(w, "process_virtual_memory_bytes", uint64(mc.PrivateUsage))
|
||||||
fmt.Fprintf(w, "process_resident_memory_peak_bytes %d\n", mc.PeakWorkingSetSize)
|
WriteGaugeUint64(w, "process_resident_memory_peak_bytes", uint64(mc.PeakWorkingSetSize))
|
||||||
fmt.Fprintf(w, "process_resident_memory_bytes %d\n", mc.WorkingSetSize)
|
WriteGaugeUint64(w, "process_resident_memory_bytes", uint64(mc.WorkingSetSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeFDMetrics(w io.Writer) {
|
func writeFDMetrics(w io.Writer) {
|
||||||
@ -80,6 +79,6 @@ func writeFDMetrics(w io.Writer) {
|
|||||||
}
|
}
|
||||||
// it seems to be hard-coded limit for 64-bit systems
|
// it seems to be hard-coded limit for 64-bit systems
|
||||||
// https://learn.microsoft.com/en-us/archive/blogs/markrussinovich/pushing-the-limits-of-windows-handles#maximum-number-of-handles
|
// https://learn.microsoft.com/en-us/archive/blogs/markrussinovich/pushing-the-limits-of-windows-handles#maximum-number-of-handles
|
||||||
fmt.Fprintf(w, "process_max_fds %d\n", 16777216)
|
WriteGaugeUint64(w, "process_max_fds", 16777216)
|
||||||
fmt.Fprintf(w, "process_open_fds %d\n", count)
|
WriteGaugeUint64(w, "process_open_fds", uint64(count))
|
||||||
}
|
}
|
||||||
|
18
set.go
18
set.go
@ -47,14 +47,30 @@ func (s *Set) WritePrometheus(w io.Writer) {
|
|||||||
sa := append([]*namedMetric(nil), s.a...)
|
sa := append([]*namedMetric(nil), s.a...)
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
prevMetricFamily := ""
|
||||||
|
for _, nm := range sa {
|
||||||
|
metricFamily := nm.family()
|
||||||
|
if metricFamily != prevMetricFamily {
|
||||||
|
// write meta info only once per metric family
|
||||||
|
metricType := nm.metric.metricType()
|
||||||
|
writeMetadataIfNeeded(&bb, metricFamily, metricType)
|
||||||
|
prevMetricFamily = metricFamily
|
||||||
|
}
|
||||||
// Call marshalTo without the global lock, since certain metric types such as Gauge
|
// Call marshalTo without the global lock, since certain metric types such as Gauge
|
||||||
// can call a callback, which, in turn, can try calling s.mu.Lock again.
|
// can call a callback, which, in turn, can try calling s.mu.Lock again.
|
||||||
for _, nm := range sa {
|
|
||||||
nm.metric.marshalTo(nm.name, &bb)
|
nm.metric.marshalTo(nm.name, &bb)
|
||||||
}
|
}
|
||||||
w.Write(bb.Bytes())
|
w.Write(bb.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeMetadataIfNeeded(w io.Writer, metricFamily, metricType string) {
|
||||||
|
if !isMetadataEnabled() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "# HELP %s\n", metricFamily)
|
||||||
|
fmt.Fprintf(w, "# TYPE %s %s\n", metricFamily, metricType)
|
||||||
|
}
|
||||||
|
|
||||||
// NewHistogram creates and returns new histogram in s with the given name.
|
// NewHistogram creates and returns new histogram in s with the given name.
|
||||||
//
|
//
|
||||||
// name must be valid Prometheus-compatible metric with possible labels.
|
// name must be valid Prometheus-compatible metric with possible labels.
|
||||||
|
@ -23,3 +23,56 @@ func ExampleSet() {
|
|||||||
// set_counter 1
|
// set_counter 1
|
||||||
// set_gauge{foo="bar"} 42
|
// set_gauge{foo="bar"} 42
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleExposeMetadata() {
|
||||||
|
metrics.ExposeMetadata(true)
|
||||||
|
defer metrics.ExposeMetadata(false)
|
||||||
|
|
||||||
|
s := metrics.NewSet()
|
||||||
|
|
||||||
|
sc := s.NewCounter("set_counter")
|
||||||
|
sc.Inc()
|
||||||
|
|
||||||
|
s.NewGauge(`unused_bytes{foo="bar"}`, func() float64 { return 58 })
|
||||||
|
s.NewGauge(`used_bytes{foo="bar"}`, func() float64 { return 42 })
|
||||||
|
s.NewGauge(`used_bytes{foo="baz"}`, func() float64 { return 43 })
|
||||||
|
|
||||||
|
h := s.NewHistogram(`request_duration_seconds{path="/foo/bar"}`)
|
||||||
|
h.Update(1)
|
||||||
|
h.Update(2)
|
||||||
|
|
||||||
|
s.NewSummary("response_size_bytes").Update(1)
|
||||||
|
|
||||||
|
// Dump metrics from s.
|
||||||
|
var bb bytes.Buffer
|
||||||
|
s.WritePrometheus(&bb)
|
||||||
|
fmt.Printf("set metrics:\n%s\n", bb.String())
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// set metrics:
|
||||||
|
// # HELP request_duration_seconds
|
||||||
|
// # TYPE request_duration_seconds histogram
|
||||||
|
// request_duration_seconds_bucket{path="/foo/bar",vmrange="8.799e-01...1.000e+00"} 1
|
||||||
|
// request_duration_seconds_bucket{path="/foo/bar",vmrange="1.896e+00...2.154e+00"} 1
|
||||||
|
// request_duration_seconds_sum{path="/foo/bar"} 3
|
||||||
|
// request_duration_seconds_count{path="/foo/bar"} 2
|
||||||
|
// # HELP response_size_bytes
|
||||||
|
// # TYPE response_size_bytes summary
|
||||||
|
// response_size_bytes_sum 1
|
||||||
|
// response_size_bytes_count 1
|
||||||
|
// response_size_bytes{quantile="0.5"} 1
|
||||||
|
// response_size_bytes{quantile="0.9"} 1
|
||||||
|
// response_size_bytes{quantile="0.97"} 1
|
||||||
|
// response_size_bytes{quantile="0.99"} 1
|
||||||
|
// response_size_bytes{quantile="1"} 1
|
||||||
|
// # HELP set_counter
|
||||||
|
// # TYPE set_counter counter
|
||||||
|
// set_counter 1
|
||||||
|
// # HELP unused_bytes
|
||||||
|
// # TYPE unused_bytes gauge
|
||||||
|
// unused_bytes{foo="bar"} 58
|
||||||
|
// # HELP used_bytes
|
||||||
|
// # TYPE used_bytes gauge
|
||||||
|
// used_bytes{foo="bar"} 42
|
||||||
|
// used_bytes{foo="baz"} 43
|
||||||
|
}
|
||||||
|
@ -119,6 +119,10 @@ func (sm *Summary) marshalTo(prefix string, w io.Writer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sm *Summary) metricType() string {
|
||||||
|
return "summary"
|
||||||
|
}
|
||||||
|
|
||||||
func splitMetricName(name string) (string, string) {
|
func splitMetricName(name string) (string, string) {
|
||||||
n := strings.IndexByte(name, '{')
|
n := strings.IndexByte(name, '{')
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
@ -196,6 +200,10 @@ func (qv *quantileValue) marshalTo(prefix string, w io.Writer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (qv *quantileValue) metricType() string {
|
||||||
|
return "unsupported"
|
||||||
|
}
|
||||||
|
|
||||||
func addTag(name, tag string) string {
|
func addTag(name, tag string) string {
|
||||||
if len(name) == 0 || name[len(name)-1] != '}' {
|
if len(name) == 0 || name[len(name)-1] != '}' {
|
||||||
return fmt.Sprintf("%s{%s}", name, tag)
|
return fmt.Sprintf("%s{%s}", name, tag)
|
||||||
|
1
vendor/github.com/valyala/fastrand/go.mod
generated
vendored
1
vendor/github.com/valyala/fastrand/go.mod
generated
vendored
@ -1 +0,0 @@
|
|||||||
module github.com/valyala/fastrand
|
|
5
vendor/github.com/valyala/histogram/go.mod
generated
vendored
5
vendor/github.com/valyala/histogram/go.mod
generated
vendored
@ -1,5 +0,0 @@
|
|||||||
module github.com/valyala/histogram
|
|
||||||
|
|
||||||
go 1.12
|
|
||||||
|
|
||||||
require github.com/valyala/fastrand v1.1.0
|
|
2
vendor/github.com/valyala/histogram/go.sum
generated
vendored
2
vendor/github.com/valyala/histogram/go.sum
generated
vendored
@ -1,2 +0,0 @@
|
|||||||
github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G8=
|
|
||||||
github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ=
|
|
5
vendor/modules.txt
vendored
5
vendor/modules.txt
vendored
@ -1,8 +1,9 @@
|
|||||||
# github.com/valyala/fastrand v1.1.0
|
# github.com/valyala/fastrand v1.1.0
|
||||||
|
## explicit
|
||||||
github.com/valyala/fastrand
|
github.com/valyala/fastrand
|
||||||
# github.com/valyala/histogram v1.2.0
|
# github.com/valyala/histogram v1.2.0
|
||||||
## explicit
|
## explicit; go 1.12
|
||||||
github.com/valyala/histogram
|
github.com/valyala/histogram
|
||||||
# golang.org/x/sys v0.15.0
|
# golang.org/x/sys v0.15.0
|
||||||
## explicit
|
## explicit; go 1.18
|
||||||
golang.org/x/sys/windows
|
golang.org/x/sys/windows
|
||||||
|
Loading…
Reference in New Issue
Block a user