Skip to content

Commit

Permalink
allow exposing meta information for registered metrics
Browse files Browse the repository at this point in the history
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.

#48
  • Loading branch information
hagen1778 committed Dec 15, 2023
1 parent 42c28a8 commit 5bb0070
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 2 deletions.
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -76,6 +76,12 @@ metrics.InitPush("http://victoria-metrics:8428/api/v1/import/prometheus", 10*tim
// metrics.InitPushWithConfig(metrics.PushConfig{URL: "http://victoria-metrics:8428/api/v1/import/prometheus", Interval: 10*time.Second})
```

By default, exposed metrics [do not have](https://github.com/VictoriaMetrics/metrics/issues/48#issuecomment-1620765811)
`TYPE` or `HELP` meta information. To enable meta information in exposition format use
[ExposeMetadata](https://pkg.go.dev/github.com/VictoriaMetrics/metrics#ExposeMetadata) function.
Please note, meta information is ignored by [VictoriaMetrics](https://docs.victoriametrics.com/) and
[vmagent](https://docs.victoriametrics.com/vmagent.html) on scrape or ingest.

See [docs](http://godoc.org/github.com/VictoriaMetrics/metrics) for more info.


Expand Down
6 changes: 6 additions & 0 deletions counter.go
Expand Up @@ -58,6 +58,12 @@ func (c *Counter) marshalTo(prefix string, w io.Writer) {
fmt.Fprintf(w, "%s %d\n", prefix, v)
}

// marshalMeta marshals c meta info with the given prefix to w.
func (c *Counter) marshalMeta(prefix string, w io.Writer) {
fmt.Fprintf(w, "# HELP %s\n", prefix)
fmt.Fprintf(w, "# TYPE %s counter\n", prefix)
}

// GetOrCreateCounter returns registered counter with the given name
// or creates new counter if the registry doesn't contain counter with
// the given name.
Expand Down
6 changes: 6 additions & 0 deletions floatcounter.go
Expand Up @@ -63,6 +63,12 @@ func (fc *FloatCounter) marshalTo(prefix string, w io.Writer) {
fmt.Fprintf(w, "%s %g\n", prefix, v)
}

// marshalMeta marshals fc meta info with the given prefix to w.
func (fc *FloatCounter) marshalMeta(prefix string, w io.Writer) {
fmt.Fprintf(w, "# HELP %s\n", prefix)
fmt.Fprintf(w, "# TYPE %s counter\n", prefix)

Check warning on line 69 in floatcounter.go

View check run for this annotation

Codecov / codecov/patch

floatcounter.go#L67-L69

Added lines #L67 - L69 were not covered by tests
}

// GetOrCreateFloatCounter returns registered FloatCounter with the given name
// or creates new FloatCounter if the registry doesn't contain FloatCounter with
// the given name.
Expand Down
6 changes: 6 additions & 0 deletions gauge.go
Expand Up @@ -46,6 +46,12 @@ func (g *Gauge) marshalTo(prefix string, w io.Writer) {
}
}

// marshalMeta marshals g meta info with the given prefix to w.
func (g *Gauge) marshalMeta(prefix string, w io.Writer) {
fmt.Fprintf(w, "# HELP %s\n", prefix)
fmt.Fprintf(w, "# TYPE %s gauge\n", prefix)
}

// GetOrCreateGauge returns registered gauge with the given name
// or creates new gauge if the registry doesn't contain gauge with
// the given name.
Expand Down
6 changes: 6 additions & 0 deletions histogram.go
Expand Up @@ -228,3 +228,9 @@ func (h *Histogram) getSum() float64 {
h.mu.Unlock()
return sum
}

// marshalMeta marshals h meta info with the given prefix to w.
func (h *Histogram) marshalMeta(prefix string, w io.Writer) {
fmt.Fprintf(w, "# HELP %s\n", prefix)
fmt.Fprintf(w, "# TYPE %s histogram\n", prefix)
}
22 changes: 22 additions & 0 deletions metrics.go
Expand Up @@ -21,12 +21,14 @@ import (

type namedMetric struct {
name string
family string
metric metric
isAux bool
}

type metric interface {
marshalTo(prefix string, w io.Writer)
marshalMeta(prefix string, w io.Writer)
}

var defaultSet = NewSet()
Expand Down Expand Up @@ -241,3 +243,23 @@ func ListMetricNames() []string {
func GetDefaultSet() *Set {
return defaultSet
}

var (
exposeMetadata bool
exposeMetadataLock sync.Mutex
)

// ExposeMetadata allows enabling adding TYPE and HELP metadata to the exposed metrics globally, for all Set.
// 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) {
exposeMetadataLock.Lock()
exposeMetadata = v
exposeMetadataLock.Unlock()
}

func isMetadataEnabled() bool {
exposeMetadataLock.Lock()
defer exposeMetadataLock.Unlock()
return exposeMetadata
}
18 changes: 16 additions & 2 deletions set.go
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"sort"
"strings"
"sync"
"time"
)
Expand Down Expand Up @@ -47,12 +48,20 @@ func (s *Set) WritePrometheus(w io.Writer) {
sa := append([]*namedMetric(nil), s.a...)
s.mu.Unlock()

// 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.
var writeMetadata = isMetadataEnabled()
var metricFamily string
for _, nm := range sa {
if writeMetadata && metricFamily != nm.family {
// write meta
metricFamily = nm.family
nm.metric.marshalMeta(metricFamily, &bb)
}
// 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.
nm.metric.marshalTo(nm.name, &bb)
}
w.Write(bb.Bytes())
return
}

// NewHistogram creates and returns new histogram in s with the given name.
Expand Down Expand Up @@ -441,8 +450,13 @@ func (s *Set) registerMetric(name string, m metric) {
func (s *Set) mustRegisterLocked(name string, m metric, isAux bool) {
nm, ok := s.m[name]
if !ok {
i := strings.IndexByte(name, '{')
if i < 0 {
i = len(name)
}
nm = &namedMetric{
name: name,
family: name[:i],
metric: m,
isAux: isAux,
}
Expand Down
56 changes: 56 additions & 0 deletions set_example_test.go
Expand Up @@ -23,3 +23,59 @@ func ExampleSet() {
// set_counter 1
// set_gauge{foo="bar"} 42
}

func ExampleExposeMetadata() {
metrics.ExposeMetadata(true)
defer func() {
metrics.ExposeMetadata(false)
}()

// Create a set with a counter
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
}
8 changes: 8 additions & 0 deletions summary.go
Expand Up @@ -119,6 +119,12 @@ func (sm *Summary) marshalTo(prefix string, w io.Writer) {
}
}

// marshalMeta marshals sm meta info with the given prefix to w.
func (sm *Summary) marshalMeta(prefix string, w io.Writer) {
fmt.Fprintf(w, "# HELP %s\n", prefix)
fmt.Fprintf(w, "# TYPE %s summary\n", prefix)
}

func splitMetricName(name string) (string, string) {
n := strings.IndexByte(name, '{')
if n < 0 {
Expand Down Expand Up @@ -196,6 +202,8 @@ func (qv *quantileValue) marshalTo(prefix string, w io.Writer) {
}
}

func (qv *quantileValue) marshalMeta(string, io.Writer) {}

Check warning on line 205 in summary.go

View check run for this annotation

Codecov / codecov/patch

summary.go#L205

Added line #L205 was not covered by tests

func addTag(name, tag string) string {
if len(name) == 0 || name[len(name)-1] != '}' {
return fmt.Sprintf("%s{%s}", name, tag)
Expand Down

0 comments on commit 5bb0070

Please sign in to comment.