github.com/newrelic/go-agent@v3.26.0+incompatible/internal/harvest_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 "testing" 8 "time" 9 ) 10 11 func TestHarvestTimerAllFixed(t *testing.T) { 12 now := time.Now() 13 harvest := NewHarvest(now, &DfltHarvestCfgr{}) 14 timer := harvest.timer 15 for _, tc := range []struct { 16 Elapsed time.Duration 17 Expect HarvestTypes 18 }{ 19 {60 * time.Second, 0}, 20 {61 * time.Second, HarvestTypesAll}, 21 {62 * time.Second, 0}, 22 {120 * time.Second, 0}, 23 {121 * time.Second, HarvestTypesAll}, 24 {122 * time.Second, 0}, 25 } { 26 if ready := timer.ready(now.Add(tc.Elapsed)); ready != tc.Expect { 27 t.Error(tc.Elapsed, ready, tc.Expect) 28 } 29 } 30 } 31 32 var one uint = 1 33 var two uint = 2 34 var three uint = 3 35 var four uint = 4 36 37 func TestHarvestTimerAllConfigurable(t *testing.T) { 38 now := time.Now() 39 harvest := NewHarvest(now, &DfltHarvestCfgr{ 40 reportPeriods: map[HarvestTypes]time.Duration{ 41 HarvestMetricsTraces: FixedHarvestPeriod, 42 HarvestTypesEvents: time.Second * 30, 43 }, 44 maxTxnEvents: &one, 45 maxCustomEvents: &two, 46 maxSpanEvents: &three, 47 maxErrorEvents: &four, 48 }) 49 timer := harvest.timer 50 for _, tc := range []struct { 51 Elapsed time.Duration 52 Expect HarvestTypes 53 }{ 54 {30 * time.Second, 0}, 55 {31 * time.Second, HarvestTypesEvents}, 56 {32 * time.Second, 0}, 57 {61 * time.Second, HarvestTypesAll}, 58 {62 * time.Second, 0}, 59 {91 * time.Second, HarvestTypesEvents}, 60 {92 * time.Second, 0}, 61 } { 62 if ready := timer.ready(now.Add(tc.Elapsed)); ready != tc.Expect { 63 t.Error(tc.Elapsed, ready, tc.Expect) 64 } 65 } 66 } 67 68 func TestCreateFinalMetrics(t *testing.T) { 69 now := time.Now() 70 71 // If the harvest or metrics is nil then CreateFinalMetrics should 72 // not panic. 73 var nilHarvest *Harvest 74 nilHarvest.CreateFinalMetrics(nil, &DfltHarvestCfgr{}) 75 emptyHarvest := &Harvest{} 76 emptyHarvest.CreateFinalMetrics(nil, &DfltHarvestCfgr{}) 77 78 replyJSON := []byte(`{"return_value":{ 79 "metric_name_rules":[{ 80 "match_expression": "rename_me", 81 "replacement": "been_renamed" 82 }], 83 "event_harvest_config":{ 84 "report_period_ms": 2000, 85 "harvest_limits": { 86 "analytic_event_data": 22, 87 "custom_event_data": 33, 88 "error_event_data": 44, 89 "span_event_data": 55 90 } 91 } 92 }}`) 93 reply, err := ConstructConnectReply(replyJSON, PreconnectReply{}) 94 if err != nil { 95 t.Fatal(err) 96 } 97 var txnEvents uint = 22 98 var customEvents uint = 33 99 var errorEvents uint = 44 100 var spanEvents uint = 55 101 cfgr := &DfltHarvestCfgr{ 102 reportPeriods: map[HarvestTypes]time.Duration{ 103 HarvestMetricsTraces: FixedHarvestPeriod, 104 HarvestTypesEvents: time.Second * 2, 105 }, 106 maxTxnEvents: &txnEvents, 107 maxCustomEvents: &customEvents, 108 maxErrorEvents: &errorEvents, 109 maxSpanEvents: &spanEvents, 110 } 111 h := NewHarvest(now, cfgr) 112 h.Metrics.addCount("rename_me", 1.0, unforced) 113 h.CreateFinalMetrics(reply, cfgr) 114 ExpectMetrics(t, h.Metrics, []WantMetric{ 115 {instanceReporting, "", true, []float64{1, 0, 0, 0, 0, 0}}, 116 {"been_renamed", "", false, []float64{1.0, 0, 0, 0, 0, 0}}, 117 {"Supportability/EventHarvest/ReportPeriod", "", true, []float64{1, 2, 2, 2, 2, 2 * 2}}, 118 {"Supportability/EventHarvest/AnalyticEventData/HarvestLimit", "", true, []float64{1, 22, 22, 22, 22, 22 * 22}}, 119 {"Supportability/EventHarvest/CustomEventData/HarvestLimit", "", true, []float64{1, 33, 33, 33, 33, 33 * 33}}, 120 {"Supportability/EventHarvest/ErrorEventData/HarvestLimit", "", true, []float64{1, 44, 44, 44, 44, 44 * 44}}, 121 {"Supportability/EventHarvest/SpanEventData/HarvestLimit", "", true, []float64{1, 55, 55, 55, 55, 55 * 55}}, 122 }) 123 124 // Test again without any metric rules or event_harvest_config. 125 126 replyJSON = []byte(`{"return_value":{ 127 }}`) 128 reply, err = ConstructConnectReply(replyJSON, PreconnectReply{}) 129 if err != nil { 130 t.Fatal(err) 131 } 132 h = NewHarvest(now, &DfltHarvestCfgr{}) 133 h.Metrics.addCount("rename_me", 1.0, unforced) 134 h.CreateFinalMetrics(reply, &DfltHarvestCfgr{}) 135 ExpectMetrics(t, h.Metrics, []WantMetric{ 136 {instanceReporting, "", true, []float64{1, 0, 0, 0, 0, 0}}, 137 {"rename_me", "", false, []float64{1.0, 0, 0, 0, 0, 0}}, 138 {"Supportability/EventHarvest/ReportPeriod", "", true, []float64{1, 60, 60, 60, 60, 60 * 60}}, 139 {"Supportability/EventHarvest/AnalyticEventData/HarvestLimit", "", true, []float64{1, 10 * 1000, 10 * 1000, 10 * 1000, 10 * 1000, 10 * 1000 * 10 * 1000}}, 140 {"Supportability/EventHarvest/CustomEventData/HarvestLimit", "", true, []float64{1, 10 * 1000, 10 * 1000, 10 * 1000, 10 * 1000, 10 * 1000 * 10 * 1000}}, 141 {"Supportability/EventHarvest/ErrorEventData/HarvestLimit", "", true, []float64{1, 100, 100, 100, 100, 100 * 100}}, 142 {"Supportability/EventHarvest/SpanEventData/HarvestLimit", "", true, []float64{1, 1000, 1000, 1000, 1000, 1000 * 1000}}, 143 }) 144 } 145 146 func TestEmptyPayloads(t *testing.T) { 147 h := NewHarvest(time.Now(), &DfltHarvestCfgr{}) 148 payloads := h.Payloads(true) 149 if len(payloads) != 8 { 150 t.Error(len(payloads)) 151 } 152 for _, p := range payloads { 153 d, err := p.Data("agentRunID", time.Now()) 154 if d != nil || err != nil { 155 t.Error(d, err) 156 } 157 } 158 } 159 func TestPayloadsNilHarvest(t *testing.T) { 160 var nilHarvest *Harvest 161 payloads := nilHarvest.Payloads(true) 162 if len(payloads) != 0 { 163 t.Error(len(payloads)) 164 } 165 } 166 167 func TestPayloadsEmptyHarvest(t *testing.T) { 168 h := &Harvest{} 169 payloads := h.Payloads(true) 170 if len(payloads) != 0 { 171 t.Error(len(payloads)) 172 } 173 } 174 175 func TestHarvestNothingReady(t *testing.T) { 176 now := time.Now() 177 h := NewHarvest(now, &DfltHarvestCfgr{}) 178 ready := h.Ready(now.Add(10 * time.Second)) 179 if ready != nil { 180 t.Error("harvest should be nil") 181 } 182 payloads := ready.Payloads(true) 183 if len(payloads) != 0 { 184 t.Error(payloads) 185 } 186 ExpectMetrics(t, h.Metrics, []WantMetric{}) 187 } 188 189 func TestHarvestCustomEventsReady(t *testing.T) { 190 now := time.Now() 191 fixedHarvestTypes := HarvestMetricsTraces & HarvestTxnEvents & HarvestSpanEvents & HarvestErrorEvents 192 h := NewHarvest(now, &DfltHarvestCfgr{ 193 reportPeriods: map[HarvestTypes]time.Duration{ 194 fixedHarvestTypes: FixedHarvestPeriod, 195 HarvestCustomEvents: time.Second * 5, 196 }, 197 maxCustomEvents: &three, 198 }) 199 params := map[string]interface{}{"zip": 1} 200 ce, _ := CreateCustomEvent("myEvent", params, time.Now()) 201 h.CustomEvents.Add(ce) 202 ready := h.Ready(now.Add(10 * time.Second)) 203 payloads := ready.Payloads(true) 204 if len(payloads) != 1 { 205 t.Fatal(payloads) 206 } 207 p := payloads[0] 208 if m := p.EndpointMethod(); m != "custom_event_data" { 209 t.Error(m) 210 } 211 data, err := p.Data("agentRunID", now) 212 if nil != err || nil == data { 213 t.Error(err, data) 214 } 215 if h.CustomEvents.capacity() != 3 || h.CustomEvents.NumSaved() != 0 { 216 t.Fatal("custom events not correctly reset") 217 } 218 ExpectCustomEvents(t, ready.CustomEvents, []WantEvent{{ 219 Intrinsics: map[string]interface{}{"type": "myEvent", "timestamp": MatchAnything}, 220 UserAttributes: params, 221 }}) 222 ExpectMetrics(t, h.Metrics, []WantMetric{ 223 {customEventsSeen, "", true, []float64{1, 0, 0, 0, 0, 0}}, 224 {customEventsSent, "", true, []float64{1, 0, 0, 0, 0, 0}}, 225 }) 226 } 227 228 func TestHarvestTxnEventsReady(t *testing.T) { 229 now := time.Now() 230 fixedHarvestTypes := HarvestMetricsTraces & HarvestCustomEvents & HarvestSpanEvents & HarvestErrorEvents 231 h := NewHarvest(now, &DfltHarvestCfgr{ 232 reportPeriods: map[HarvestTypes]time.Duration{ 233 fixedHarvestTypes: FixedHarvestPeriod, 234 HarvestTxnEvents: time.Second * 5, 235 }, 236 maxTxnEvents: &three, 237 }) 238 h.TxnEvents.AddTxnEvent(&TxnEvent{ 239 FinalName: "finalName", 240 Start: time.Now(), 241 Duration: 1 * time.Second, 242 TotalTime: 2 * time.Second, 243 }, 0) 244 ready := h.Ready(now.Add(10 * time.Second)) 245 payloads := ready.Payloads(true) 246 if len(payloads) != 1 { 247 t.Fatal(payloads) 248 } 249 p := payloads[0] 250 if m := p.EndpointMethod(); m != "analytic_event_data" { 251 t.Error(m) 252 } 253 data, err := p.Data("agentRunID", now) 254 if nil != err || nil == data { 255 t.Error(err, data) 256 } 257 if h.TxnEvents.capacity() != 3 || h.TxnEvents.NumSaved() != 0 { 258 t.Fatal("txn events not correctly reset") 259 } 260 ExpectTxnEvents(t, ready.TxnEvents, []WantEvent{{ 261 Intrinsics: map[string]interface{}{ 262 "name": "finalName", 263 "totalTime": 2.0, 264 }, 265 }}) 266 ExpectMetrics(t, h.Metrics, []WantMetric{ 267 {txnEventsSeen, "", true, []float64{1, 0, 0, 0, 0, 0}}, 268 {txnEventsSent, "", true, []float64{1, 0, 0, 0, 0, 0}}, 269 }) 270 } 271 272 func TestHarvestErrorEventsReady(t *testing.T) { 273 now := time.Now() 274 fixedHarvestTypes := HarvestMetricsTraces & HarvestCustomEvents & HarvestSpanEvents & HarvestTxnEvents 275 h := NewHarvest(now, &DfltHarvestCfgr{ 276 reportPeriods: map[HarvestTypes]time.Duration{ 277 fixedHarvestTypes: FixedHarvestPeriod, 278 HarvestErrorEvents: time.Second * 5, 279 }, 280 maxErrorEvents: &three, 281 }) 282 h.ErrorEvents.Add(&ErrorEvent{ 283 ErrorData: ErrorData{Klass: "klass", Msg: "msg", When: time.Now()}, 284 TxnEvent: TxnEvent{FinalName: "finalName", Duration: 1 * time.Second}, 285 }, 0) 286 ready := h.Ready(now.Add(10 * time.Second)) 287 payloads := ready.Payloads(true) 288 if len(payloads) != 1 { 289 t.Fatal(payloads) 290 } 291 p := payloads[0] 292 if m := p.EndpointMethod(); m != "error_event_data" { 293 t.Error(m) 294 } 295 data, err := p.Data("agentRunID", now) 296 if nil != err || nil == data { 297 t.Error(err, data) 298 } 299 if h.ErrorEvents.capacity() != 3 || h.ErrorEvents.NumSaved() != 0 { 300 t.Fatal("error events not correctly reset") 301 } 302 ExpectErrorEvents(t, ready.ErrorEvents, []WantEvent{{ 303 Intrinsics: map[string]interface{}{ 304 "error.class": "klass", 305 "error.message": "msg", 306 "transactionName": "finalName", 307 }, 308 }}) 309 ExpectMetrics(t, h.Metrics, []WantMetric{ 310 {errorEventsSeen, "", true, []float64{1, 0, 0, 0, 0, 0}}, 311 {errorEventsSent, "", true, []float64{1, 0, 0, 0, 0, 0}}, 312 }) 313 } 314 315 func TestHarvestSpanEventsReady(t *testing.T) { 316 now := time.Now() 317 fixedHarvestTypes := HarvestMetricsTraces & HarvestCustomEvents & HarvestTxnEvents & HarvestErrorEvents 318 h := NewHarvest(now, &DfltHarvestCfgr{ 319 reportPeriods: map[HarvestTypes]time.Duration{ 320 fixedHarvestTypes: FixedHarvestPeriod, 321 HarvestSpanEvents: time.Second * 5, 322 }, 323 maxSpanEvents: &three, 324 }) 325 h.SpanEvents.addEventPopulated(&sampleSpanEvent) 326 ready := h.Ready(now.Add(10 * time.Second)) 327 payloads := ready.Payloads(true) 328 if len(payloads) != 1 { 329 t.Fatal(payloads) 330 } 331 p := payloads[0] 332 if m := p.EndpointMethod(); m != "span_event_data" { 333 t.Error(m) 334 } 335 data, err := p.Data("agentRunID", now) 336 if nil != err || nil == data { 337 t.Error(err, data) 338 } 339 if h.SpanEvents.capacity() != 3 || h.SpanEvents.NumSaved() != 0 { 340 t.Fatal("span events not correctly reset") 341 } 342 ExpectSpanEvents(t, ready.SpanEvents, []WantEvent{{ 343 Intrinsics: map[string]interface{}{ 344 "type": "Span", 345 "name": "myName", 346 "sampled": true, 347 "priority": 0.5, 348 "category": spanCategoryGeneric, 349 "nr.entryPoint": true, 350 "guid": "guid", 351 "transactionId": "txn-id", 352 "traceId": "trace-id", 353 }, 354 }}) 355 ExpectMetrics(t, h.Metrics, []WantMetric{ 356 {spanEventsSeen, "", true, []float64{1, 0, 0, 0, 0, 0}}, 357 {spanEventsSent, "", true, []float64{1, 0, 0, 0, 0, 0}}, 358 }) 359 } 360 361 func TestHarvestMetricsTracesReady(t *testing.T) { 362 now := time.Now() 363 h := NewHarvest(now, &DfltHarvestCfgr{ 364 reportPeriods: map[HarvestTypes]time.Duration{ 365 HarvestMetricsTraces: FixedHarvestPeriod, 366 HarvestTypesEvents: time.Second * 65, 367 }, 368 maxTxnEvents: &one, 369 maxCustomEvents: &one, 370 maxErrorEvents: &one, 371 maxSpanEvents: &one, 372 }) 373 h.Metrics.addCount("zip", 1, forced) 374 375 ers := NewTxnErrors(10) 376 ers.Add(ErrorData{When: time.Now(), Msg: "msg", Klass: "klass", Stack: GetStackTrace()}) 377 MergeTxnErrors(&h.ErrorTraces, ers, TxnEvent{FinalName: "finalName", Attrs: nil}) 378 379 h.TxnTraces.Witness(HarvestTrace{ 380 TxnEvent: TxnEvent{ 381 Start: time.Now(), 382 Duration: 20 * time.Second, 383 TotalTime: 30 * time.Second, 384 FinalName: "WebTransaction/Go/hello", 385 }, 386 Trace: TxnTrace{}, 387 }) 388 389 slows := newSlowQueries(maxTxnSlowQueries) 390 slows.observeInstance(slowQueryInstance{ 391 Duration: 2 * time.Second, 392 DatastoreMetric: "Datastore/statement/MySQL/users/INSERT", 393 ParameterizedQuery: "INSERT users", 394 }) 395 h.SlowSQLs.Merge(slows, TxnEvent{FinalName: "finalName", Attrs: nil}) 396 397 ready := h.Ready(now.Add(61 * time.Second)) 398 payloads := ready.Payloads(true) 399 if len(payloads) != 4 { 400 t.Fatal(payloads) 401 } 402 403 ExpectMetrics(t, ready.Metrics, []WantMetric{ 404 {"zip", "", true, []float64{1, 0, 0, 0, 0, 0}}, 405 }) 406 ExpectMetrics(t, h.Metrics, []WantMetric{}) 407 408 ExpectErrors(t, ready.ErrorTraces, []WantError{{ 409 TxnName: "finalName", 410 Msg: "msg", 411 Klass: "klass", 412 }}) 413 ExpectErrors(t, h.ErrorTraces, []WantError{}) 414 415 ExpectSlowQueries(t, ready.SlowSQLs, []WantSlowQuery{{ 416 Count: 1, 417 MetricName: "Datastore/statement/MySQL/users/INSERT", 418 Query: "INSERT users", 419 TxnName: "finalName", 420 }}) 421 ExpectSlowQueries(t, h.SlowSQLs, []WantSlowQuery{}) 422 423 ExpectTxnTraces(t, ready.TxnTraces, []WantTxnTrace{{ 424 MetricName: "WebTransaction/Go/hello", 425 }}) 426 ExpectTxnTraces(t, h.TxnTraces, []WantTxnTrace{}) 427 } 428 429 func TestMergeFailedHarvest(t *testing.T) { 430 start1 := time.Now() 431 start2 := start1.Add(1 * time.Minute) 432 433 h := NewHarvest(start1, &DfltHarvestCfgr{}) 434 h.Metrics.addCount("zip", 1, forced) 435 h.TxnEvents.AddTxnEvent(&TxnEvent{ 436 FinalName: "finalName", 437 Start: time.Now(), 438 Duration: 1 * time.Second, 439 TotalTime: 2 * time.Second, 440 }, 0) 441 customEventParams := map[string]interface{}{"zip": 1} 442 ce, err := CreateCustomEvent("myEvent", customEventParams, time.Now()) 443 if nil != err { 444 t.Fatal(err) 445 } 446 h.CustomEvents.Add(ce) 447 h.ErrorEvents.Add(&ErrorEvent{ 448 ErrorData: ErrorData{ 449 Klass: "klass", 450 Msg: "msg", 451 When: time.Now(), 452 }, 453 TxnEvent: TxnEvent{ 454 FinalName: "finalName", 455 Duration: 1 * time.Second, 456 }, 457 }, 0) 458 459 ers := NewTxnErrors(10) 460 ers.Add(ErrorData{ 461 When: time.Now(), 462 Msg: "msg", 463 Klass: "klass", 464 Stack: GetStackTrace(), 465 }) 466 MergeTxnErrors(&h.ErrorTraces, ers, TxnEvent{ 467 FinalName: "finalName", 468 Attrs: nil, 469 }) 470 h.SpanEvents.addEventPopulated(&sampleSpanEvent) 471 472 if start1 != h.Metrics.metricPeriodStart { 473 t.Error(h.Metrics.metricPeriodStart) 474 } 475 if 0 != h.Metrics.failedHarvests { 476 t.Error(h.Metrics.failedHarvests) 477 } 478 if 0 != h.CustomEvents.analyticsEvents.failedHarvests { 479 t.Error(h.CustomEvents.analyticsEvents.failedHarvests) 480 } 481 if 0 != h.TxnEvents.analyticsEvents.failedHarvests { 482 t.Error(h.TxnEvents.analyticsEvents.failedHarvests) 483 } 484 if 0 != h.ErrorEvents.analyticsEvents.failedHarvests { 485 t.Error(h.ErrorEvents.analyticsEvents.failedHarvests) 486 } 487 if 0 != h.SpanEvents.analyticsEvents.failedHarvests { 488 t.Error(h.SpanEvents.analyticsEvents.failedHarvests) 489 } 490 ExpectMetrics(t, h.Metrics, []WantMetric{ 491 {"zip", "", true, []float64{1, 0, 0, 0, 0, 0}}, 492 }) 493 ExpectCustomEvents(t, h.CustomEvents, []WantEvent{{ 494 Intrinsics: map[string]interface{}{ 495 "type": "myEvent", 496 "timestamp": MatchAnything, 497 }, 498 UserAttributes: customEventParams, 499 }}) 500 ExpectErrorEvents(t, h.ErrorEvents, []WantEvent{{ 501 Intrinsics: map[string]interface{}{ 502 "error.class": "klass", 503 "error.message": "msg", 504 "transactionName": "finalName", 505 }, 506 }}) 507 ExpectTxnEvents(t, h.TxnEvents, []WantEvent{{ 508 Intrinsics: map[string]interface{}{ 509 "name": "finalName", 510 "totalTime": 2.0, 511 }, 512 }}) 513 ExpectSpanEvents(t, h.SpanEvents, []WantEvent{{ 514 Intrinsics: map[string]interface{}{ 515 "type": "Span", 516 "name": "myName", 517 "sampled": true, 518 "priority": 0.5, 519 "category": spanCategoryGeneric, 520 "nr.entryPoint": true, 521 "guid": "guid", 522 "transactionId": "txn-id", 523 "traceId": "trace-id", 524 }, 525 }}) 526 ExpectErrors(t, h.ErrorTraces, []WantError{{ 527 TxnName: "finalName", 528 Msg: "msg", 529 Klass: "klass", 530 }}) 531 532 nextHarvest := NewHarvest(start2, &DfltHarvestCfgr{}) 533 if start2 != nextHarvest.Metrics.metricPeriodStart { 534 t.Error(nextHarvest.Metrics.metricPeriodStart) 535 } 536 payloads := h.Payloads(true) 537 for _, p := range payloads { 538 p.MergeIntoHarvest(nextHarvest) 539 } 540 541 if start1 != nextHarvest.Metrics.metricPeriodStart { 542 t.Error(nextHarvest.Metrics.metricPeriodStart) 543 } 544 if 1 != nextHarvest.Metrics.failedHarvests { 545 t.Error(nextHarvest.Metrics.failedHarvests) 546 } 547 if 1 != nextHarvest.CustomEvents.analyticsEvents.failedHarvests { 548 t.Error(nextHarvest.CustomEvents.analyticsEvents.failedHarvests) 549 } 550 if 1 != nextHarvest.TxnEvents.analyticsEvents.failedHarvests { 551 t.Error(nextHarvest.TxnEvents.analyticsEvents.failedHarvests) 552 } 553 if 1 != nextHarvest.ErrorEvents.analyticsEvents.failedHarvests { 554 t.Error(nextHarvest.ErrorEvents.analyticsEvents.failedHarvests) 555 } 556 if 1 != nextHarvest.SpanEvents.analyticsEvents.failedHarvests { 557 t.Error(nextHarvest.SpanEvents.analyticsEvents.failedHarvests) 558 } 559 ExpectMetrics(t, nextHarvest.Metrics, []WantMetric{ 560 {"zip", "", true, []float64{1, 0, 0, 0, 0, 0}}, 561 }) 562 ExpectCustomEvents(t, nextHarvest.CustomEvents, []WantEvent{{ 563 Intrinsics: map[string]interface{}{ 564 "type": "myEvent", 565 "timestamp": MatchAnything, 566 }, 567 UserAttributes: customEventParams, 568 }}) 569 ExpectErrorEvents(t, nextHarvest.ErrorEvents, []WantEvent{{ 570 Intrinsics: map[string]interface{}{ 571 "error.class": "klass", 572 "error.message": "msg", 573 "transactionName": "finalName", 574 }, 575 }}) 576 ExpectTxnEvents(t, nextHarvest.TxnEvents, []WantEvent{{ 577 Intrinsics: map[string]interface{}{ 578 "name": "finalName", 579 "totalTime": 2.0, 580 }, 581 }}) 582 ExpectSpanEvents(t, h.SpanEvents, []WantEvent{{ 583 Intrinsics: map[string]interface{}{ 584 "type": "Span", 585 "name": "myName", 586 "sampled": true, 587 "priority": 0.5, 588 "category": spanCategoryGeneric, 589 "nr.entryPoint": true, 590 "guid": "guid", 591 "transactionId": "txn-id", 592 "traceId": "trace-id", 593 }, 594 }}) 595 ExpectErrors(t, nextHarvest.ErrorTraces, []WantError{}) 596 } 597 598 func TestCreateTxnMetrics(t *testing.T) { 599 txnErr := &ErrorData{} 600 txnErrors := []*ErrorData{txnErr} 601 webName := "WebTransaction/zip/zap" 602 backgroundName := "OtherTransaction/zip/zap" 603 args := &TxnData{} 604 args.Duration = 123 * time.Second 605 args.TotalTime = 150 * time.Second 606 args.ApdexThreshold = 2 * time.Second 607 608 args.BetterCAT.Enabled = true 609 610 args.FinalName = webName 611 args.IsWeb = true 612 args.Errors = txnErrors 613 args.Zone = ApdexTolerating 614 metrics := newMetricTable(100, time.Now()) 615 CreateTxnMetrics(args, metrics) 616 ExpectMetrics(t, metrics, []WantMetric{ 617 {webName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 618 {webRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 619 {dispatcherMetric, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 620 {"WebTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}}, 621 {"WebTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}}, 622 {"Errors/all", "", true, []float64{1, 0, 0, 0, 0, 0}}, 623 {"Errors/allWeb", "", true, []float64{1, 0, 0, 0, 0, 0}}, 624 {"Errors/" + webName, "", true, []float64{1, 0, 0, 0, 0, 0}}, 625 {apdexRollup, "", true, []float64{0, 1, 0, 2, 2, 0}}, 626 {"Apdex/zip/zap", "", false, []float64{0, 1, 0, 2, 2, 0}}, 627 {"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}}, 628 {"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}}, 629 {"ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/all", "", false, []float64{1, 0, 0, 0, 0, 0}}, 630 {"ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", "", false, []float64{1, 0, 0, 0, 0, 0}}, 631 }) 632 633 args.FinalName = webName 634 args.IsWeb = true 635 args.Errors = nil 636 args.Zone = ApdexTolerating 637 metrics = newMetricTable(100, time.Now()) 638 CreateTxnMetrics(args, metrics) 639 ExpectMetrics(t, metrics, []WantMetric{ 640 {webName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 641 {webRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 642 {dispatcherMetric, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 643 {"WebTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}}, 644 {"WebTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}}, 645 {apdexRollup, "", true, []float64{0, 1, 0, 2, 2, 0}}, 646 {"Apdex/zip/zap", "", false, []float64{0, 1, 0, 2, 2, 0}}, 647 {"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}}, 648 {"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allWeb", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}}, 649 }) 650 651 args.FinalName = backgroundName 652 args.IsWeb = false 653 args.Errors = txnErrors 654 args.Zone = ApdexNone 655 metrics = newMetricTable(100, time.Now()) 656 CreateTxnMetrics(args, metrics) 657 ExpectMetrics(t, metrics, []WantMetric{ 658 {backgroundName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 659 {backgroundRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 660 {"OtherTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}}, 661 {"OtherTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}}, 662 {"Errors/all", "", true, []float64{1, 0, 0, 0, 0, 0}}, 663 {"Errors/allOther", "", true, []float64{1, 0, 0, 0, 0, 0}}, 664 {"Errors/" + backgroundName, "", true, []float64{1, 0, 0, 0, 0, 0}}, 665 {"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}}, 666 {"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}}, 667 {"ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/all", "", false, []float64{1, 0, 0, 0, 0, 0}}, 668 {"ErrorsByCaller/Unknown/Unknown/Unknown/Unknown/allOther", "", false, []float64{1, 0, 0, 0, 0, 0}}, 669 }) 670 671 args.FinalName = backgroundName 672 args.IsWeb = false 673 args.Errors = nil 674 args.Zone = ApdexNone 675 metrics = newMetricTable(100, time.Now()) 676 CreateTxnMetrics(args, metrics) 677 ExpectMetrics(t, metrics, []WantMetric{ 678 {backgroundName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 679 {backgroundRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 680 {"OtherTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}}, 681 {"OtherTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}}, 682 {"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}}, 683 {"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther", "", false, []float64{1, 123, 123, 123, 123, 123 * 123}}, 684 }) 685 686 } 687 688 func TestHarvestSplitTxnEvents(t *testing.T) { 689 now := time.Now() 690 h := NewHarvest(now, &DfltHarvestCfgr{}) 691 for i := 0; i < MaxTxnEvents; i++ { 692 h.TxnEvents.AddTxnEvent(&TxnEvent{}, Priority(float32(i))) 693 } 694 695 payloadsWithSplit := h.Payloads(true) 696 payloadsWithoutSplit := h.Payloads(false) 697 698 if len(payloadsWithSplit) != 9 { 699 t.Error(len(payloadsWithSplit)) 700 } 701 if len(payloadsWithoutSplit) != 8 { 702 t.Error(len(payloadsWithoutSplit)) 703 } 704 } 705 706 func TestCreateTxnMetricsOldCAT(t *testing.T) { 707 txnErr := &ErrorData{} 708 txnErrors := []*ErrorData{txnErr} 709 webName := "WebTransaction/zip/zap" 710 backgroundName := "OtherTransaction/zip/zap" 711 args := &TxnData{} 712 args.Duration = 123 * time.Second 713 args.TotalTime = 150 * time.Second 714 args.ApdexThreshold = 2 * time.Second 715 716 // When BetterCAT is disabled, affirm that the caller metrics are not created. 717 args.BetterCAT.Enabled = false 718 719 args.FinalName = webName 720 args.IsWeb = true 721 args.Errors = txnErrors 722 args.Zone = ApdexTolerating 723 metrics := newMetricTable(100, time.Now()) 724 CreateTxnMetrics(args, metrics) 725 ExpectMetrics(t, metrics, []WantMetric{ 726 {webName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 727 {webRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 728 {dispatcherMetric, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 729 {"WebTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}}, 730 {"WebTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}}, 731 {"Errors/all", "", true, []float64{1, 0, 0, 0, 0, 0}}, 732 {"Errors/allWeb", "", true, []float64{1, 0, 0, 0, 0, 0}}, 733 {"Errors/" + webName, "", true, []float64{1, 0, 0, 0, 0, 0}}, 734 {apdexRollup, "", true, []float64{0, 1, 0, 2, 2, 0}}, 735 {"Apdex/zip/zap", "", false, []float64{0, 1, 0, 2, 2, 0}}, 736 }) 737 738 args.FinalName = webName 739 args.IsWeb = true 740 args.Errors = nil 741 args.Zone = ApdexTolerating 742 metrics = newMetricTable(100, time.Now()) 743 CreateTxnMetrics(args, metrics) 744 ExpectMetrics(t, metrics, []WantMetric{ 745 {webName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 746 {webRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 747 {dispatcherMetric, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 748 {"WebTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}}, 749 {"WebTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}}, 750 {apdexRollup, "", true, []float64{0, 1, 0, 2, 2, 0}}, 751 {"Apdex/zip/zap", "", false, []float64{0, 1, 0, 2, 2, 0}}, 752 }) 753 754 args.FinalName = backgroundName 755 args.IsWeb = false 756 args.Errors = txnErrors 757 args.Zone = ApdexNone 758 metrics = newMetricTable(100, time.Now()) 759 CreateTxnMetrics(args, metrics) 760 ExpectMetrics(t, metrics, []WantMetric{ 761 {backgroundName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 762 {backgroundRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 763 {"OtherTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}}, 764 {"OtherTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}}, 765 {"Errors/all", "", true, []float64{1, 0, 0, 0, 0, 0}}, 766 {"Errors/allOther", "", true, []float64{1, 0, 0, 0, 0, 0}}, 767 {"Errors/" + backgroundName, "", true, []float64{1, 0, 0, 0, 0, 0}}, 768 }) 769 770 args.FinalName = backgroundName 771 args.IsWeb = false 772 args.Errors = nil 773 args.Zone = ApdexNone 774 metrics = newMetricTable(100, time.Now()) 775 CreateTxnMetrics(args, metrics) 776 ExpectMetrics(t, metrics, []WantMetric{ 777 {backgroundName, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 778 {backgroundRollup, "", true, []float64{1, 123, 0, 123, 123, 123 * 123}}, 779 {"OtherTransactionTotalTime", "", true, []float64{1, 150, 150, 150, 150, 150 * 150}}, 780 {"OtherTransactionTotalTime/zip/zap", "", false, []float64{1, 150, 150, 150, 150, 150 * 150}}, 781 }) 782 } 783 784 func TestNewHarvestSetsDefaultValues(t *testing.T) { 785 now := time.Now() 786 h := NewHarvest(now, &DfltHarvestCfgr{}) 787 788 if cp := h.TxnEvents.capacity(); cp != MaxTxnEvents { 789 t.Error("wrong txn event capacity", cp) 790 } 791 if cp := h.CustomEvents.capacity(); cp != MaxCustomEvents { 792 t.Error("wrong custom event capacity", cp) 793 } 794 if cp := h.ErrorEvents.capacity(); cp != MaxErrorEvents { 795 t.Error("wrong error event capacity", cp) 796 } 797 if cp := h.SpanEvents.capacity(); cp != MaxSpanEvents { 798 t.Error("wrong span event capacity", cp) 799 } 800 } 801 802 func TestNewHarvestUsesConnectReply(t *testing.T) { 803 now := time.Now() 804 h := NewHarvest(now, &DfltHarvestCfgr{ 805 reportPeriods: map[HarvestTypes]time.Duration{ 806 HarvestMetricsTraces: FixedHarvestPeriod, 807 HarvestTypesEvents: time.Second * 5, 808 }, 809 maxTxnEvents: &one, 810 maxCustomEvents: &two, 811 maxErrorEvents: &three, 812 maxSpanEvents: &four, 813 }) 814 815 if cp := h.TxnEvents.capacity(); cp != 1 { 816 t.Error("wrong txn event capacity", cp) 817 } 818 if cp := h.CustomEvents.capacity(); cp != 2 { 819 t.Error("wrong custom event capacity", cp) 820 } 821 if cp := h.ErrorEvents.capacity(); cp != 3 { 822 t.Error("wrong error event capacity", cp) 823 } 824 if cp := h.SpanEvents.capacity(); cp != 4 { 825 t.Error("wrong span event capacity", cp) 826 } 827 } 828 829 func TestConfigurableHarvestZeroHarvestLimits(t *testing.T) { 830 now := time.Now() 831 832 var zero uint 833 h := NewHarvest(now, &DfltHarvestCfgr{ 834 reportPeriods: map[HarvestTypes]time.Duration{ 835 HarvestMetricsTraces: FixedHarvestPeriod, 836 HarvestTypesEvents: time.Second * 5, 837 }, 838 maxTxnEvents: &zero, 839 maxCustomEvents: &zero, 840 maxErrorEvents: &zero, 841 maxSpanEvents: &zero, 842 }) 843 if cp := h.TxnEvents.capacity(); cp != 0 { 844 t.Error("wrong txn event capacity", cp) 845 } 846 if cp := h.CustomEvents.capacity(); cp != 0 { 847 t.Error("wrong custom event capacity", cp) 848 } 849 if cp := h.ErrorEvents.capacity(); cp != 0 { 850 t.Error("wrong error event capacity", cp) 851 } 852 if cp := h.SpanEvents.capacity(); cp != 0 { 853 t.Error("wrong error event capacity", cp) 854 } 855 856 // Add events to ensure that adding events to zero-capacity pools is 857 // safe. 858 h.TxnEvents.AddTxnEvent(&TxnEvent{}, 1.0) 859 h.CustomEvents.Add(&CustomEvent{}) 860 h.ErrorEvents.Add(&ErrorEvent{}, 1.0) 861 h.SpanEvents.addEventPopulated(&sampleSpanEvent) 862 863 // Create the payloads to ensure doing so with zero-capacity pools is 864 // safe. 865 payloads := h.Ready(now.Add(2 * time.Minute)).Payloads(false) 866 for _, p := range payloads { 867 js, err := p.Data("agentRunID", now.Add(2*time.Minute)) 868 if nil != err { 869 t.Error(err) 870 continue 871 } 872 // Only metric data should be present. 873 if (p.EndpointMethod() == "metric_data") != 874 (string(js) != "") { 875 t.Error(p.EndpointMethod(), string(js)) 876 } 877 } 878 }