diff --git a/api/v1/metric_routes_test.go b/api/v1/metric_routes_test.go index 80d37fe4ca6..9cb661169c8 100644 --- a/api/v1/metric_routes_test.go +++ b/api/v1/metric_routes_test.go @@ -47,13 +47,15 @@ func TestGetMetrics(t *testing.T) { logger.SetOutput(testutils.NewTestOutput(t)) registry := metrics.NewRegistry() builtinMetrics := metrics.RegisterBuiltinMetrics(registry) + testMetric, err := registry.NewMetric("my_metric", stats.Trend, stats.Time) + require.NoError(t, err) execScheduler, err := local.NewExecutionScheduler(&minirunner.MiniRunner{}, builtinMetrics, logger) require.NoError(t, err) engine, err := core.NewEngine(execScheduler, lib.Options{}, lib.RuntimeOptions{}, nil, logger, registry) require.NoError(t, err) engine.MetricsEngine.ObservedMetrics = map[string]*stats.Metric{ - "my_metric": stats.New("my_metric", stats.Trend, stats.Time), + "my_metric": testMetric, } engine.MetricsEngine.ObservedMetrics["my_metric"].Tainted = null.BoolFrom(true) @@ -106,6 +108,8 @@ func TestGetMetric(t *testing.T) { logger := logrus.New() logger.SetOutput(testutils.NewTestOutput(t)) registry := metrics.NewRegistry() + testMetric, err := registry.NewMetric("my_metric", stats.Trend, stats.Time) + require.NoError(t, err) builtinMetrics := metrics.RegisterBuiltinMetrics(registry) execScheduler, err := local.NewExecutionScheduler(&minirunner.MiniRunner{}, builtinMetrics, logger) require.NoError(t, err) @@ -113,7 +117,7 @@ func TestGetMetric(t *testing.T) { require.NoError(t, err) engine.MetricsEngine.ObservedMetrics = map[string]*stats.Metric{ - "my_metric": stats.New("my_metric", stats.Trend, stats.Time), + "my_metric": testMetric, } engine.MetricsEngine.ObservedMetrics["my_metric"].Tainted = null.BoolFrom(true) diff --git a/api/v1/metric_test.go b/api/v1/metric_test.go index 787c2f6e9a6..cfd092b9a2e 100644 --- a/api/v1/metric_test.go +++ b/api/v1/metric_test.go @@ -25,8 +25,10 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v3" + "go.k6.io/k6/metrics" "go.k6.io/k6/stats" ) @@ -107,10 +109,11 @@ func TestNullValueTypeJSON(t *testing.T) { func TestNewMetric(t *testing.T) { t.Parallel() - old := stats.New("name", stats.Trend, stats.Time) + old, err := metrics.NewRegistry().NewMetric("test_metric", stats.Trend, stats.Time) + require.NoError(t, err) old.Tainted = null.BoolFrom(true) m := NewMetric(old, 0) - assert.Equal(t, "name", m.Name) + assert.Equal(t, "test_metric", m.Name) assert.True(t, m.Type.Valid) assert.Equal(t, stats.Trend, m.Type.Type) assert.True(t, m.Contains.Valid) diff --git a/core/local/local_test.go b/core/local/local_test.go index 496a3c31135..cbdbf673c4f 100644 --- a/core/local/local_test.go +++ b/core/local/local_test.go @@ -1227,7 +1227,8 @@ func TestRealTimeAndSetupTeardownMetrics(t *testing.T) { } return stats.IntoSampleTags(&tags) } - testCounter := stats.New("test_counter", stats.Counter) + testCounter, err := registry.NewMetric("test_counter", stats.Counter) + require.NoError(t, err) getSample := func(expValue float64, expMetric *stats.Metric, expTags ...string) stats.SampleContainer { return stats.Sample{ Metric: expMetric, diff --git a/js/summary_test.go b/js/summary_test.go index 23d38b2dbdb..335847270fc 100644 --- a/js/summary_test.go +++ b/js/summary_test.go @@ -35,6 +35,7 @@ import ( "go.k6.io/k6/lib" "go.k6.io/k6/lib/testutils" + "go.k6.io/k6/metrics" "go.k6.io/k6/stats" ) @@ -98,21 +99,28 @@ func TestTextSummary(t *testing.T) { func TestTextSummaryWithSubMetrics(t *testing.T) { t.Parallel() - parentMetric := stats.New("my_parent", stats.Counter) + registry := metrics.NewRegistry() + parentMetric, err := registry.NewMetric("my_parent", stats.Counter) + require.NoError(t, err) parentMetric.Sink.Add(stats.Sample{Value: 11}) - parentMetricPost := stats.New("my_parent_post", stats.Counter) + + parentMetricPost, err := registry.NewMetric("my_parent_post", stats.Counter) + require.NoError(t, err) parentMetricPost.Sink.Add(stats.Sample{Value: 22}) - subMetric := stats.New("my_parent{sub:1}", stats.Counter) - subMetric.Sink.Add(stats.Sample{Value: 1}) - subMetricPost := stats.New("my_parent_post{sub:2}", stats.Counter) - subMetricPost.Sink.Add(stats.Sample{Value: 2}) + subMetric, err := parentMetric.AddSubmetric("sub:1") + require.NoError(t, err) + subMetric.Metric.Sink.Add(stats.Sample{Value: 1}) + + subMetricPost, err := parentMetricPost.AddSubmetric("sub:2") + require.NoError(t, err) + subMetricPost.Metric.Sink.Add(stats.Sample{Value: 2}) metrics := map[string]*stats.Metric{ parentMetric.Name: parentMetric, parentMetricPost.Name: parentMetricPost, - subMetric.Name: subMetric, - subMetricPost.Name: subMetricPost, + subMetric.Name: subMetric.Metric, + subMetricPost.Name: subMetricPost.Metric, } summary := &lib.Summary{ @@ -147,15 +155,20 @@ func TestTextSummaryWithSubMetrics(t *testing.T) { } func createTestMetrics(t *testing.T) (map[string]*stats.Metric, *lib.Group) { + registry := metrics.NewRegistry() metrics := make(map[string]*stats.Metric) - gaugeMetric := stats.New("vus", stats.Gauge) + + gaugeMetric, err := registry.NewMetric("vus", stats.Gauge) + require.NoError(t, err) gaugeMetric.Sink.Add(stats.Sample{Value: 1}) - countMetric := stats.New("http_reqs", stats.Counter) + countMetric, err := registry.NewMetric("http_reqs", stats.Counter) + require.NoError(t, err) countMetric.Tainted = null.BoolFrom(true) countMetric.Thresholds = stats.Thresholds{Thresholds: []*stats.Threshold{{Source: "rate<100", LastFailed: true}}} - checksMetric := stats.New("checks", stats.Rate) + checksMetric, err := registry.NewMetric("checks", stats.Rate) + require.NoError(t, err) checksMetric.Tainted = null.BoolFrom(false) checksMetric.Thresholds = stats.Thresholds{Thresholds: []*stats.Threshold{{Source: "rate>70", LastFailed: false}}} sink := &stats.TrendSink{} diff --git a/metrics/registry.go b/metrics/registry.go index 6f7f1be1d23..bfc28a44290 100644 --- a/metrics/registry.go +++ b/metrics/registry.go @@ -61,7 +61,7 @@ func (r *Registry) NewMetric(name string, typ stats.MetricType, t ...stats.Value oldMetric, ok := r.metrics[name] if !ok { - m := stats.New(name, typ, t...) + m := newMetric(name, typ, t...) r.metrics[name] = m return m, nil } @@ -91,3 +91,29 @@ func (r *Registry) MustNewMetric(name string, typ stats.MetricType, t ...stats.V func (r *Registry) Get(name string) *stats.Metric { return r.metrics[name] } + +func newMetric(name string, mt stats.MetricType, vt ...stats.ValueType) *stats.Metric { + valueType := stats.Default + if len(vt) > 0 { + valueType = vt[0] + } + var sink stats.Sink + switch mt { + case stats.Counter: + sink = &stats.CounterSink{} + case stats.Gauge: + sink = &stats.GaugeSink{} + case stats.Trend: + sink = &stats.TrendSink{} + case stats.Rate: + sink = &stats.RateSink{} + default: + return nil + } + return &stats.Metric{ + Name: name, + Type: mt, + Contains: valueType, + Sink: sink, + } +} diff --git a/output/csv/output_test.go b/output/csv/output_test.go index 4c3e7ff88ba..0a60bbf9bcf 100644 --- a/output/csv/output_test.go +++ b/output/csv/output_test.go @@ -37,6 +37,7 @@ import ( "go.k6.io/k6/lib" "go.k6.io/k6/lib/testutils" + "go.k6.io/k6/metrics" "go.k6.io/k6/output" "go.k6.io/k6/stats" ) @@ -65,6 +66,9 @@ func TestMakeHeader(t *testing.T) { } func TestSampleToRow(t *testing.T) { + testMetric, err := metrics.NewRegistry().NewMetric("my_metric", stats.Gauge) + require.NoError(t, err) + testData := []struct { testname string sample *stats.Sample @@ -75,7 +79,7 @@ func TestSampleToRow(t *testing.T) { testname: "One res tag, one ignored tag, one extra tag", sample: &stats.Sample{ Time: time.Unix(1562324644, 0), - Metric: stats.New("my_metric", stats.Gauge), + Metric: testMetric, Value: 1, Tags: stats.NewSampleTags(map[string]string{ "tag1": "val1", @@ -90,7 +94,7 @@ func TestSampleToRow(t *testing.T) { testname: "Two res tags, three extra tags", sample: &stats.Sample{ Time: time.Unix(1562324644, 0), - Metric: stats.New("my_metric", stats.Gauge), + Metric: testMetric, Value: 1, Tags: stats.NewSampleTags(map[string]string{ "tag1": "val1", @@ -107,7 +111,7 @@ func TestSampleToRow(t *testing.T) { testname: "Two res tags, two ignored", sample: &stats.Sample{ Time: time.Unix(1562324644, 0), - Metric: stats.New("my_metric", stats.Gauge), + Metric: testMetric, Value: 1, Tags: stats.NewSampleTags(map[string]string{ "tag1": "val1", @@ -214,6 +218,10 @@ func readCompressedFile(fileName string, fs afero.Fs) string { func TestRun(t *testing.T) { t.Parallel() + + testMetric, err := metrics.NewRegistry().NewMetric("my_metric", stats.Gauge) + require.NoError(t, err) + testData := []struct { samples []stats.SampleContainer fileName string @@ -224,7 +232,7 @@ func TestRun(t *testing.T) { samples: []stats.SampleContainer{ stats.Sample{ Time: time.Unix(1562324643, 0), - Metric: stats.New("my_metric", stats.Gauge), + Metric: testMetric, Value: 1, Tags: stats.NewSampleTags(map[string]string{ "check": "val1", @@ -234,7 +242,7 @@ func TestRun(t *testing.T) { }, stats.Sample{ Time: time.Unix(1562324644, 0), - Metric: stats.New("my_metric", stats.Gauge), + Metric: testMetric, Value: 1, Tags: stats.NewSampleTags(map[string]string{ "check": "val1", @@ -252,7 +260,7 @@ func TestRun(t *testing.T) { samples: []stats.SampleContainer{ stats.Sample{ Time: time.Unix(1562324643, 0), - Metric: stats.New("my_metric", stats.Gauge), + Metric: testMetric, Value: 1, Tags: stats.NewSampleTags(map[string]string{ "check": "val1", @@ -262,7 +270,7 @@ func TestRun(t *testing.T) { }, stats.Sample{ Time: time.Unix(1562324644, 0), - Metric: stats.New("my_metric", stats.Gauge), + Metric: testMetric, Value: 1, Tags: stats.NewSampleTags(map[string]string{ "check": "val1", diff --git a/output/helpers_test.go b/output/helpers_test.go index a4baac2228b..0200c5f5769 100644 --- a/output/helpers_test.go +++ b/output/helpers_test.go @@ -29,14 +29,20 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.k6.io/k6/metrics" "go.k6.io/k6/stats" ) func TestSampleBufferBasics(t *testing.T) { t.Parallel() + + registry := metrics.NewRegistry() + metric, err := registry.NewMetric("my_metric", stats.Rate) + require.NoError(t, err) + single := stats.Sample{ Time: time.Now(), - Metric: stats.New("my_metric", stats.Rate), + Metric: metric, Value: float64(123), Tags: stats.NewSampleTags(map[string]string{"tag1": "val1"}), } @@ -70,6 +76,10 @@ func TestSampleBufferConcurrently(t *testing.T) { r := rand.New(rand.NewSource(seed)) //nolint:gosec t.Logf("Random source seeded with %d\n", seed) + registry := metrics.NewRegistry() + metric, err := registry.NewMetric("my_metric", stats.Gauge) + require.NoError(t, err) + producersCount := 50 + r.Intn(50) sampleCount := 10 + r.Intn(10) sleepModifier := 10 + r.Intn(10) @@ -80,7 +90,7 @@ func TestSampleBufferConcurrently(t *testing.T) { for i := 0; i < sampleCount; i++ { buffer.AddMetricSamples([]stats.SampleContainer{stats.Sample{ Time: time.Unix(1562324644, 0), - Metric: stats.New("my_metric", stats.Gauge), + Metric: metric, Value: float64(i), Tags: stats.NewSampleTags(map[string]string{"tag1": "val1"}), }}) diff --git a/output/influxdb/bench_test.go b/output/influxdb/bench_test.go index 4251dcd9912..2b8a20552d1 100644 --- a/output/influxdb/bench_test.go +++ b/output/influxdb/bench_test.go @@ -27,10 +27,15 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + "go.k6.io/k6/metrics" "go.k6.io/k6/stats" ) func benchmarkInfluxdb(b *testing.B, t time.Duration) { + metric, err := metrics.NewRegistry().NewMetric("test_gauge", stats.Gauge) + require.NoError(b, err) + testOutputCycle(b, func(rw http.ResponseWriter, r *http.Request) { for { time.Sleep(t) @@ -47,7 +52,7 @@ func benchmarkInfluxdb(b *testing.B, t time.Duration) { samples := make(stats.Samples, 10) for i := 0; i < len(samples); i++ { samples[i] = stats.Sample{ - Metric: stats.New("testGauge", stats.Gauge), + Metric: metric, Time: time.Now(), Tags: stats.NewSampleTags(map[string]string{ "something": "else", diff --git a/output/influxdb/output_test.go b/output/influxdb/output_test.go index 48244b2b61f..6708cd7ccfe 100644 --- a/output/influxdb/output_test.go +++ b/output/influxdb/output_test.go @@ -36,6 +36,7 @@ import ( "github.com/stretchr/testify/require" "go.k6.io/k6/lib/testutils" + "go.k6.io/k6/metrics" "go.k6.io/k6/output" "go.k6.io/k6/stats" ) @@ -108,10 +109,14 @@ func testOutputCycle(t testing.TB, handler http.HandlerFunc, body func(testing.T func TestOutput(t *testing.T) { t.Parallel() + metric, err := metrics.NewRegistry().NewMetric("test_gauge", stats.Gauge) + require.NoError(t, err) + var samplesRead int defer func() { require.Equal(t, samplesRead, 20) }() + testOutputCycle(t, func(rw http.ResponseWriter, r *http.Request) { b := bytes.NewBuffer(nil) _, _ = io.Copy(b, r.Body) @@ -130,7 +135,7 @@ func TestOutput(t *testing.T) { samples := make(stats.Samples, 10) for i := 0; i < len(samples); i++ { samples[i] = stats.Sample{ - Metric: stats.New("testGauge", stats.Gauge), + Metric: metric, Time: time.Now(), Tags: stats.NewSampleTags(map[string]string{ "something": "else", @@ -152,6 +157,7 @@ func TestOutputFlushMetricsConcurrency(t *testing.T) { requests = int32(0) block = make(chan struct{}) ) + wg := sync.WaitGroup{} ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { // block all the received requests @@ -170,6 +176,9 @@ func TestOutputFlushMetricsConcurrency(t *testing.T) { ts.Close() }() + metric, err := metrics.NewRegistry().NewMetric("test_gauge", stats.Gauge) + require.NoError(t, err) + o, err := newOutput(output.Params{ Logger: testutils.NewLogger(t), ConfigArgument: ts.URL, @@ -183,7 +192,7 @@ func TestOutputFlushMetricsConcurrency(t *testing.T) { wg.Add(1) o.AddMetricSamples([]stats.SampleContainer{stats.Samples{ stats.Sample{ - Metric: stats.New("gauge", stats.Gauge), + Metric: metric, Value: 2.0, }, }}) diff --git a/output/json/json_test.go b/output/json/json_test.go index 9f0cf9f68a4..49da4037740 100644 --- a/output/json/json_test.go +++ b/output/json/json_test.go @@ -33,6 +33,7 @@ import ( "github.com/stretchr/testify/require" "go.k6.io/k6/lib/testutils" + "go.k6.io/k6/metrics" "go.k6.io/k6/output" "go.k6.io/k6/stats" ) @@ -55,8 +56,14 @@ func getValidator(t testing.TB, expected []string) func(io.Reader) { } func generateTestMetricSamples(t testing.TB) ([]stats.SampleContainer, func(io.Reader)) { - metric1 := stats.New("my_metric1", stats.Gauge) - metric2 := stats.New("my_metric2", stats.Counter, stats.Data) + registry := metrics.NewRegistry() + + metric1, err := registry.NewMetric("my_metric1", stats.Gauge) + require.NoError(t, err) + + metric2, err := registry.NewMetric("my_metric2", stats.Counter, stats.Data) + require.NoError(t, err) + time1 := time.Date(2021, time.February, 24, 13, 37, 10, 0, time.UTC) time2 := time1.Add(10 * time.Second) time3 := time2.Add(10 * time.Second) diff --git a/output/statsd/test_helper.go b/output/statsd/test_helper.go index 1ab6f83a965..3e1cf72e064 100644 --- a/output/statsd/test_helper.go +++ b/output/statsd/test_helper.go @@ -31,6 +31,7 @@ import ( "go.k6.io/k6/lib/testutils" "go.k6.io/k6/lib/types" + "go.k6.io/k6/metrics" "go.k6.io/k6/stats" ) @@ -92,11 +93,18 @@ func baseTest(t *testing.T, } } - myCounter := stats.New("my_counter", stats.Counter) - myGauge := stats.New("my_gauge", stats.Gauge) - myTrend := stats.New("my_trend", stats.Trend) - myRate := stats.New("my_rate", stats.Rate) - myCheck := stats.New("my_check", stats.Rate) + registry := metrics.NewRegistry() + myCounter, err := registry.NewMetric("my_counter", stats.Counter) + require.NoError(t, err) + myGauge, err := registry.NewMetric("my_gauge", stats.Gauge) + require.NoError(t, err) + myTrend, err := registry.NewMetric("my_trend", stats.Trend) + require.NoError(t, err) + myRate, err := registry.NewMetric("my_rate", stats.Rate) + require.NoError(t, err) + myCheck, err := registry.NewMetric("my_check", stats.Rate) + require.NoError(t, err) + testMatrix := []struct { input []stats.SampleContainer output string diff --git a/stats/stats.go b/stats/stats.go index 22f4866db0b..eaae2bf0967 100644 --- a/stats/stats.go +++ b/stats/stats.go @@ -466,13 +466,15 @@ func (m *Metric) Sample(t time.Time, tags *SampleTags, value float64) Sample { } } -func New(name string, typ MetricType, t ...ValueType) *Metric { - vt := Default - if len(t) > 0 { - vt = t[0] +// newMetric instantiates a new Metric +func newMetric(name string, mt MetricType, vt ...ValueType) *Metric { + contains := Default + if len(vt) > 0 { + contains = vt[0] } + var sink Sink - switch typ { + switch mt { case Counter: sink = &CounterSink{} case Gauge: @@ -484,7 +486,8 @@ func New(name string, typ MetricType, t ...ValueType) *Metric { default: return nil } - return &Metric{Name: name, Type: typ, Contains: vt, Sink: sink} + + return &Metric{Name: name, Type: mt, Contains: contains, Sink: sink} } // A Submetric represents a filtered dataset based on a parent metric. @@ -539,7 +542,7 @@ func (m *Metric) AddSubmetric(keyValues string) (*Submetric, error) { Tags: tags, Parent: m, } - subMetricMetric := New(subMetric.Name, m.Type, m.Contains) + subMetricMetric := newMetric(subMetric.Name, m.Type, m.Contains) subMetricMetric.Sub = subMetric // sigh subMetric.Metric = subMetricMetric diff --git a/stats/stats_test.go b/stats/stats_test.go index a206be7707c..7ec7ae3887c 100644 --- a/stats/stats_test.go +++ b/stats/stats_test.go @@ -30,7 +30,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestNew(t *testing.T) { +func TestNewMetric(t *testing.T) { t.Parallel() testdata := map[string]struct { Type MetricType @@ -46,7 +46,7 @@ func TestNew(t *testing.T) { name, data := name, data t.Run(name, func(t *testing.T) { t.Parallel() - m := New("my_metric", data.Type) + m := newMetric("my_metric", data.Type) assert.Equal(t, "my_metric", m.Name) assert.IsType(t, data.SinkType, m.Sink) }) @@ -77,7 +77,7 @@ func TestAddSubmetric(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() - m := New("metric", Trend) + m := newMetric("metric", Trend) sm, err := m.AddSubmetric(name) if expected.err { require.Error(t, err) @@ -156,7 +156,7 @@ func TestSampleImplementations(t *testing.T) { now := time.Now() sample := Sample{ - Metric: New("test_metric", Counter), + Metric: newMetric("test_metric", Counter), Time: now, Tags: NewSampleTags(tagMap), Value: 1.0,