trpc.group/trpc-go/trpc-go@v1.0.3/metrics/metrics_test.go (about) 1 // 2 // 3 // Tencent is pleased to support the open source community by making tRPC available. 4 // 5 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 6 // All rights reserved. 7 // 8 // If you have downloaded a copy of the tRPC source code from Tencent, 9 // please note that tRPC source code is licensed under the Apache 2.0 License, 10 // A copy of the Apache 2.0 License is included in this file. 11 // 12 // 13 14 package metrics_test 15 16 import ( 17 "errors" 18 "reflect" 19 "sync" 20 "testing" 21 "time" 22 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 "trpc.group/trpc-go/trpc-go/metrics" 26 ) 27 28 func TestNewCounter(t *testing.T) { 29 // create expected counters 30 type args struct { 31 name string 32 } 33 tests := []struct { 34 name string 35 args args 36 comp metrics.ICounter 37 match bool 38 }{ 39 {"same-Name-same-counter", args{"req.total.num"}, metrics.Counter("req.total.num"), true}, 40 {"diff-Name-diff-counter", args{"req.total.num"}, metrics.Counter("req.total.fail"), false}, 41 } 42 for _, tt := range tests { 43 t.Run(tt.name, func(t *testing.T) { 44 if got := metrics.Counter(tt.args.name); reflect.DeepEqual(got, tt.comp) != tt.match { 45 t.Errorf("Counter() = %v, comp %v, match should be %v", got, tt.comp, tt.match) 46 } 47 }) 48 } 49 } 50 51 func TestNewGauge(t *testing.T) { 52 type args struct { 53 name string 54 } 55 tests := []struct { 56 name string 57 args args 58 comp metrics.IGauge 59 match bool 60 }{ 61 {"same-Name-same-gauge", args{"cpu.load.average"}, metrics.Gauge("cpu.load.average"), true}, 62 {"diff-Name-diff-gauge", args{"cpu.load.average"}, metrics.Gauge("cpu.load.max"), false}, 63 } 64 for _, tt := range tests { 65 t.Run(tt.name, func(t *testing.T) { 66 if got := metrics.Gauge(tt.args.name); reflect.DeepEqual(got, tt.comp) != tt.match { 67 t.Errorf("Gauge() = %v, comp %v, match should be %v", got, tt.comp, tt.match) 68 } 69 }) 70 } 71 } 72 73 func TestNewTimer(t *testing.T) { 74 type args struct { 75 name string 76 } 77 tests := []struct { 78 name string 79 args args 80 comp metrics.ITimer 81 match bool 82 }{ 83 {"same-Name-same-timer", args{"req.1.timecost"}, metrics.Timer("req.1.timecost"), true}, 84 {"diff-Name-diff-timer", args{"req.1.timecost"}, metrics.Timer("req.2.timecost"), false}, 85 } 86 for _, tt := range tests { 87 t.Run(tt.name, func(t *testing.T) { 88 if got := metrics.Timer(tt.args.name); reflect.DeepEqual(got, tt.comp) != tt.match { 89 t.Errorf("Timer() = %v, compared with %v, match should be %v", got, tt.comp, tt.match) 90 } 91 }) 92 } 93 } 94 95 func TestNewHistogram(t *testing.T) { 96 buckets := metrics.NewDurationBounds(0*time.Millisecond, 97 100*time.Millisecond, 500*time.Millisecond, 1000*time.Millisecond) 98 99 type args struct { 100 name string 101 buckets metrics.BucketBounds 102 } 103 tests := []struct { 104 name string 105 args args 106 comp metrics.IHistogram 107 match bool 108 }{ 109 {"same-Name-same-histogram", args{"cmd.1.timecost", buckets}, 110 metrics.Histogram("cmd.1.timecost", buckets), true}, 111 {"diff-Name-diff-histogram", args{"cmd.1.timecost", buckets}, 112 metrics.Histogram("cmd.2.timecost", buckets), false}, 113 } 114 115 for _, tt := range tests { 116 t.Run(tt.name, func(t *testing.T) { 117 if got := metrics.Histogram(tt.args.name, tt.args.buckets); reflect. 118 DeepEqual(got, tt.comp) != tt.match { 119 120 t.Errorf("Histogram() = %v, comp %v, match should be %v", got, tt.comp, tt.match) 121 } 122 }) 123 } 124 } 125 126 func TestRegisterMetricsSink(t *testing.T) { 127 type args struct { 128 sink metrics.Sink 129 } 130 tests := []struct { 131 name string 132 args args 133 }{ 134 {"noop", args{&metrics.NoopSink{}}}, 135 {"console", args{metrics.NewConsoleSink()}}, 136 } 137 for _, tt := range tests { 138 t.Run(tt.name, func(t *testing.T) { 139 metrics.RegisterMetricsSink(tt.args.sink) 140 err := metrics.Report(metrics.Record{}) 141 require.Nil(t, err) 142 }) 143 } 144 } 145 146 func TestIncrCounter(t *testing.T) { 147 type args struct { 148 key string 149 value float64 150 } 151 tests := []struct { 152 name string 153 args args 154 }{ 155 {"counter-1", args{"req.total", 100}}, 156 {"counter-2", args{"req.fail", 1}}, 157 {"counter-3", args{"req.succ", 99}}, 158 } 159 for _, tt := range tests { 160 t.Run(tt.name, func(t *testing.T) { 161 require.NotNil(t, metrics.Counter(tt.args.key)) 162 metrics.IncrCounter(tt.args.key, tt.args.value) 163 }) 164 } 165 } 166 167 func TestSetGauge(t *testing.T) { 168 type args struct { 169 key string 170 value float64 171 } 172 tests := []struct { 173 name string 174 args args 175 }{ 176 {"gauge-1", args{"cpu.avgload", 70.1}}, 177 {"gauge-2", args{"mem.avgload", 80.0}}, 178 } 179 for _, tt := range tests { 180 t.Run(tt.name, func(t *testing.T) { 181 require.NotNil(t, metrics.Gauge(tt.args.key)) 182 metrics.SetGauge(tt.args.key, tt.args.value) 183 }) 184 } 185 } 186 187 func TestRecordTimer(t *testing.T) { 188 type args struct { 189 key string 190 duration time.Duration 191 } 192 tests := []struct { 193 name string 194 args args 195 }{ 196 {"timer-1", args{"timer.cmd.1", time.Second}}, 197 {"timer-2", args{"timer.cmd.2", time.Second * 2}}, 198 } 199 for _, tt := range tests { 200 t.Run(tt.name, func(t *testing.T) { 201 require.NotNil(t, metrics.Timer(tt.args.key)) 202 metrics.RecordTimer(tt.args.key, tt.args.duration) 203 }) 204 } 205 } 206 207 func TestAddSample(t *testing.T) { 208 metrics.Histogram("timecost.dist", metrics.NewDurationBounds(time.Second, 209 time.Second*2, time.Second*3, time.Second*4)) 210 metrics.RegisterMetricsSink(metrics.NewConsoleSink()) 211 type args struct { 212 key string 213 buckets metrics.BucketBounds 214 value float64 215 } 216 buckets := metrics.NewDurationBounds(time.Second, time.Second*2, time.Second*5) 217 tests := []struct { 218 name string 219 args args 220 }{ 221 {"histogram-1", args{"timecost.dist", buckets, float64(time.Second)}}, 222 {"histogram-2", args{"timecost.dist", buckets, float64(time.Second * 2)}}, 223 {"histogram-2", args{"timecost.dist", buckets, float64(time.Second * 3)}}, 224 } 225 for _, tt := range tests { 226 t.Run(tt.name, func(t *testing.T) { 227 metrics.AddSample(tt.args.key, tt.args.buckets, tt.args.value) 228 h, _ := metrics.GetHistogram(tt.args.key) 229 require.NotNil(t, h) 230 }) 231 } 232 } 233 234 func TestHistogramSink(t *testing.T) { 235 var histNameBucketBounds sync.Map 236 metrics.RegisterMetricsSink(&histSink{ 237 name: "histSink1", 238 register: func(name string, o metrics.HistogramOption) { 239 histNameBucketBounds.LoadOrStore(name, o.BucketBounds) 240 }, 241 }) 242 metrics.Histogram("hist1", metrics.BucketBounds{1, 2}) 243 bb, ok := histNameBucketBounds.Load("hist1") 244 require.True(t, ok) 245 require.True(t, reflect.DeepEqual(bb, metrics.BucketBounds{1, 2})) 246 247 histNameBucketBounds.Delete("hist1") 248 metrics.RegisterMetricsSink(&histSink{ 249 name: "histSink2", 250 register: func(name string, o metrics.HistogramOption) { 251 histNameBucketBounds.LoadOrStore(name, o.BucketBounds) 252 }, 253 }) 254 bb, ok = histNameBucketBounds.Load("hist1") 255 require.True(t, ok) 256 require.True(t, reflect.DeepEqual(bb, metrics.BucketBounds{1, 2})) 257 258 metrics.RegisterHistogram("hist2", 259 metrics.HistogramOption{BucketBounds: metrics.BucketBounds{1, 2}}) 260 bb, ok = histNameBucketBounds.Load("hist2") 261 require.True(t, ok) 262 require.True(t, reflect.DeepEqual(bb, metrics.BucketBounds{1, 2})) 263 } 264 265 type unhealthySink struct{} 266 267 func (u *unhealthySink) Name() string { 268 return "unhealthy" 269 } 270 271 func (u *unhealthySink) Report(_ metrics.Record, _ ...metrics.Option) error { 272 time.Sleep(time.Millisecond * 100) 273 return errors.New("timeout") 274 } 275 276 type unstableSink struct{} 277 278 func (p *unstableSink) Name() string { 279 return "unstable" 280 } 281 282 func (p *unstableSink) Report(_ metrics.Record, _ ...metrics.Option) error { 283 time.Sleep(time.Millisecond * 100) 284 return errors.New("backend error") 285 } 286 287 func TestReport(t *testing.T) { 288 289 metrics.RegisterMetricsSink(metrics.NewConsoleSink()) 290 metrics.RegisterMetricsSink(&metrics.NoopSink{}) 291 metrics.RegisterMetricsSink(&unhealthySink{}) 292 metrics.RegisterMetricsSink(&unstableSink{}) 293 294 rec := metrics.NewSingleDimensionMetrics("total.req", float64(100), metrics.PolicySUM) 295 tests := []struct { 296 name string 297 rec metrics.Record 298 wantErr bool 299 }{ 300 {"reportHasError", rec, true}, 301 } 302 for _, tt := range tests { 303 t.Run(tt.name, func(t *testing.T) { 304 if err := metrics.Report(rec); (err != nil) != tt.wantErr { 305 t.Errorf("Report() error = %v, wantErr %v", err, tt.wantErr) 306 } 307 }) 308 } 309 } 310 311 type histSink struct { 312 name string 313 register func(name string, o metrics.HistogramOption) 314 } 315 316 func (hs *histSink) Name() string { return hs.name } 317 318 func (*histSink) Report(_ metrics.Record, _ ...metrics.Option) error { 319 return nil 320 } 321 322 func (hs *histSink) Register(name string, o metrics.HistogramOption) { 323 hs.register(name, o) 324 } 325 326 func TestGetMetricsSink(t *testing.T) { 327 metrics.RegisterMetricsSink(metrics.NewConsoleSink()) 328 type args struct { 329 name string 330 } 331 tests := []struct { 332 name string 333 args args 334 want metrics.Sink 335 want1 bool 336 }{ 337 {"GetSuccess", args{"console"}, metrics.NewConsoleSink(), true}, 338 {"GetFailed", args{"not_exit_key"}, nil, false}, 339 } 340 for _, tt := range tests { 341 t.Run(tt.name, func(t *testing.T) { 342 got, got1 := metrics.GetMetricsSink(tt.args.name) 343 assert.Equalf(t, tt.want, got, "GetMetricsSink(%v)", tt.args.name) 344 assert.Equalf(t, tt.want1, got1, "GetMetricsSink(%v)", tt.args.name) 345 }) 346 } 347 }