github.com/uber-go/tally/v4@v4.1.17/multi/reporter_test.go (about)

     1  // Copyright (c) 2021 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package multi
    22  
    23  import (
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  	tally "github.com/uber-go/tally/v4"
    30  )
    31  
    32  func TestMultiReporter(t *testing.T) {
    33  	a, b, c :=
    34  		newCapturingStatsReporter(),
    35  		newCapturingStatsReporter(),
    36  		newCapturingStatsReporter()
    37  	all := []*capturingStatsReporter{a, b, c}
    38  
    39  	r := NewMultiReporter(a, b, c)
    40  
    41  	tags := []map[string]string{
    42  		{"foo": "bar"},
    43  		{"foo": "baz"},
    44  		{"foo": "qux"},
    45  		{"foo": "bzz"},
    46  		{"foo": "buz"},
    47  	}
    48  
    49  	valueBuckets := tally.MustMakeLinearValueBuckets(0, 2, 5)
    50  	durationBuckets := tally.MustMakeLinearDurationBuckets(0, 2*time.Second, 5)
    51  
    52  	r.ReportCounter("foo", tags[0], 42)
    53  	r.ReportCounter("foo", tags[0], 84)
    54  	r.ReportGauge("baz", tags[1], 42.0)
    55  	r.ReportTimer("qux", tags[2], 126*time.Millisecond)
    56  	r.ReportHistogramValueSamples("bzz", tags[3], valueBuckets,
    57  		2.0, 4.0, 3)
    58  	r.ReportHistogramDurationSamples("buz", tags[4], durationBuckets,
    59  		2*time.Second, 4*time.Second, 3)
    60  	for _, r := range all {
    61  		require.Equal(t, 2, len(r.counts))
    62  
    63  		assert.Equal(t, "foo", r.counts[0].name)
    64  		assert.Equal(t, tags[0], r.counts[0].tags)
    65  		assert.Equal(t, int64(42), r.counts[0].value)
    66  
    67  		assert.Equal(t, "foo", r.counts[1].name)
    68  		assert.Equal(t, tags[0], r.counts[1].tags)
    69  		assert.Equal(t, int64(84), r.counts[1].value)
    70  
    71  		assert.Equal(t, "baz", r.gauges[0].name)
    72  		assert.Equal(t, tags[1], r.gauges[0].tags)
    73  		assert.Equal(t, float64(42.0), r.gauges[0].value)
    74  
    75  		assert.Equal(t, "qux", r.timers[0].name)
    76  		assert.Equal(t, tags[2], r.timers[0].tags)
    77  		assert.Equal(t, 126*time.Millisecond, r.timers[0].value)
    78  
    79  		assert.Equal(t, "bzz", r.histogramValueSamples[0].name)
    80  		assert.Equal(t, tags[3], r.histogramValueSamples[0].tags)
    81  		assert.Equal(t, 2.0, r.histogramValueSamples[0].bucketLowerBound)
    82  		assert.Equal(t, 4.0, r.histogramValueSamples[0].bucketUpperBound)
    83  		assert.Equal(t, int64(3), r.histogramValueSamples[0].samples)
    84  
    85  		assert.Equal(t, "buz", r.histogramDurationSamples[0].name)
    86  		assert.Equal(t, tags[4], r.histogramDurationSamples[0].tags)
    87  		assert.Equal(t, 2*time.Second, r.histogramDurationSamples[0].bucketLowerBound)
    88  		assert.Equal(t, 4*time.Second, r.histogramDurationSamples[0].bucketUpperBound)
    89  		assert.Equal(t, int64(3), r.histogramDurationSamples[0].samples)
    90  	}
    91  
    92  	assert.NotNil(t, r.Capabilities())
    93  
    94  	r.Flush()
    95  	for _, r := range all {
    96  		assert.Equal(t, 1, r.flush)
    97  	}
    98  }
    99  
   100  func TestMultiCachedReporter(t *testing.T) {
   101  	a, b, c :=
   102  		newCapturingStatsReporter(),
   103  		newCapturingStatsReporter(),
   104  		newCapturingStatsReporter()
   105  	all := []*capturingStatsReporter{a, b, c}
   106  
   107  	r := NewMultiCachedReporter(a, b, c)
   108  
   109  	tags := []map[string]string{
   110  		{"foo": "bar"},
   111  		{"foo": "baz"},
   112  		{"foo": "qux"},
   113  		{"foo": "bzz"},
   114  		{"foo": "buz"},
   115  	}
   116  
   117  	valueBuckets := tally.MustMakeLinearValueBuckets(0, 2, 5)
   118  	durationBuckets := tally.MustMakeLinearDurationBuckets(0, 2*time.Second, 5)
   119  
   120  	ctr := r.AllocateCounter("foo", tags[0])
   121  	ctr.ReportCount(42)
   122  	ctr.ReportCount(84)
   123  
   124  	gauge := r.AllocateGauge("baz", tags[1])
   125  	gauge.ReportGauge(42.0)
   126  
   127  	tmr := r.AllocateTimer("qux", tags[2])
   128  	tmr.ReportTimer(126 * time.Millisecond)
   129  
   130  	vhist := r.AllocateHistogram("bzz", tags[3], valueBuckets)
   131  	vhist.ValueBucket(2.0, 4.0).ReportSamples(3)
   132  
   133  	dhist := r.AllocateHistogram("buz", tags[4], durationBuckets)
   134  	dhist.DurationBucket(2*time.Second, 4*time.Second).ReportSamples(3)
   135  
   136  	for _, r := range all {
   137  		require.Equal(t, 2, len(r.counts))
   138  
   139  		assert.Equal(t, "foo", r.counts[0].name)
   140  		assert.Equal(t, tags[0], r.counts[0].tags)
   141  		assert.Equal(t, int64(42), r.counts[0].value)
   142  
   143  		assert.Equal(t, "foo", r.counts[1].name)
   144  		assert.Equal(t, tags[0], r.counts[1].tags)
   145  		assert.Equal(t, int64(84), r.counts[1].value)
   146  
   147  		assert.Equal(t, "baz", r.gauges[0].name)
   148  		assert.Equal(t, tags[1], r.gauges[0].tags)
   149  		assert.Equal(t, float64(42.0), r.gauges[0].value)
   150  
   151  		assert.Equal(t, "qux", r.timers[0].name)
   152  		assert.Equal(t, tags[2], r.timers[0].tags)
   153  		assert.Equal(t, 126*time.Millisecond, r.timers[0].value)
   154  
   155  		assert.Equal(t, "bzz", r.histogramValueSamples[0].name)
   156  		assert.Equal(t, tags[3], r.histogramValueSamples[0].tags)
   157  		assert.Equal(t, 2.0, r.histogramValueSamples[0].bucketLowerBound)
   158  		assert.Equal(t, 4.0, r.histogramValueSamples[0].bucketUpperBound)
   159  		assert.Equal(t, int64(3), r.histogramValueSamples[0].samples)
   160  
   161  		assert.Equal(t, "buz", r.histogramDurationSamples[0].name)
   162  		assert.Equal(t, tags[4], r.histogramDurationSamples[0].tags)
   163  		assert.Equal(t, 2*time.Second, r.histogramDurationSamples[0].bucketLowerBound)
   164  		assert.Equal(t, 4*time.Second, r.histogramDurationSamples[0].bucketUpperBound)
   165  		assert.Equal(t, int64(3), r.histogramDurationSamples[0].samples)
   166  	}
   167  
   168  	assert.NotNil(t, r.Capabilities())
   169  
   170  	r.Flush()
   171  	for _, r := range all {
   172  		assert.Equal(t, 1, r.flush)
   173  	}
   174  }
   175  
   176  type capturingStatsReporter struct {
   177  	counts                   []capturedCount
   178  	gauges                   []capturedGauge
   179  	timers                   []capturedTimer
   180  	histogramValueSamples    []capturedHistogramValueSamples
   181  	histogramDurationSamples []capturedHistogramDurationSamples
   182  	capabilities             int
   183  	flush                    int
   184  }
   185  
   186  type capturedCount struct {
   187  	name  string
   188  	tags  map[string]string
   189  	value int64
   190  }
   191  
   192  type capturedGauge struct {
   193  	name  string
   194  	tags  map[string]string
   195  	value float64
   196  }
   197  
   198  type capturedTimer struct {
   199  	name  string
   200  	tags  map[string]string
   201  	value time.Duration
   202  }
   203  
   204  type capturedHistogramValueSamples struct {
   205  	name             string
   206  	tags             map[string]string
   207  	bucketLowerBound float64
   208  	bucketUpperBound float64
   209  	samples          int64
   210  }
   211  
   212  type capturedHistogramDurationSamples struct {
   213  	name             string
   214  	tags             map[string]string
   215  	bucketLowerBound time.Duration
   216  	bucketUpperBound time.Duration
   217  	samples          int64
   218  }
   219  
   220  func newCapturingStatsReporter() *capturingStatsReporter {
   221  	return &capturingStatsReporter{}
   222  }
   223  
   224  func (r *capturingStatsReporter) ReportCounter(
   225  	name string,
   226  	tags map[string]string,
   227  	value int64,
   228  ) {
   229  	r.counts = append(r.counts, capturedCount{name, tags, value})
   230  }
   231  
   232  func (r *capturingStatsReporter) ReportGauge(
   233  	name string,
   234  	tags map[string]string,
   235  	value float64,
   236  ) {
   237  	r.gauges = append(r.gauges, capturedGauge{name, tags, value})
   238  }
   239  
   240  func (r *capturingStatsReporter) ReportTimer(
   241  	name string,
   242  	tags map[string]string,
   243  	value time.Duration,
   244  ) {
   245  	r.timers = append(r.timers, capturedTimer{name, tags, value})
   246  }
   247  
   248  func (r *capturingStatsReporter) ReportHistogramValueSamples(
   249  	name string,
   250  	tags map[string]string,
   251  	buckets tally.Buckets,
   252  	bucketLowerBound,
   253  	bucketUpperBound float64,
   254  	samples int64,
   255  ) {
   256  	elem := capturedHistogramValueSamples{
   257  		name, tags,
   258  		bucketLowerBound, bucketUpperBound, samples,
   259  	}
   260  	r.histogramValueSamples = append(r.histogramValueSamples, elem)
   261  }
   262  
   263  func (r *capturingStatsReporter) ReportHistogramDurationSamples(
   264  	name string,
   265  	tags map[string]string,
   266  	buckets tally.Buckets,
   267  	bucketLowerBound,
   268  	bucketUpperBound time.Duration,
   269  	samples int64,
   270  ) {
   271  	elem := capturedHistogramDurationSamples{
   272  		name, tags,
   273  		bucketLowerBound, bucketUpperBound, samples,
   274  	}
   275  	r.histogramDurationSamples = append(r.histogramDurationSamples, elem)
   276  }
   277  
   278  func (r *capturingStatsReporter) AllocateCounter(
   279  	name string,
   280  	tags map[string]string,
   281  ) tally.CachedCount {
   282  	return cachedCount{fn: func(value int64) {
   283  		r.counts = append(r.counts, capturedCount{name, tags, value})
   284  	}}
   285  }
   286  
   287  func (r *capturingStatsReporter) AllocateGauge(
   288  	name string,
   289  	tags map[string]string,
   290  ) tally.CachedGauge {
   291  	return cachedGauge{fn: func(value float64) {
   292  		r.gauges = append(r.gauges, capturedGauge{name, tags, value})
   293  	}}
   294  }
   295  
   296  func (r *capturingStatsReporter) AllocateTimer(
   297  	name string,
   298  	tags map[string]string,
   299  ) tally.CachedTimer {
   300  	return cachedTimer{fn: func(value time.Duration) {
   301  		r.timers = append(r.timers, capturedTimer{name, tags, value})
   302  	}}
   303  }
   304  
   305  func (r *capturingStatsReporter) AllocateHistogram(
   306  	name string,
   307  	tags map[string]string,
   308  	buckets tally.Buckets,
   309  ) tally.CachedHistogram {
   310  	return cachedHistogram{
   311  		valueFn: func(bucketLowerBound, bucketUpperBound float64, samples int64) {
   312  			elem := capturedHistogramValueSamples{
   313  				name, tags, bucketLowerBound, bucketUpperBound, samples,
   314  			}
   315  			r.histogramValueSamples = append(r.histogramValueSamples, elem)
   316  		},
   317  		durationFn: func(bucketLowerBound, bucketUpperBound time.Duration, samples int64) {
   318  			elem := capturedHistogramDurationSamples{
   319  				name, tags, bucketLowerBound, bucketUpperBound, samples,
   320  			}
   321  			r.histogramDurationSamples = append(r.histogramDurationSamples, elem)
   322  		},
   323  	}
   324  }
   325  
   326  func (r *capturingStatsReporter) Capabilities() tally.Capabilities {
   327  	r.capabilities++
   328  	return r
   329  }
   330  
   331  func (r *capturingStatsReporter) Reporting() bool {
   332  	return true
   333  }
   334  
   335  func (r *capturingStatsReporter) Tagging() bool {
   336  	return true
   337  }
   338  
   339  func (r *capturingStatsReporter) Flush() {
   340  	r.flush++
   341  }
   342  
   343  type cachedCount struct {
   344  	fn func(value int64)
   345  }
   346  
   347  func (c cachedCount) ReportCount(value int64) {
   348  	c.fn(value)
   349  }
   350  
   351  type cachedGauge struct {
   352  	fn func(value float64)
   353  }
   354  
   355  func (c cachedGauge) ReportGauge(value float64) {
   356  	c.fn(value)
   357  }
   358  
   359  type cachedTimer struct {
   360  	fn func(value time.Duration)
   361  }
   362  
   363  func (c cachedTimer) ReportTimer(value time.Duration) {
   364  	c.fn(value)
   365  }
   366  
   367  type cachedHistogram struct {
   368  	valueFn    func(bucketLowerBound, bucketUpperBound float64, samples int64)
   369  	durationFn func(bucketLowerBound, bucketUpperBound time.Duration, samples int64)
   370  }
   371  
   372  func (h cachedHistogram) ValueBucket(
   373  	bucketLowerBound, bucketUpperBound float64,
   374  ) tally.CachedHistogramBucket {
   375  	return cachedHistogramValueBucket{&h, bucketLowerBound, bucketUpperBound}
   376  }
   377  
   378  func (h cachedHistogram) DurationBucket(
   379  	bucketLowerBound, bucketUpperBound time.Duration,
   380  ) tally.CachedHistogramBucket {
   381  	return cachedHistogramDurationBucket{&h, bucketLowerBound, bucketUpperBound}
   382  }
   383  
   384  type cachedHistogramValueBucket struct {
   385  	histogram        *cachedHistogram
   386  	bucketLowerBound float64
   387  	bucketUpperBound float64
   388  }
   389  
   390  func (b cachedHistogramValueBucket) ReportSamples(v int64) {
   391  	b.histogram.valueFn(b.bucketLowerBound, b.bucketUpperBound, v)
   392  }
   393  
   394  type cachedHistogramDurationBucket struct {
   395  	histogram        *cachedHistogram
   396  	bucketLowerBound time.Duration
   397  	bucketUpperBound time.Duration
   398  }
   399  
   400  func (b cachedHistogramDurationBucket) ReportSamples(v int64) {
   401  	b.histogram.durationFn(b.bucketLowerBound, b.bucketUpperBound, v)
   402  }