github.com/uber-go/tally/v4@v4.1.16/scope_registry_test.go (about) 1 // Copyright (c) 2024 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 tally 22 23 import ( 24 "fmt" 25 "testing" 26 27 "github.com/stretchr/testify/assert" 28 ) 29 30 var ( 31 numInternalMetrics = 4 32 ) 33 34 func TestVerifyCachedTaggedScopesAlloc(t *testing.T) { 35 root, _ := NewRootScope(ScopeOptions{ 36 Prefix: "funkytown", 37 Reporter: NullStatsReporter, 38 Tags: map[string]string{ 39 "style": "funky", 40 "hair": "wavy", 41 "jefferson": "starship", 42 }, 43 }, 0) 44 45 allocs := testing.AllocsPerRun(1000, func() { 46 root.Tagged(map[string]string{ 47 "foo": "bar", 48 "baz": "qux", 49 "qux": "quux", 50 }) 51 }) 52 expected := 3.0 53 assert.True(t, allocs <= expected, "the cached tagged scopes should allocate at most %.0f allocations, but did allocate %.0f", expected, allocs) 54 } 55 56 func TestVerifyOmitCardinalityMetricsTags(t *testing.T) { 57 r := newTestStatsReporter() 58 _, closer := NewRootScope(ScopeOptions{ 59 Reporter: r, 60 OmitCardinalityMetrics: false, 61 CardinalityMetricsTags: map[string]string{ 62 "cardinality_tag_key": "cardinality_tag_value", 63 }, 64 }, 0) 65 wantOmitCardinalityMetricsTags := map[string]string{ 66 "cardinality_tag_key": "cardinality_tag_value", 67 "version": Version, 68 "host": "global", 69 "instance": "global", 70 } 71 72 r.gg.Add(numInternalMetrics) 73 closer.Close() 74 r.WaitAll() 75 76 assert.NotNil(t, r.gauges[counterCardinalityName], "counter cardinality should not be nil") 77 assert.Equal( 78 t, wantOmitCardinalityMetricsTags, r.gauges[counterCardinalityName].tags, "expected tags %v, got tags %v", 79 wantOmitCardinalityMetricsTags, r.gauges[counterCardinalityName].tags, 80 ) 81 } 82 83 func TestNewTestStatsReporterOneScope(t *testing.T) { 84 r := newTestStatsReporter() 85 root, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: false}, 0) 86 s := root.(*scope) 87 88 numFakeCounters := 3 89 numFakeGauges := 5 90 numFakeHistograms := 11 91 numScopes := 1 92 93 r.cg.Add(numFakeCounters) 94 for c := 1; c <= numFakeCounters; c++ { 95 s.Counter(fmt.Sprintf("counter-%d", c)).Inc(int64(c)) 96 } 97 98 r.gg.Add(numFakeGauges + numInternalMetrics) 99 for g := 1; g <= numFakeGauges; g++ { 100 s.Gauge(fmt.Sprintf("gauge_%d", g)).Update(float64(g)) 101 } 102 103 r.hg.Add(numFakeHistograms) 104 for h := 1; h <= numFakeHistograms; h++ { 105 s.Histogram(fmt.Sprintf("histogram_%d", h), MustMakeLinearValueBuckets(0, 1, 10)).RecordValue(float64(h)) 106 } 107 108 closer.Close() 109 r.WaitAll() 110 111 assert.NotNil(t, r.gauges[counterCardinalityName], "counter cardinality should not be nil") 112 assert.Equal( 113 t, numFakeCounters, int(r.gauges[counterCardinalityName].val), "expected %d counters, got %d counters", 114 numFakeCounters, r.gauges[counterCardinalityName].val, 115 ) 116 117 assert.NotNil(t, r.gauges[gaugeCardinalityName], "gauge cardinality should not be nil") 118 assert.Equal( 119 t, numFakeGauges, int(r.gauges[gaugeCardinalityName].val), "expected %d gauges, got %d gauges", 120 numFakeGauges, r.gauges[gaugeCardinalityName].val, 121 ) 122 123 assert.NotNil(t, r.gauges[histogramCardinalityName], "histogram cardinality should not be nil") 124 assert.Equal( 125 t, numFakeHistograms, int(r.gauges[histogramCardinalityName].val), 126 "expected %d histograms, got %d histograms", numFakeHistograms, r.gauges[histogramCardinalityName].val, 127 ) 128 129 assert.NotNil(t, r.gauges[scopeCardinalityName], "scope cardinality should not be nil") 130 assert.Equal( 131 t, numScopes, int(r.gauges[scopeCardinalityName].val), "expected %d scopes, got %d scopes", 132 numScopes, r.gauges[scopeCardinalityName].val, 133 ) 134 } 135 136 func TestNewTestStatsReporterManyScopes(t *testing.T) { 137 r := newTestStatsReporter() 138 root, closer := NewRootScope(ScopeOptions{Reporter: r, OmitCardinalityMetrics: false}, 0) 139 wantCounters, wantGauges, wantHistograms, wantScopes := 3, 2, 1, 2 140 141 s := root.(*scope) 142 r.cg.Add(2) 143 s.Counter("counter-foo").Inc(1) 144 s.Counter("counter-bar").Inc(2) 145 r.gg.Add(1 + numInternalMetrics) 146 s.Gauge("gauge-foo").Update(3) 147 r.hg.Add(1) 148 s.Histogram("histogram-foo", MustMakeLinearValueBuckets(0, 1, 10)).RecordValue(4) 149 150 ss := root.SubScope("sub-scope").(*scope) 151 r.cg.Add(1) 152 ss.Counter("counter-baz").Inc(5) 153 r.gg.Add(1) 154 ss.Gauge("gauge-bar").Update(6) 155 156 closer.Close() 157 r.WaitAll() 158 159 assert.NotNil(t, r.gauges[counterCardinalityName], "counter cardinality should not be nil") 160 assert.Equal( 161 t, wantCounters, int(r.gauges[counterCardinalityName].val), "expected %d counters, got %d counters", wantCounters, 162 r.gauges[counterCardinalityName].val, 163 ) 164 165 assert.NotNil(t, r.gauges[gaugeCardinalityName], "gauge cardinality should not be nil") 166 assert.Equal( 167 t, wantGauges, int(r.gauges[gaugeCardinalityName].val), "expected %d gauges, got %d gauges", wantGauges, 168 r.gauges[gaugeCardinalityName].val, 169 ) 170 171 assert.NotNil(t, r.gauges[histogramCardinalityName], "histogram cardinality should not be nil") 172 assert.Equal( 173 t, wantHistograms, int(r.gauges[histogramCardinalityName].val), "expected %d histograms, got %d histograms", 174 wantHistograms, r.gauges[histogramCardinalityName].val, 175 ) 176 177 assert.NotNil(t, r.gauges[scopeCardinalityName], "scope cardinality should not be nil") 178 assert.Equal( 179 t, wantScopes, int(r.gauges[scopeCardinalityName].val), "expected %d scopes, got %d scopes", 180 wantScopes, r.gauges[scopeCardinalityName].val, 181 ) 182 } 183 184 func TestForEachScopeConcurrent(t *testing.T) { 185 var ( 186 root = newRootScope(ScopeOptions{Prefix: "", Tags: nil}, 0) 187 quit = make(chan struct{}) 188 done = make(chan struct{}) 189 ) 190 191 go func() { 192 defer close(done) 193 for { 194 select { 195 case <-quit: 196 return 197 default: 198 hello := root.Tagged(map[string]string{"a": "b"}).Counter("hello") 199 hello.Inc(1) 200 } 201 } 202 }() 203 204 var c Counter = nil 205 for { 206 // Keep poking at the subscopes until the counter is written. 207 root.registry.ForEachScope( 208 func(ss *scope) { 209 ss.cm.RLock() 210 if ss.counters["hello"] != nil { 211 c = ss.counters["hello"] 212 } 213 ss.cm.RUnlock() 214 }, 215 ) 216 if c != nil { 217 quit <- struct{}{} 218 break 219 } 220 } 221 222 <-done 223 } 224 225 func TestCachedReporterInternalMetricsAlloc(t *testing.T) { 226 tests := []struct { 227 name string 228 omitCardinalityMetrics bool 229 wantGauges int 230 }{ 231 { 232 name: "omit metrics", 233 omitCardinalityMetrics: true, 234 wantGauges: 1, 235 }, 236 { 237 name: "include metrics", 238 omitCardinalityMetrics: false, 239 wantGauges: 1 + numInternalMetrics, 240 }, 241 } 242 243 for _, tt := range tests { 244 r := newTestStatsReporter() 245 root, closer := NewRootScope(ScopeOptions{CachedReporter: r, OmitCardinalityMetrics: tt.omitCardinalityMetrics}, 0) 246 s := root.(*scope) 247 248 r.gg.Add(tt.wantGauges) 249 s.Gauge("gauge-foo").Update(3) 250 251 closer.Close() 252 r.WaitAll() 253 254 assert.Equal( 255 t, tt.wantGauges, len(r.gauges), "%n: expected %d gauges, got %d gauges", tt.name, tt.wantGauges, 256 len(r.gauges), 257 ) 258 } 259 }