diff --git a/metrics/adapters/cache_test.go b/metrics/adapters/cache_test.go index 1053d18..dc79a66 100644 --- a/metrics/adapters/cache_test.go +++ b/metrics/adapters/cache_test.go @@ -26,9 +26,9 @@ import ( func TestCache(t *testing.T) { f := metricstest.NewFactory(100 * time.Second) - c1 := f.Counter("x", nil) - g1 := f.Gauge("y", nil) - t1 := f.Timer("z", nil) + c1 := f.Counter(metrics.Options{Name: "x"}) + g1 := f.Gauge(metrics.Options{Name: "y"}) + t1 := f.Timer(metrics.Options{Name: "z"}) c := newCache() diff --git a/metrics/adapters/factory.go b/metrics/adapters/factory.go index bc2e137..e054fa2 100644 --- a/metrics/adapters/factory.go +++ b/metrics/adapters/factory.go @@ -20,9 +20,9 @@ import ( // FactoryWithTags creates metrics with fully qualified name and tags. type FactoryWithTags interface { - Counter(name string, tags map[string]string) metrics.Counter - Gauge(name string, tags map[string]string) metrics.Gauge - Timer(name string, tags map[string]string) metrics.Timer + Counter(name string, tags map[string]string, help string) metrics.Counter + Gauge(name string, tags map[string]string, help string) metrics.Gauge + Timer(name string, tags map[string]string, help string) metrics.Timer } // Options affect how the adapter factory behaves. @@ -63,32 +63,32 @@ type factory struct { cache *cache } -func (f *factory) Counter(name string, tags map[string]string) metrics.Counter { - fullName, fullTags, key := f.getKey(name, tags) +func (f *factory) Counter(options metrics.Options) metrics.Counter { + fullName, fullTags, key := f.getKey(options.Name, options.Tags) return f.cache.getOrSetCounter(key, func() metrics.Counter { - return f.factory.Counter(fullName, fullTags) + return f.factory.Counter(fullName, fullTags, options.Help) }) } -func (f *factory) Gauge(name string, tags map[string]string) metrics.Gauge { - fullName, fullTags, key := f.getKey(name, tags) +func (f *factory) Gauge(options metrics.Options) metrics.Gauge { + fullName, fullTags, key := f.getKey(options.Name, options.Tags) return f.cache.getOrSetGauge(key, func() metrics.Gauge { - return f.factory.Gauge(fullName, fullTags) + return f.factory.Gauge(fullName, fullTags, options.Help) }) } -func (f *factory) Timer(name string, tags map[string]string) metrics.Timer { - fullName, fullTags, key := f.getKey(name, tags) +func (f *factory) Timer(options metrics.Options) metrics.Timer { + fullName, fullTags, key := f.getKey(options.Name, options.Tags) return f.cache.getOrSetTimer(key, func() metrics.Timer { - return f.factory.Timer(fullName, fullTags) + return f.factory.Timer(fullName, fullTags, options.Help) }) } -func (f *factory) Namespace(name string, tags map[string]string) metrics.Factory { +func (f *factory) Namespace(scope metrics.NSOptions) metrics.Factory { return &factory{ cache: f.cache, - scope: f.subScope(name), - tags: f.mergeTags(tags), + scope: f.subScope(scope.Name), + tags: f.mergeTags(scope.Tags), factory: f.factory, Options: f.Options, } diff --git a/metrics/adapters/factory_test.go b/metrics/adapters/factory_test.go index c133248..89f9156 100644 --- a/metrics/adapters/factory_test.go +++ b/metrics/adapters/factory_test.go @@ -80,15 +80,36 @@ func TestFactory(t *testing.T) { ff := &fakeTagless{factory: local} f := WrapFactoryWithoutTags(ff, Options{}) if testCase.namespace != "" || testCase.nsTags != nil { - f = f.Namespace(testCase.namespace, testCase.nsTags) + f = f.Namespace(metrics.NSOptions{ + Name: testCase.namespace, + Tags: testCase.nsTags, + }) } - counter := f.Counter(counterPrefix+testCase.name, testCase.tags) - gauge := f.Gauge(gaugePrefix+testCase.name, testCase.tags) - timer := f.Timer(timerPrefix+testCase.name, testCase.tags) + counter := f.Counter(metrics.Options{ + Name: counterPrefix + testCase.name, + Tags: testCase.tags, + }) + gauge := f.Gauge(metrics.Options{ + Name: gaugePrefix + testCase.name, + Tags: testCase.tags, + }) + timer := f.Timer(metrics.Options{ + Name: timerPrefix + testCase.name, + Tags: testCase.tags, + }) - assert.Equal(t, counter, f.Counter(counterPrefix+testCase.name, testCase.tags)) - assert.Equal(t, gauge, f.Gauge(gaugePrefix+testCase.name, testCase.tags)) - assert.Equal(t, timer, f.Timer(timerPrefix+testCase.name, testCase.tags)) + assert.Equal(t, counter, f.Counter(metrics.Options{ + Name: counterPrefix + testCase.name, + Tags: testCase.tags, + })) + assert.Equal(t, gauge, f.Gauge(metrics.Options{ + Name: gaugePrefix + testCase.name, + Tags: testCase.tags, + })) + assert.Equal(t, timer, f.Timer(metrics.Options{ + Name: timerPrefix + testCase.name, + Tags: testCase.tags, + })) assert.Equal(t, fmt.Sprintf(testCase.fullName, counterPrefix), ff.counter) assert.Equal(t, fmt.Sprintf(testCase.fullName, gaugePrefix), ff.gauge) @@ -104,17 +125,26 @@ type fakeTagless struct { timer string } -func (f *fakeTagless) Counter(name string) metrics.Counter { +func (f *fakeTagless) Counter(name string, help string) metrics.Counter { f.counter = name - return f.factory.Counter(name, nil) + return f.factory.Counter(metrics.Options{ + Name: name, + Help: help, + }) } -func (f *fakeTagless) Gauge(name string) metrics.Gauge { +func (f *fakeTagless) Gauge(name string, help string) metrics.Gauge { f.gauge = name - return f.factory.Gauge(name, nil) + return f.factory.Gauge(metrics.Options{ + Name: name, + Help: help, + }) } -func (f *fakeTagless) Timer(name string) metrics.Timer { +func (f *fakeTagless) Timer(name string, help string) metrics.Timer { f.timer = name - return f.factory.Timer(name, nil) + return f.factory.Timer(metrics.Options{ + Name: name, + Help: help, + }) } diff --git a/metrics/adapters/tagless.go b/metrics/adapters/tagless.go index a393b0a..d9552c7 100644 --- a/metrics/adapters/tagless.go +++ b/metrics/adapters/tagless.go @@ -19,9 +19,9 @@ import "github.com/uber/jaeger-lib/metrics" // FactoryWithoutTags creates metrics based on name only, without tags. // Suitable for integrating with statsd-like backends that don't support tags. type FactoryWithoutTags interface { - Counter(name string) metrics.Counter - Gauge(name string) metrics.Gauge - Timer(name string) metrics.Timer + Counter(name string, help string) metrics.Counter + Gauge(name string, help string) metrics.Gauge + Timer(name string, help string) metrics.Timer } // WrapFactoryWithoutTags creates a real metrics.Factory that supports subscopes. @@ -41,19 +41,19 @@ type tagless struct { factory FactoryWithoutTags } -func (f *tagless) Counter(name string, tags map[string]string) metrics.Counter { +func (f *tagless) Counter(name string, tags map[string]string, help string) metrics.Counter { fullName := f.getFullName(name, tags) - return f.factory.Counter(fullName) + return f.factory.Counter(fullName, help) } -func (f *tagless) Gauge(name string, tags map[string]string) metrics.Gauge { +func (f *tagless) Gauge(name string, tags map[string]string, help string) metrics.Gauge { fullName := f.getFullName(name, tags) - return f.factory.Gauge(fullName) + return f.factory.Gauge(fullName, help) } -func (f *tagless) Timer(name string, tags map[string]string) metrics.Timer { +func (f *tagless) Timer(name string, tags map[string]string, help string) metrics.Timer { fullName := f.getFullName(name, tags) - return f.factory.Timer(fullName) + return f.factory.Timer(fullName, help) } func (f *tagless) getFullName(name string, tags map[string]string) string { diff --git a/metrics/expvar/factory.go b/metrics/expvar/factory.go index 114f092..6b42d78 100644 --- a/metrics/expvar/factory.go +++ b/metrics/expvar/factory.go @@ -36,14 +36,14 @@ type factory struct { factory xkit.Factory } -func (f *factory) Counter(name string) metrics.Counter { +func (f *factory) Counter(name string, help string) metrics.Counter { return xkit.NewCounter(f.factory.Counter(name)) } -func (f *factory) Gauge(name string) metrics.Gauge { +func (f *factory) Gauge(name string, help string) metrics.Gauge { return xkit.NewGauge(f.factory.Gauge(name)) } -func (f *factory) Timer(name string) metrics.Timer { +func (f *factory) Timer(name string, help string) metrics.Timer { return xkit.NewTimer(f.factory.Histogram(name)) } diff --git a/metrics/expvar/factory_test.go b/metrics/expvar/factory_test.go index a13f8f0..ae6650b 100644 --- a/metrics/expvar/factory_test.go +++ b/metrics/expvar/factory_test.go @@ -22,6 +22,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" ) var ( @@ -63,16 +64,37 @@ func TestFactory(t *testing.T) { } ff := f if testCase.namespace != "" || testCase.nsTags != nil { - ff = f.Namespace(testCase.namespace, testCase.nsTags) + ff = f.Namespace(metrics.NSOptions{ + Name: testCase.namespace, + Tags: testCase.nsTags, + }) } - counter := ff.Counter(counterPrefix+testCase.name, testCase.tags) - gauge := ff.Gauge(gaugePrefix+testCase.name, testCase.tags) - timer := ff.Timer(timerPrefix+testCase.name, testCase.tags) + counter := ff.Counter(metrics.Options{ + Name: counterPrefix + testCase.name, + Tags: testCase.tags, + }) + gauge := ff.Gauge(metrics.Options{ + Name: gaugePrefix + testCase.name, + Tags: testCase.tags, + }) + timer := ff.Timer(metrics.Options{ + Name: timerPrefix + testCase.name, + Tags: testCase.tags, + }) // register second time, should not panic - ff.Counter(counterPrefix+testCase.name, testCase.tags) - ff.Gauge(gaugePrefix+testCase.name, testCase.tags) - ff.Timer(timerPrefix+testCase.name, testCase.tags) + ff.Counter(metrics.Options{ + Name: counterPrefix + testCase.name, + Tags: testCase.tags, + }) + ff.Gauge(metrics.Options{ + Name: gaugePrefix + testCase.name, + Tags: testCase.tags, + }) + ff.Timer(metrics.Options{ + Name: timerPrefix + testCase.name, + Tags: testCase.tags, + }) counter.Inc(42) gauge.Update(42) diff --git a/metrics/factory.go b/metrics/factory.go index a744a89..7cca820 100644 --- a/metrics/factory.go +++ b/metrics/factory.go @@ -14,14 +14,27 @@ package metrics +// NSOptions defines the name and tags map associated with a metric +type NSOptions struct { + Name string + Tags map[string]string +} + +// Options defines the information associated with a metric +type Options struct { + Name string + Tags map[string]string + Help string +} + // Factory creates new metrics type Factory interface { - Counter(name string, tags map[string]string) Counter - Timer(name string, tags map[string]string) Timer - Gauge(name string, tags map[string]string) Gauge + Counter(metric Options) Counter + Timer(metric Options) Timer + Gauge(metric Options) Gauge // Namespace returns a nested metrics factory. - Namespace(name string, tags map[string]string) Factory + Namespace(scope NSOptions) Factory } // NullFactory is a metrics factory that returns NullCounter, NullTimer, and NullGauge. @@ -29,7 +42,13 @@ var NullFactory Factory = nullFactory{} type nullFactory struct{} -func (nullFactory) Counter(name string, tags map[string]string) Counter { return NullCounter } -func (nullFactory) Timer(name string, tags map[string]string) Timer { return NullTimer } -func (nullFactory) Gauge(name string, tags map[string]string) Gauge { return NullGauge } -func (nullFactory) Namespace(name string, tags map[string]string) Factory { return NullFactory } +func (nullFactory) Counter(options Options) Counter { + return NullCounter +} +func (nullFactory) Timer(options Options) Timer { + return NullTimer +} +func (nullFactory) Gauge(options Options) Gauge { + return NullGauge +} +func (nullFactory) Namespace(scope NSOptions) Factory { return NullFactory } diff --git a/metrics/go-kit/factory.go b/metrics/go-kit/factory.go index d19c23c..30aa5c2 100644 --- a/metrics/go-kit/factory.go +++ b/metrics/go-kit/factory.go @@ -103,8 +103,8 @@ func (f *factory) nameAndTagsList(nom string, tags map[string]string) (name stri return } -func (f *factory) Counter(name string, tags map[string]string) metrics.Counter { - name, tagsList := f.nameAndTagsList(name, tags) +func (f *factory) Counter(options metrics.Options) metrics.Counter { + name, tagsList := f.nameAndTagsList(options.Name, options.Tags) counter := f.factory.Counter(name) if len(tagsList) > 0 { counter = counter.With(tagsList...) @@ -112,8 +112,8 @@ func (f *factory) Counter(name string, tags map[string]string) metrics.Counter { return NewCounter(counter) } -func (f *factory) Timer(name string, tags map[string]string) metrics.Timer { - name, tagsList := f.nameAndTagsList(name, tags) +func (f *factory) Timer(options metrics.Options) metrics.Timer { + name, tagsList := f.nameAndTagsList(options.Name, options.Tags) hist := f.factory.Histogram(name) if len(tagsList) > 0 { hist = hist.With(tagsList...) @@ -121,8 +121,8 @@ func (f *factory) Timer(name string, tags map[string]string) metrics.Timer { return NewTimer(hist) } -func (f *factory) Gauge(name string, tags map[string]string) metrics.Gauge { - name, tagsList := f.nameAndTagsList(name, tags) +func (f *factory) Gauge(options metrics.Options) metrics.Gauge { + name, tagsList := f.nameAndTagsList(options.Name, options.Tags) gauge := f.factory.Gauge(name) if len(tagsList) > 0 { gauge = gauge.With(tagsList...) @@ -130,10 +130,10 @@ func (f *factory) Gauge(name string, tags map[string]string) metrics.Gauge { return NewGauge(gauge) } -func (f *factory) Namespace(name string, tags map[string]string) metrics.Factory { +func (f *factory) Namespace(scope metrics.NSOptions) metrics.Factory { return &factory{ - scope: f.subScope(name), - tags: f.mergeTags(tags), + scope: f.subScope(scope.Name), + tags: f.mergeTags(scope.Tags), factory: f.factory, scopeSep: f.scopeSep, tagsSep: f.tagsSep, diff --git a/metrics/go-kit/factory_test.go b/metrics/go-kit/factory_test.go index d5e7d0e..dcc5bd6 100644 --- a/metrics/go-kit/factory_test.go +++ b/metrics/go-kit/factory_test.go @@ -153,7 +153,10 @@ func TestFactoryScoping(t *testing.T) { t.Run(factoryName+"_"+testSuite.metricType+"_"+testCase.expName, func(t *testing.T) { f := Wrap(testCase.prefix, testCase.f, testCase.options...) if testCase.useNamespace { - f = f.Namespace(testCase.namespace, testCase.namespaceTags) + f = f.Namespace(metrics.NSOptions{ + Name: testCase.namespace, + Tags: testCase.namespaceTags, + }) } name, labels := testSuite.metricFunc(t, testCase, f) assert.Equal(t, testCase.expName, name()) @@ -166,7 +169,10 @@ func TestFactoryScoping(t *testing.T) { } func testCounter(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { - c := f.Counter(testCase.name, testCase.tags) + c := f.Counter(metrics.Options{ + Name: testCase.name, + Tags: testCase.tags, + }) c.Inc(123) kc := c.(*Counter).counter var gc *generic.Counter @@ -182,7 +188,10 @@ func testCounter(t *testing.T, testCase testCase, f metrics.Factory) (name func( } func testGauge(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { - g := f.Gauge(testCase.name, testCase.tags) + g := f.Gauge(metrics.Options{ + Name: testCase.name, + Tags: testCase.tags, + }) g.Update(123) gg := g.(*Gauge).gauge.(*generic.Gauge) assert.EqualValues(t, 123.0, gg.Value()) @@ -192,7 +201,10 @@ func testGauge(t *testing.T, testCase testCase, f metrics.Factory) (name func() } func testTimer(t *testing.T, testCase testCase, f metrics.Factory) (name func() string, labels func() []string) { - tm := f.Timer(testCase.name, testCase.tags) + tm := f.Timer(metrics.Options{ + Name: testCase.name, + Tags: testCase.tags, + }) tm.Record(123 * time.Millisecond) gt := tm.(*Timer).hist.(*generic.Histogram) assert.InDelta(t, 0.123, gt.Quantile(0.9), 0.00001) diff --git a/metrics/go-kit/influx/factory_test.go b/metrics/go-kit/influx/factory_test.go index f4dc71c..3f29424 100644 --- a/metrics/go-kit/influx/factory_test.go +++ b/metrics/go-kit/influx/factory_test.go @@ -11,6 +11,7 @@ import ( influxdb "github.com/influxdata/influxdb/client/v2" "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" "github.com/uber/jaeger-lib/metrics/go-kit" ) @@ -19,7 +20,10 @@ func TestCounter(t *testing.T) { inf := NewFactory(in) wf := xkit.Wrap("namespace", inf) - c := wf.Counter("gokit.infl-counter", map[string]string{"label": "val1"}) + c := wf.Counter(metrics.Options{ + Name: "gokit.infl-counter", + Tags: map[string]string{"label": "val1"}, + }) c.Inc(7) assert.Contains(t, reportToString(in), "namespace.gokit.infl-counter,label=val1 count=7") @@ -30,7 +34,10 @@ func TestGauge(t *testing.T) { inf := NewFactory(in) wf := xkit.Wrap("namespace", inf) - g := wf.Gauge("gokit.infl-gauge", map[string]string{"x": "y"}) + g := wf.Gauge(metrics.Options{ + Name: "gokit.infl-gauge", + Tags: map[string]string{"x": "y"}, + }) g.Update(42) assert.Contains(t, reportToString(in), "namespace.gokit.infl-gauge,x=y value=42") @@ -41,7 +48,10 @@ func TestTimer(t *testing.T) { inf := NewFactory(in) wf := xkit.Wrap("namespace", inf) - timer := wf.Timer("gokit.infl-timer", map[string]string{"x": "y"}) + timer := wf.Timer(metrics.Options{ + Name: "gokit.infl-timer", + Tags: map[string]string{"x": "y"}, + }) timer.Record(time.Second * 1) timer.Record(time.Second * 1) timer.Record(time.Second * 10) @@ -54,9 +64,15 @@ func TestWrapperNamespaces(t *testing.T) { inf := NewFactory(in) wf := xkit.Wrap("namespace", inf) - wf = wf.Namespace("bar", map[string]string{"bar_tag": "bar_tag"}) + wf = wf.Namespace(metrics.NSOptions{ + Name: "bar", + Tags: map[string]string{"bar_tag": "bar_tag"}, + }) - c := wf.Counter("gokit.prom-wrapped-counter", map[string]string{"x": "y"}) + c := wf.Counter(metrics.Options{ + Name: "gokit.prom-wrapped-counter", + Tags: map[string]string{"x": "y"}, + }) c.Inc(42) assert.Contains(t, reportToString(in), "namespace.bar.gokit.prom-wrapped-counter,bar_tag=bar_tag,x=y count=42") diff --git a/metrics/metrics.go b/metrics/metrics.go index 4c36b75..fdea7e2 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -72,13 +72,19 @@ func InitOrError(m interface{}, factory Factory, globalTags map[string]string) e tags[tag[0]] = tag[1] } } + help := field.Tag.Get("help") var obj interface{} + options := Options{ + Name: metric, + Tags: tags, + Help: help, + } if field.Type.AssignableTo(counterPtrType) { - obj = factory.Counter(metric, tags) + obj = factory.Counter(options) } else if field.Type.AssignableTo(gaugePtrType) { - obj = factory.Gauge(metric, tags) + obj = factory.Gauge(options) } else if field.Type.AssignableTo(timerPtrType) { - obj = factory.Timer(metric, tags) + obj = factory.Timer(options) } else { return fmt.Errorf( "Field %s is not a pointer to timer, gauge, or counter", diff --git a/metrics/metrics_test.go b/metrics/metrics_test.go index 3316a31..f27f056 100644 --- a/metrics/metrics_test.go +++ b/metrics/metrics_test.go @@ -86,8 +86,18 @@ func TestInitPanic(t *testing.T) { func TestNullMetrics(t *testing.T) { // This test is just for cover - metrics.NullFactory.Timer("name", nil).Record(0) - metrics.NullFactory.Counter("name", nil).Inc(0) - metrics.NullFactory.Gauge("name", nil).Update(0) - metrics.NullFactory.Namespace("name", nil).Gauge("name2", nil).Update(0) + metrics.NullFactory.Timer(metrics.Options{ + Name: "name", + }).Record(0) + metrics.NullFactory.Counter(metrics.Options{ + Name: "name", + }).Inc(0) + metrics.NullFactory.Gauge(metrics.Options{ + Name: "name", + }).Update(0) + metrics.NullFactory.Namespace(metrics.NSOptions{ + Name: "name", + }).Gauge(metrics.Options{ + Name: "name2", + }).Update(0) } diff --git a/metrics/metricstest/local.go b/metrics/metricstest/local.go index e727b5f..ea543a7 100644 --- a/metrics/metricstest/local.go +++ b/metrics/metricstest/local.go @@ -279,43 +279,43 @@ func (l *Factory) newNamespace(name string) string { } // Counter returns a local stats counter -func (l *Factory) Counter(name string, tags map[string]string) metrics.Counter { +func (l *Factory) Counter(options metrics.Options) metrics.Counter { return &localCounter{ stats{ - name: l.newNamespace(name), - tags: l.appendTags(tags), + name: l.newNamespace(options.Name), + tags: l.appendTags(options.Tags), localBackend: l.Backend, }, } } // Timer returns a local stats timer. -func (l *Factory) Timer(name string, tags map[string]string) metrics.Timer { +func (l *Factory) Timer(options metrics.Options) metrics.Timer { return &localTimer{ stats{ - name: l.newNamespace(name), - tags: l.appendTags(tags), + name: l.newNamespace(options.Name), + tags: l.appendTags(options.Tags), localBackend: l.Backend, }, } } // Gauge returns a local stats gauge. -func (l *Factory) Gauge(name string, tags map[string]string) metrics.Gauge { +func (l *Factory) Gauge(options metrics.Options) metrics.Gauge { return &localGauge{ stats{ - name: l.newNamespace(name), - tags: l.appendTags(tags), + name: l.newNamespace(options.Name), + tags: l.appendTags(options.Tags), localBackend: l.Backend, }, } } // Namespace returns a new namespace. -func (l *Factory) Namespace(name string, tags map[string]string) metrics.Factory { +func (l *Factory) Namespace(scope metrics.NSOptions) metrics.Factory { return &Factory{ - namespace: l.newNamespace(name), - tags: l.appendTags(tags), + namespace: l.newNamespace(scope.Name), + tags: l.appendTags(scope.Tags), Backend: l.Backend, } } diff --git a/metrics/metricstest/local_test.go b/metrics/metricstest/local_test.go index ae21589..9aa73ef 100644 --- a/metrics/metricstest/local_test.go +++ b/metrics/metricstest/local_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/uber/jaeger-lib/metrics" ) func TestLocalMetrics(t *testing.T) { @@ -15,15 +16,40 @@ func TestLocalMetrics(t *testing.T) { f := NewFactory(0) defer f.Stop() - f.Counter("my-counter", tags).Inc(4) - f.Counter("my-counter", tags).Inc(6) - f.Counter("my-counter", nil).Inc(6) - f.Counter("other-counter", nil).Inc(8) - f.Gauge("my-gauge", nil).Update(25) - f.Gauge("my-gauge", nil).Update(43) - f.Gauge("other-gauge", nil).Update(74) - f.Namespace("namespace", tags).Counter("my-counter", nil).Inc(7) - f.Namespace("ns.subns", nil).Counter("", map[string]string{"service": "a-service"}).Inc(9) + f.Counter(metrics.Options{ + Name: "my-counter", + Tags: tags, + }).Inc(4) + f.Counter(metrics.Options{ + Name: "my-counter", + Tags: tags, + }).Inc(6) + f.Counter(metrics.Options{ + Name: "my-counter", + }).Inc(6) + f.Counter(metrics.Options{ + Name: "other-counter", + }).Inc(8) + f.Gauge(metrics.Options{ + Name: "my-gauge", + }).Update(25) + f.Gauge(metrics.Options{ + Name: "my-gauge", + }).Update(43) + f.Gauge(metrics.Options{ + Name: "other-gauge", + }).Update(74) + f.Namespace(metrics.NSOptions{ + Name: "namespace", + Tags: tags, + }).Counter(metrics.Options{ + Name: "my-counter", + }).Inc(7) + f.Namespace(metrics.NSOptions{ + Name: "ns.subns", + }).Counter(metrics.Options{ + Tags: map[string]string{"service": "a-service"}, + }).Inc(9) timings := map[string][]time.Duration{ "foo-latency": { @@ -42,7 +68,9 @@ func TestLocalMetrics(t *testing.T) { for metric, timing := range timings { for _, d := range timing { - f.Timer(metric, nil).Record(d) + f.Timer(metrics.Options{ + Name: metric, + }).Record(d) } } @@ -90,7 +118,9 @@ func TestLocalMetricsInterval(t *testing.T) { f := NewFactory(refreshInterval) defer f.Stop() - f.Timer("timer", nil).Record(1) + f.Timer(metrics.Options{ + Name: "timer", + }).Record(1) f.tm.Lock() timer := f.timers["timer"] diff --git a/metrics/multi/multi.go b/metrics/multi/multi.go index c3791b6..aa84d29 100644 --- a/metrics/multi/multi.go +++ b/metrics/multi/multi.go @@ -43,12 +43,12 @@ func (c *counter) Inc(delta int64) { } // Counter implements metrics.Factory interface -func (f *Factory) Counter(name string, tags map[string]string) metrics.Counter { +func (f *Factory) Counter(options metrics.Options) metrics.Counter { counter := &counter{ counters: make([]metrics.Counter, len(f.factories)), } for i, factory := range f.factories { - counter.counters[i] = factory.Counter(name, tags) + counter.counters[i] = factory.Counter(options) } return counter } @@ -64,12 +64,12 @@ func (t *timer) Record(delta time.Duration) { } // Timer implements metrics.Factory interface -func (f *Factory) Timer(name string, tags map[string]string) metrics.Timer { +func (f *Factory) Timer(options metrics.Options) metrics.Timer { timer := &timer{ timers: make([]metrics.Timer, len(f.factories)), } for i, factory := range f.factories { - timer.timers[i] = factory.Timer(name, tags) + timer.timers[i] = factory.Timer(options) } return timer } @@ -85,23 +85,23 @@ func (t *gauge) Update(value int64) { } // Gauge implements metrics.Factory interface -func (f *Factory) Gauge(name string, tags map[string]string) metrics.Gauge { +func (f *Factory) Gauge(options metrics.Options) metrics.Gauge { gauge := &gauge{ gauges: make([]metrics.Gauge, len(f.factories)), } for i, factory := range f.factories { - gauge.gauges[i] = factory.Gauge(name, tags) + gauge.gauges[i] = factory.Gauge(options) } return gauge } // Namespace implements metrics.Factory interface -func (f *Factory) Namespace(name string, tags map[string]string) metrics.Factory { +func (f *Factory) Namespace(scope metrics.NSOptions) metrics.Factory { newFactory := &Factory{ factories: make([]metrics.Factory, len(f.factories)), } for i, factory := range f.factories { - newFactory.factories[i] = factory.Namespace(name, tags) + newFactory.factories[i] = factory.Namespace(scope) } return newFactory } diff --git a/metrics/multi/multi_test.go b/metrics/multi/multi_test.go index 1ad0a03..923fc9d 100644 --- a/metrics/multi/multi_test.go +++ b/metrics/multi/multi_test.go @@ -15,11 +15,22 @@ func TestMultiFactory(t *testing.T) { f1 := metricstest.NewFactory(time.Second) f2 := metricstest.NewFactory(time.Second) multi1 := New(f1, f2) - multi2 := multi1.Namespace("ns2", nil) + multi2 := multi1.Namespace(metrics.NSOptions{ + Name: "ns2", + }) tags := map[string]string{"x": "y"} - multi2.Counter("counter", tags).Inc(42) - multi2.Gauge("gauge", tags).Update(42) - multi2.Timer("timer", tags).Record(42 * time.Millisecond) + multi2.Counter(metrics.Options{ + Name: "counter", + Tags: tags, + }).Inc(42) + multi2.Gauge(metrics.Options{ + Name: "gauge", + Tags: tags, + }).Update(42) + multi2.Timer(metrics.Options{ + Name: "timer", + Tags: tags, + }).Record(42 * time.Millisecond) for _, f := range []*metricstest.Factory{f1, f2} { f.AssertCounterMetrics(t, diff --git a/metrics/prometheus/factory.go b/metrics/prometheus/factory.go index 1050cb1..7641361 100644 --- a/metrics/prometheus/factory.go +++ b/metrics/prometheus/factory.go @@ -125,13 +125,17 @@ func newFactory(parent *Factory, scope string, tags map[string]string) *Factory } // Counter implements Counter of metrics.Factory. -func (f *Factory) Counter(name string, tags map[string]string) metrics.Counter { - name = counterNamingConvention(f.subScope(name)) - tags = f.mergeTags(tags) +func (f *Factory) Counter(options metrics.Options) metrics.Counter { + help := strings.TrimSpace(options.Help) + if len(help) == 0 { + help = options.Name + } + name := counterNamingConvention(f.subScope(options.Name)) + tags := f.mergeTags(options.Tags) labelNames := f.tagNames(tags) opts := prometheus.CounterOpts{ Name: name, - Help: name, + Help: help, } cv := f.cache.getOrMakeCounterVec(opts, labelNames) return &counter{ @@ -140,13 +144,17 @@ func (f *Factory) Counter(name string, tags map[string]string) metrics.Counter { } // Gauge implements Gauge of metrics.Factory. -func (f *Factory) Gauge(name string, tags map[string]string) metrics.Gauge { - name = f.subScope(name) - tags = f.mergeTags(tags) +func (f *Factory) Gauge(options metrics.Options) metrics.Gauge { + help := strings.TrimSpace(options.Help) + if len(help) == 0 { + help = options.Name + } + name := f.subScope(options.Name) + tags := f.mergeTags(options.Tags) labelNames := f.tagNames(tags) opts := prometheus.GaugeOpts{ Name: name, - Help: name, + Help: help, } gv := f.cache.getOrMakeGaugeVec(opts, labelNames) return &gauge{ @@ -155,13 +163,17 @@ func (f *Factory) Gauge(name string, tags map[string]string) metrics.Gauge { } // Timer implements Timer of metrics.Factory. -func (f *Factory) Timer(name string, tags map[string]string) metrics.Timer { - name = f.subScope(name) - tags = f.mergeTags(tags) +func (f *Factory) Timer(options metrics.Options) metrics.Timer { + help := strings.TrimSpace(options.Help) + if len(help) == 0 { + help = options.Name + } + name := f.subScope(options.Name) + tags := f.mergeTags(options.Tags) labelNames := f.tagNames(tags) opts := prometheus.HistogramOpts{ Name: name, - Help: name, + Help: help, Buckets: f.buckets, } hv := f.cache.getOrMakeHistogramVec(opts, labelNames) @@ -171,8 +183,8 @@ func (f *Factory) Timer(name string, tags map[string]string) metrics.Timer { } // Namespace implements Namespace of metrics.Factory. -func (f *Factory) Namespace(name string, tags map[string]string) metrics.Factory { - return newFactory(f, f.subScope(name), f.mergeTags(tags)) +func (f *Factory) Namespace(scope metrics.NSOptions) metrics.Factory { + return newFactory(f, f.subScope(scope.Name), f.mergeTags(scope.Tags)) } type counter struct { diff --git a/metrics/prometheus/factory_test.go b/metrics/prometheus/factory_test.go index 9c42da0..9615f1a 100644 --- a/metrics/prometheus/factory_test.go +++ b/metrics/prometheus/factory_test.go @@ -37,7 +37,13 @@ func TestOptions(t *testing.T) { func TestSeparator(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry), WithSeparator(SeparatorColon)) - c1 := f1.Namespace("bender", nil).Counter("rodriguez", map[string]string{"a": "b"}) + c1 := f1.Namespace(metrics.NSOptions{ + Name: "bender", + }).Counter(metrics.Options{ + Name: "rodriguez", + Tags: map[string]string{"a": "b"}, + Help: "Help message", + }) c1.Inc(1) snapshot, err := registry.Gather() require.NoError(t, err) @@ -48,13 +54,28 @@ func TestSeparator(t *testing.T) { func TestCounter(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry)) - fDummy := f1.Namespace("", nil) - f2 := fDummy.Namespace("bender", map[string]string{"a": "b"}) - f3 := f2.Namespace("", nil) + fDummy := f1.Namespace(metrics.NSOptions{}) + f2 := fDummy.Namespace(metrics.NSOptions{ + Name: "bender", + Tags: map[string]string{"a": "b"}, + }) + f3 := f2.Namespace(metrics.NSOptions{}) - c1 := f2.Counter("rodriguez", map[string]string{"x": "y"}) - c2 := f2.Counter("rodriguez", map[string]string{"x": "z"}) - c3 := f3.Counter("rodriguez", map[string]string{"x": "z"}) // same tags as c2, but from f3 + c1 := f2.Counter(metrics.Options{ + Name: "rodriguez", + Tags: map[string]string{"x": "y"}, + Help: "Help message", + }) + c2 := f2.Counter(metrics.Options{ + Name: "rodriguez", + Tags: map[string]string{"x": "z"}, + Help: "Help message", + }) + c3 := f3.Counter(metrics.Options{ + Name: "rodriguez", + Tags: map[string]string{"x": "z"}, + Help: "Help message", + }) // same tags as c2, but from f3 c1.Inc(1) c1.Inc(2) c2.Inc(3) @@ -63,6 +84,8 @@ func TestCounter(t *testing.T) { snapshot, err := registry.Gather() require.NoError(t, err) + assert.EqualValues(t, "Help message", snapshot[0].GetHelp()) + m1 := findMetric(t, snapshot, "bender_rodriguez_total", map[string]string{"a": "b", "x": "y"}) assert.EqualValues(t, 3, m1.GetCounter().GetValue(), "%+v", m1) @@ -70,14 +93,46 @@ func TestCounter(t *testing.T) { assert.EqualValues(t, 7, m2.GetCounter().GetValue(), "%+v", m2) } +func TestCounterDefaultHelp(t *testing.T) { + registry := prometheus.NewPedanticRegistry() + f1 := New(WithRegisterer(registry)) + c1 := f1.Counter(metrics.Options{ + Name: "rodriguez", + Tags: map[string]string{"x": "y"}, + }) + c1.Inc(1) + + snapshot, err := registry.Gather() + require.NoError(t, err) + + assert.EqualValues(t, "rodriguez", snapshot[0].GetHelp()) +} + func TestGauge(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry)) - f2 := f1.Namespace("bender", map[string]string{"a": "b"}) - f3 := f2.Namespace("", map[string]string{"a": "b"}) // essentially same as f2 - g1 := f2.Gauge("rodriguez", map[string]string{"x": "y"}) - g2 := f2.Gauge("rodriguez", map[string]string{"x": "z"}) - g3 := f3.Gauge("rodriguez", map[string]string{"x": "z"}) // same as g2, but from f3 + f2 := f1.Namespace(metrics.NSOptions{ + Name: "bender", + Tags: map[string]string{"a": "b"}, + }) + f3 := f2.Namespace(metrics.NSOptions{ + Tags: map[string]string{"a": "b"}, + }) // essentially same as f2 + g1 := f2.Gauge(metrics.Options{ + Name: "rodriguez", + Tags: map[string]string{"x": "y"}, + Help: "Help message", + }) + g2 := f2.Gauge(metrics.Options{ + Name: "rodriguez", + Tags: map[string]string{"x": "z"}, + Help: "Help message", + }) + g3 := f3.Gauge(metrics.Options{ + Name: "rodriguez", + Tags: map[string]string{"x": "z"}, + Help: "Help message", + }) // same as g2, but from f3 g1.Update(1) g1.Update(2) g2.Update(3) @@ -86,6 +141,8 @@ func TestGauge(t *testing.T) { snapshot, err := registry.Gather() require.NoError(t, err) + assert.EqualValues(t, "Help message", snapshot[0].GetHelp()) + m1 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "y"}) assert.EqualValues(t, 2, m1.GetGauge().GetValue(), "%+v", m1) @@ -93,14 +150,46 @@ func TestGauge(t *testing.T) { assert.EqualValues(t, 4, m2.GetGauge().GetValue(), "%+v", m2) } +func TestGaugeDefaultHelp(t *testing.T) { + registry := prometheus.NewPedanticRegistry() + f1 := New(WithRegisterer(registry)) + g1 := f1.Gauge(metrics.Options{ + Name: "rodriguez", + Tags: map[string]string{"x": "y"}, + }) + g1.Update(1) + + snapshot, err := registry.Gather() + require.NoError(t, err) + + assert.EqualValues(t, "rodriguez", snapshot[0].GetHelp()) +} + func TestTimer(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry)) - f2 := f1.Namespace("bender", map[string]string{"a": "b"}) - f3 := f2.Namespace("", map[string]string{"a": "b"}) // essentially same as f2 - t1 := f2.Timer("rodriguez", map[string]string{"x": "y"}) - t2 := f2.Timer("rodriguez", map[string]string{"x": "z"}) - t3 := f3.Timer("rodriguez", map[string]string{"x": "z"}) // same as t2, but from f3 + f2 := f1.Namespace(metrics.NSOptions{ + Name: "bender", + Tags: map[string]string{"a": "b"}, + }) + f3 := f2.Namespace(metrics.NSOptions{ + Tags: map[string]string{"a": "b"}, + }) // essentially same as f2 + t1 := f2.Timer(metrics.Options{ + Name: "rodriguez", + Tags: map[string]string{"x": "y"}, + Help: "Help message", + }) + t2 := f2.Timer(metrics.Options{ + Name: "rodriguez", + Tags: map[string]string{"x": "z"}, + Help: "Help message", + }) + t3 := f3.Timer(metrics.Options{ + Name: "rodriguez", + Tags: map[string]string{"x": "z"}, + Help: "Help message", + }) // same as t2, but from f3 t1.Record(1 * time.Second) t1.Record(2 * time.Second) t2.Record(3 * time.Second) @@ -109,6 +198,8 @@ func TestTimer(t *testing.T) { snapshot, err := registry.Gather() require.NoError(t, err) + assert.EqualValues(t, "Help message", snapshot[0].GetHelp()) + m1 := findMetric(t, snapshot, "bender_rodriguez", map[string]string{"a": "b", "x": "y"}) assert.EqualValues(t, 2, m1.GetHistogram().GetSampleCount(), "%+v", m1) assert.EqualValues(t, 3, m1.GetHistogram().GetSampleSum(), "%+v", m1) @@ -136,11 +227,29 @@ func TestTimer(t *testing.T) { } } +func TestTimerDefaultHelp(t *testing.T) { + registry := prometheus.NewPedanticRegistry() + f1 := New(WithRegisterer(registry)) + t1 := f1.Timer(metrics.Options{ + Name: "rodriguez", + Tags: map[string]string{"x": "y"}, + }) + t1.Record(1 * time.Second) + + snapshot, err := registry.Gather() + require.NoError(t, err) + + assert.EqualValues(t, "rodriguez", snapshot[0].GetHelp()) +} + func TestTimerCustomBuckets(t *testing.T) { registry := prometheus.NewPedanticRegistry() f1 := New(WithRegisterer(registry), WithBuckets([]float64{1.5})) // dot and dash in the metric name will be replaced with underscore - t1 := f1.Timer("bender.bending-rodriguez", map[string]string{"x": "y"}) + t1 := f1.Timer(metrics.Options{ + Name: "bender.bending-rodriguez", + Tags: map[string]string{"x": "y"}, + }) t1.Record(1 * time.Second) t1.Record(2 * time.Second) diff --git a/metrics/tally/factory.go b/metrics/tally/factory.go index 7923772..b66f987 100644 --- a/metrics/tally/factory.go +++ b/metrics/tally/factory.go @@ -32,32 +32,32 @@ type factory struct { tally tally.Scope } -func (f *factory) Counter(name string, tags map[string]string) metrics.Counter { +func (f *factory) Counter(options metrics.Options) metrics.Counter { scope := f.tally - if len(tags) > 0 { - scope = scope.Tagged(tags) + if len(options.Tags) > 0 { + scope = scope.Tagged(options.Tags) } - return NewCounter(scope.Counter(name)) + return NewCounter(scope.Counter(options.Name)) } -func (f *factory) Gauge(name string, tags map[string]string) metrics.Gauge { +func (f *factory) Gauge(options metrics.Options) metrics.Gauge { scope := f.tally - if len(tags) > 0 { - scope = scope.Tagged(tags) + if len(options.Tags) > 0 { + scope = scope.Tagged(options.Tags) } - return NewGauge(scope.Gauge(name)) + return NewGauge(scope.Gauge(options.Name)) } -func (f *factory) Timer(name string, tags map[string]string) metrics.Timer { +func (f *factory) Timer(options metrics.Options) metrics.Timer { scope := f.tally - if len(tags) > 0 { - scope = scope.Tagged(tags) + if len(options.Tags) > 0 { + scope = scope.Tagged(options.Tags) } - return NewTimer(scope.Timer(name)) + return NewTimer(scope.Timer(options.Name)) } -func (f *factory) Namespace(name string, tags map[string]string) metrics.Factory { +func (f *factory) Namespace(scope metrics.NSOptions) metrics.Factory { return &factory{ - tally: f.tally.SubScope(name).Tagged(tags), + tally: f.tally.SubScope(scope.Name).Tagged(scope.Tags), } } diff --git a/metrics/tally/factory_test.go b/metrics/tally/factory_test.go index fbbbe20..b21b8fc 100644 --- a/metrics/tally/factory_test.go +++ b/metrics/tally/factory_test.go @@ -5,18 +5,31 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/uber/jaeger-lib/metrics" "github.com/uber-go/tally" ) func TestFactory(t *testing.T) { testScope := tally.NewTestScope("pre", map[string]string{"a": "b"}) - factory := Wrap(testScope).Namespace("fix", map[string]string{"c": "d"}) - counter := factory.Counter("counter", map[string]string{"x": "y"}) + factory := Wrap(testScope).Namespace(metrics.NSOptions{ + Name: "fix", + Tags: map[string]string{"c": "d"}, + }) + counter := factory.Counter(metrics.Options{ + Name: "counter", + Tags: map[string]string{"x": "y"}, + }) counter.Inc(42) - gauge := factory.Gauge("gauge", map[string]string{"x": "y"}) + gauge := factory.Gauge(metrics.Options{ + Name: "gauge", + Tags: map[string]string{"x": "y"}, + }) gauge.Update(42) - timer := factory.Timer("timer", map[string]string{"x": "y"}) + timer := factory.Timer(metrics.Options{ + Name: "timer", + Tags: map[string]string{"x": "y"}, + }) timer.Record(42 * time.Millisecond) snapshot := testScope.Snapshot()