github.com/newrelic/go-agent@v3.26.0+incompatible/internal/metrics_test.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package internal 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "testing" 10 "time" 11 ) 12 13 var ( 14 start = time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 15 end = time.Date(2014, time.November, 28, 1, 2, 0, 0, time.UTC) 16 ) 17 18 func TestEmptyMetrics(t *testing.T) { 19 mt := newMetricTable(20, start) 20 js, err := mt.CollectorJSON(`12345`, end) 21 if nil != err { 22 t.Fatal(err) 23 } 24 if nil != js { 25 t.Error(string(js)) 26 } 27 } 28 29 func isValidJSON(data []byte) error { 30 var v interface{} 31 32 return json.Unmarshal(data, &v) 33 } 34 35 func TestMetrics(t *testing.T) { 36 mt := newMetricTable(20, start) 37 38 mt.addDuration("one", "", 2*time.Second, 1*time.Second, unforced) 39 mt.addDuration("two", "my_scope", 4*time.Second, 2*time.Second, unforced) 40 mt.addDuration("one", "my_scope", 2*time.Second, 1*time.Second, unforced) 41 mt.addDuration("one", "", 2*time.Second, 1*time.Second, unforced) 42 43 mt.addApdex("apdex satisfied", "", 9*time.Second, ApdexSatisfying, unforced) 44 mt.addApdex("apdex satisfied", "", 8*time.Second, ApdexSatisfying, unforced) 45 mt.addApdex("apdex tolerated", "", 7*time.Second, ApdexTolerating, unforced) 46 mt.addApdex("apdex tolerated", "", 8*time.Second, ApdexTolerating, unforced) 47 mt.addApdex("apdex failed", "my_scope", 1*time.Second, ApdexFailing, unforced) 48 49 mt.addCount("count 123", float64(123), unforced) 50 mt.addSingleCount("count 1", unforced) 51 52 ExpectMetrics(t, mt, []WantMetric{ 53 {"apdex satisfied", "", false, []float64{2, 0, 0, 8, 9, 0}}, 54 {"apdex tolerated", "", false, []float64{0, 2, 0, 7, 8, 0}}, 55 {"one", "", false, []float64{2, 4, 2, 2, 2, 8}}, 56 {"apdex failed", "my_scope", false, []float64{0, 0, 1, 1, 1, 0}}, 57 {"one", "my_scope", false, []float64{1, 2, 1, 2, 2, 4}}, 58 {"two", "my_scope", false, []float64{1, 4, 2, 4, 4, 16}}, 59 {"count 123", "", false, []float64{123, 0, 0, 0, 0, 0}}, 60 {"count 1", "", false, []float64{1, 0, 0, 0, 0, 0}}, 61 }) 62 63 js, err := mt.Data("12345", end) 64 if nil != err { 65 t.Error(err) 66 } 67 // The JSON metric order is not deterministic, so we merely test that it 68 // is valid JSON. 69 if err := isValidJSON(js); nil != err { 70 t.Error(err, string(js)) 71 } 72 } 73 74 func TestApplyRules(t *testing.T) { 75 js := `[ 76 { 77 "ignore":false, 78 "each_segment":false, 79 "terminate_chain":true, 80 "replacement":"been_renamed", 81 "replace_all":false, 82 "match_expression":"one$", 83 "eval_order":1 84 }, 85 { 86 "ignore":true, 87 "each_segment":false, 88 "terminate_chain":true, 89 "replace_all":false, 90 "match_expression":"ignore_me", 91 "eval_order":1 92 }, 93 { 94 "ignore":false, 95 "each_segment":false, 96 "terminate_chain":true, 97 "replacement":"merge_me", 98 "replace_all":false, 99 "match_expression":"merge_me[0-9]+$", 100 "eval_order":1 101 } 102 ]` 103 var rules metricRules 104 err := json.Unmarshal([]byte(js), &rules) 105 if nil != err { 106 t.Fatal(err) 107 } 108 109 mt := newMetricTable(20, start) 110 mt.addDuration("one", "", 2*time.Second, 1*time.Second, unforced) 111 mt.addDuration("one", "scope1", 2*time.Second, 1*time.Second, unforced) 112 mt.addDuration("one", "scope2", 2*time.Second, 1*time.Second, unforced) 113 mt.addDuration("ignore_me", "", 2*time.Second, 1*time.Second, unforced) 114 mt.addDuration("ignore_me", "scope1", 2*time.Second, 1*time.Second, unforced) 115 mt.addDuration("ignore_me", "scope2", 2*time.Second, 1*time.Second, unforced) 116 mt.addDuration("merge_me1", "", 2*time.Second, 1*time.Second, unforced) 117 mt.addDuration("merge_me2", "", 2*time.Second, 1*time.Second, unforced) 118 119 applied := mt.ApplyRules(rules) 120 ExpectMetrics(t, applied, []WantMetric{ 121 {"been_renamed", "", false, []float64{1, 2, 1, 2, 2, 4}}, 122 {"been_renamed", "scope1", false, []float64{1, 2, 1, 2, 2, 4}}, 123 {"been_renamed", "scope2", false, []float64{1, 2, 1, 2, 2, 4}}, 124 {"merge_me", "", false, []float64{2, 4, 2, 2, 2, 8}}, 125 }) 126 } 127 128 func TestApplyEmptyRules(t *testing.T) { 129 js := `[]` 130 var rules metricRules 131 err := json.Unmarshal([]byte(js), &rules) 132 if nil != err { 133 t.Fatal(err) 134 } 135 mt := newMetricTable(20, start) 136 mt.addDuration("one", "", 2*time.Second, 1*time.Second, unforced) 137 mt.addDuration("one", "my_scope", 2*time.Second, 1*time.Second, unforced) 138 applied := mt.ApplyRules(rules) 139 ExpectMetrics(t, applied, []WantMetric{ 140 {"one", "", false, []float64{1, 2, 1, 2, 2, 4}}, 141 {"one", "my_scope", false, []float64{1, 2, 1, 2, 2, 4}}, 142 }) 143 } 144 145 func TestApplyNilRules(t *testing.T) { 146 var rules metricRules 147 148 mt := newMetricTable(20, start) 149 mt.addDuration("one", "", 2*time.Second, 1*time.Second, unforced) 150 mt.addDuration("one", "my_scope", 2*time.Second, 1*time.Second, unforced) 151 applied := mt.ApplyRules(rules) 152 ExpectMetrics(t, applied, []WantMetric{ 153 {"one", "", false, []float64{1, 2, 1, 2, 2, 4}}, 154 {"one", "my_scope", false, []float64{1, 2, 1, 2, 2, 4}}, 155 }) 156 } 157 158 func TestForced(t *testing.T) { 159 mt := newMetricTable(0, start) 160 161 mt.addDuration("unforced", "", 1*time.Second, 1*time.Second, unforced) 162 mt.addDuration("forced", "", 2*time.Second, 2*time.Second, forced) 163 164 ExpectMetrics(t, mt, []WantMetric{ 165 {"forced", "", true, []float64{1, 2, 2, 2, 2, 4}}, 166 {supportabilityDropped, "", true, []float64{1, 0, 0, 0, 0, 0}}, 167 }) 168 169 } 170 171 func TestMetricsMergeIntoEmpty(t *testing.T) { 172 src := newMetricTable(20, start) 173 src.addDuration("one", "", 2*time.Second, 1*time.Second, unforced) 174 src.addDuration("two", "", 2*time.Second, 1*time.Second, unforced) 175 dest := newMetricTable(20, start) 176 dest.merge(src, "") 177 178 ExpectMetrics(t, dest, []WantMetric{ 179 {"one", "", false, []float64{1, 2, 1, 2, 2, 4}}, 180 {"two", "", false, []float64{1, 2, 1, 2, 2, 4}}, 181 }) 182 } 183 184 func TestMetricsMergeFromEmpty(t *testing.T) { 185 src := newMetricTable(20, start) 186 dest := newMetricTable(20, start) 187 dest.addDuration("one", "", 2*time.Second, 1*time.Second, unforced) 188 dest.addDuration("two", "", 2*time.Second, 1*time.Second, unforced) 189 dest.merge(src, "") 190 191 ExpectMetrics(t, dest, []WantMetric{ 192 {"one", "", false, []float64{1, 2, 1, 2, 2, 4}}, 193 {"two", "", false, []float64{1, 2, 1, 2, 2, 4}}, 194 }) 195 } 196 197 func TestMetricsMerge(t *testing.T) { 198 src := newMetricTable(20, start) 199 dest := newMetricTable(20, start) 200 dest.addDuration("one", "", 2*time.Second, 1*time.Second, unforced) 201 dest.addDuration("two", "", 2*time.Second, 1*time.Second, unforced) 202 src.addDuration("two", "", 2*time.Second, 1*time.Second, unforced) 203 src.addDuration("three", "", 2*time.Second, 1*time.Second, unforced) 204 205 dest.merge(src, "") 206 207 ExpectMetrics(t, dest, []WantMetric{ 208 {"one", "", false, []float64{1, 2, 1, 2, 2, 4}}, 209 {"two", "", false, []float64{2, 4, 2, 2, 2, 8}}, 210 {"three", "", false, []float64{1, 2, 1, 2, 2, 4}}, 211 }) 212 } 213 214 func TestMergeFailedSuccess(t *testing.T) { 215 src := newMetricTable(20, start) 216 dest := newMetricTable(20, end) 217 dest.addDuration("one", "", 2*time.Second, 1*time.Second, unforced) 218 dest.addDuration("two", "", 2*time.Second, 1*time.Second, unforced) 219 src.addDuration("two", "", 2*time.Second, 1*time.Second, unforced) 220 src.addDuration("three", "", 2*time.Second, 1*time.Second, unforced) 221 222 if 0 != dest.failedHarvests { 223 t.Fatal(dest.failedHarvests) 224 } 225 226 dest.mergeFailed(src) 227 228 ExpectMetrics(t, dest, []WantMetric{ 229 {"one", "", false, []float64{1, 2, 1, 2, 2, 4}}, 230 {"two", "", false, []float64{2, 4, 2, 2, 2, 8}}, 231 {"three", "", false, []float64{1, 2, 1, 2, 2, 4}}, 232 }) 233 } 234 235 func TestMergeFailedLimitReached(t *testing.T) { 236 src := newMetricTable(20, start) 237 dest := newMetricTable(20, end) 238 dest.addDuration("one", "", 2*time.Second, 1*time.Second, unforced) 239 dest.addDuration("two", "", 2*time.Second, 1*time.Second, unforced) 240 src.addDuration("two", "", 2*time.Second, 1*time.Second, unforced) 241 src.addDuration("three", "", 2*time.Second, 1*time.Second, unforced) 242 243 src.failedHarvests = failedMetricAttemptsLimit 244 245 dest.mergeFailed(src) 246 247 ExpectMetrics(t, dest, []WantMetric{ 248 {"one", "", false, []float64{1, 2, 1, 2, 2, 4}}, 249 {"two", "", false, []float64{1, 2, 1, 2, 2, 4}}, 250 }) 251 } 252 253 func BenchmarkMetricTableCollectorJSON(b *testing.B) { 254 mt := newMetricTable(2000, time.Now()) 255 md := metricData{ 256 countSatisfied: 1234567812345678.1234567812345678, 257 totalTolerated: 1234567812345678.1234567812345678, 258 exclusiveFailed: 1234567812345678.1234567812345678, 259 min: 1234567812345678.1234567812345678, 260 max: 1234567812345678.1234567812345678, 261 sumSquares: 1234567812345678.1234567812345678, 262 } 263 264 for i := 0; i < 20; i++ { 265 scope := fmt.Sprintf("WebTransaction/Uri/myblog2/%d", i) 266 267 for j := 0; j < 20; j++ { 268 name := fmt.Sprintf("Datastore/statement/MySQL/City%d/insert", j) 269 mt.add(name, "", md, forced) 270 mt.add(name, scope, md, forced) 271 272 name = fmt.Sprintf("WebTransaction/Uri/myblog2/newPost_rum_%d.php", j) 273 mt.add(name, "", md, forced) 274 mt.add(name, scope, md, forced) 275 } 276 } 277 278 data, err := mt.CollectorJSON("12345", time.Now()) 279 if nil != err { 280 b.Fatal(err) 281 } 282 if err := isValidJSON(data); nil != err { 283 b.Fatal(err, string(data)) 284 } 285 286 b.ResetTimer() 287 b.ReportAllocs() 288 289 id := "12345" 290 now := time.Now() 291 for i := 0; i < b.N; i++ { 292 mt.CollectorJSON(id, now) 293 } 294 } 295 296 func BenchmarkAddingSameMetrics(b *testing.B) { 297 name := "my_name" 298 scope := "my_scope" 299 duration := 2 * time.Second 300 exclusive := 1 * time.Second 301 302 mt := newMetricTable(2000, time.Now()) 303 304 b.ResetTimer() 305 b.ReportAllocs() 306 307 for i := 0; i < b.N; i++ { 308 mt.addDuration(name, scope, duration, exclusive, forced) 309 mt.addSingleCount(name, forced) 310 } 311 } 312 313 func TestMergedMetricsAreCopied(t *testing.T) { 314 src := newMetricTable(20, start) 315 dest := newMetricTable(20, start) 316 317 src.addSingleCount("zip", unforced) 318 dest.merge(src, "") 319 src.addSingleCount("zip", unforced) 320 ExpectMetrics(t, dest, []WantMetric{ 321 {"zip", "", false, []float64{1, 0, 0, 0, 0, 0}}, 322 }) 323 } 324 325 func TestMergedWithScope(t *testing.T) { 326 src := newMetricTable(20, start) 327 dest := newMetricTable(20, start) 328 329 src.addSingleCount("one", unforced) 330 src.addDuration("two", "", 2*time.Second, 1*time.Second, unforced) 331 dest.addDuration("two", "my_scope", 2*time.Second, 1*time.Second, unforced) 332 dest.merge(src, "my_scope") 333 334 ExpectMetrics(t, dest, []WantMetric{ 335 {"one", "my_scope", false, []float64{1, 0, 0, 0, 0, 0}}, 336 {"two", "my_scope", false, []float64{2, 4, 2, 2, 2, 8}}, 337 }) 338 }