github.com/newrelic/go-agent@v3.26.0+incompatible/internal/txn_trace_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 "net/http" 8 "strconv" 9 "testing" 10 "time" 11 12 "github.com/newrelic/go-agent/internal/cat" 13 "github.com/newrelic/go-agent/internal/logger" 14 ) 15 16 func TestTxnTrace(t *testing.T) { 17 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 18 txndata := &TxnData{} 19 thread := &Thread{} 20 txndata.TxnTrace.Enabled = true 21 txndata.TxnTrace.StackTraceThreshold = 1 * time.Hour 22 txndata.TxnTrace.SegmentThreshold = 0 23 24 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 25 t2 := StartSegment(txndata, thread, start.Add(2*time.Second)) 26 qParams, err := vetQueryParameters(map[string]interface{}{"zip": 1}) 27 if nil != err { 28 t.Error("error creating query params", err) 29 } 30 EndDatastoreSegment(EndDatastoreParams{ 31 TxnData: txndata, 32 Thread: thread, 33 Start: t2, 34 Now: start.Add(3 * time.Second), 35 Product: "MySQL", 36 Operation: "SELECT", 37 Collection: "my_table", 38 ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)", 39 QueryParameters: qParams, 40 Database: "my_db", 41 Host: "db-server-1", 42 PortPathOrID: "3306", 43 }) 44 t3 := StartSegment(txndata, thread, start.Add(4*time.Second)) 45 EndExternalSegment(EndExternalParams{ 46 TxnData: txndata, 47 Thread: thread, 48 Start: t3, 49 Now: start.Add(5 * time.Second), 50 URL: parseURL("http://example.com/zip/zap?secret=shhh"), 51 Logger: logger.ShimLogger{}, 52 }) 53 EndBasicSegment(txndata, thread, t1, start.Add(6*time.Second), "t1") 54 t4 := StartSegment(txndata, thread, start.Add(7*time.Second)) 55 t5 := StartSegment(txndata, thread, start.Add(8*time.Second)) 56 t6 := StartSegment(txndata, thread, start.Add(9*time.Second)) 57 EndBasicSegment(txndata, thread, t6, start.Add(10*time.Second), "t6") 58 EndBasicSegment(txndata, thread, t5, start.Add(11*time.Second), "t5") 59 t7 := StartSegment(txndata, thread, start.Add(12*time.Second)) 60 EndDatastoreSegment(EndDatastoreParams{ 61 TxnData: txndata, 62 Thread: thread, 63 Start: t7, 64 Now: start.Add(13 * time.Second), 65 Product: "MySQL", 66 Operation: "SELECT", 67 // no collection 68 }) 69 t8 := StartSegment(txndata, thread, start.Add(14*time.Second)) 70 EndExternalSegment(EndExternalParams{ 71 TxnData: txndata, 72 Thread: thread, 73 Start: t8, 74 Now: start.Add(15 * time.Second), 75 URL: nil, 76 Logger: logger.ShimLogger{}, 77 }) 78 EndBasicSegment(txndata, thread, t4, start.Add(16*time.Second), "t4") 79 80 t9 := StartSegment(txndata, thread, start.Add(17*time.Second)) 81 EndMessageSegment(EndMessageParams{ 82 TxnData: txndata, 83 Thread: thread, 84 Start: t9, 85 Now: start.Add(18 * time.Second), 86 Logger: nil, 87 DestinationName: "MyTopic", 88 Library: "Kafka", 89 DestinationType: "Topic", 90 }) 91 92 acfg := CreateAttributeConfig(sampleAttributeConfigInput, true) 93 attr := NewAttributes(acfg) 94 attr.Agent.Add(attributeRequestURI, "/url", nil) 95 AddUserAttribute(attr, "zap", 123, DestAll) 96 97 ht := newHarvestTraces() 98 ht.regular.addTxnTrace(&HarvestTrace{ 99 TxnEvent: TxnEvent{ 100 Start: start, 101 Duration: 20 * time.Second, 102 TotalTime: 30 * time.Second, 103 FinalName: "WebTransaction/Go/hello", 104 Attrs: attr, 105 BetterCAT: BetterCAT{ 106 Enabled: true, 107 ID: "txn-id", 108 Priority: 0.5, 109 }, 110 }, 111 Trace: txndata.TxnTrace, 112 }) 113 114 ExpectTxnTraces(t, ht, []WantTxnTrace{{ 115 MetricName: "WebTransaction/Go/hello", 116 UserAttributes: map[string]interface{}{"zap": 123}, 117 AgentAttributes: map[string]interface{}{"request.uri": "/url"}, 118 Intrinsics: map[string]interface{}{ 119 "guid": "txn-id", 120 "traceId": "txn-id", 121 "priority": 0.500000, 122 "sampled": false, 123 "totalTime": 30, 124 }, 125 Root: WantTraceSegment{ 126 SegmentName: "ROOT", 127 RelativeStartMillis: 0, 128 RelativeStopMillis: 20000, 129 Attributes: map[string]interface{}{}, 130 Children: []WantTraceSegment{{ 131 SegmentName: "WebTransaction/Go/hello", 132 RelativeStartMillis: 0, 133 RelativeStopMillis: 20000, 134 Attributes: map[string]interface{}{"exclusive_duration_millis": 20000}, 135 Children: []WantTraceSegment{ 136 { 137 SegmentName: "Custom/t1", 138 RelativeStartMillis: 1000, 139 RelativeStopMillis: 6000, 140 Attributes: map[string]interface{}{}, 141 Children: []WantTraceSegment{ 142 { 143 SegmentName: "Datastore/statement/MySQL/my_table/SELECT", 144 RelativeStartMillis: 2000, 145 RelativeStopMillis: 3000, 146 Attributes: map[string]interface{}{ 147 "db.instance": "my_db", 148 "peer.hostname": "db-server-1", 149 "peer.address": "db-server-1:3306", 150 "db.statement": "INSERT INTO users (name, age) VALUES ($1, $2)", 151 "query_parameters": "map[zip:1]", 152 }, 153 Children: []WantTraceSegment{}, 154 }, 155 { 156 SegmentName: "External/example.com/http", 157 RelativeStartMillis: 4000, 158 RelativeStopMillis: 5000, 159 Attributes: map[string]interface{}{ 160 "http.url": "http://example.com/zip/zap", 161 }, 162 Children: []WantTraceSegment{}, 163 }, 164 }, 165 }, 166 { 167 SegmentName: "Custom/t4", 168 RelativeStartMillis: 7000, 169 RelativeStopMillis: 16000, 170 Attributes: map[string]interface{}{}, 171 Children: []WantTraceSegment{ 172 { 173 SegmentName: "Custom/t5", 174 RelativeStartMillis: 8000, 175 RelativeStopMillis: 11000, 176 Attributes: map[string]interface{}{}, 177 Children: []WantTraceSegment{ 178 { 179 SegmentName: "Custom/t6", 180 RelativeStartMillis: 9000, 181 RelativeStopMillis: 10000, 182 Attributes: map[string]interface{}{}, 183 Children: []WantTraceSegment{}, 184 }, 185 }, 186 }, 187 { 188 SegmentName: "Datastore/operation/MySQL/SELECT", 189 RelativeStartMillis: 12000, 190 RelativeStopMillis: 13000, 191 Attributes: map[string]interface{}{ 192 "db.statement": "'SELECT' on 'unknown' using 'MySQL'", 193 }, 194 Children: []WantTraceSegment{}, 195 }, 196 { 197 SegmentName: "External/unknown/http", 198 RelativeStartMillis: 14000, 199 RelativeStopMillis: 15000, 200 Attributes: map[string]interface{}{}, 201 Children: []WantTraceSegment{}, 202 }, 203 }, 204 }, 205 { 206 SegmentName: "MessageBroker/Kafka/Topic/Produce/Named/MyTopic", 207 RelativeStartMillis: 17000, 208 RelativeStopMillis: 18000, 209 Attributes: map[string]interface{}{}, 210 Children: []WantTraceSegment{}, 211 }, 212 }, 213 }}, 214 }, 215 }}) 216 } 217 218 func TestTxnTraceNoNodes(t *testing.T) { 219 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 220 txndata := &TxnData{} 221 txndata.TxnTrace.Enabled = true 222 txndata.TxnTrace.StackTraceThreshold = 1 * time.Hour 223 txndata.TxnTrace.SegmentThreshold = 0 224 225 ht := newHarvestTraces() 226 ht.regular.addTxnTrace(&HarvestTrace{ 227 TxnEvent: TxnEvent{ 228 Start: start, 229 Duration: 20 * time.Second, 230 TotalTime: 30 * time.Second, 231 FinalName: "WebTransaction/Go/hello", 232 Attrs: nil, 233 BetterCAT: BetterCAT{ 234 Enabled: true, 235 ID: "txn-id", 236 Priority: 0.5, 237 }, 238 }, 239 Trace: txndata.TxnTrace, 240 }) 241 242 ExpectTxnTraces(t, ht, []WantTxnTrace{{ 243 MetricName: "WebTransaction/Go/hello", 244 UserAttributes: map[string]interface{}{}, 245 AgentAttributes: map[string]interface{}{}, 246 Intrinsics: map[string]interface{}{ 247 "guid": "txn-id", 248 "traceId": "txn-id", 249 "priority": 0.500000, 250 "sampled": false, 251 "totalTime": 30, 252 }, 253 Root: WantTraceSegment{ 254 SegmentName: "ROOT", 255 RelativeStartMillis: 0, 256 RelativeStopMillis: 20000, 257 Attributes: map[string]interface{}{}, 258 Children: []WantTraceSegment{{ 259 SegmentName: "WebTransaction/Go/hello", 260 RelativeStartMillis: 0, 261 RelativeStopMillis: 20000, 262 Attributes: map[string]interface{}{"exclusive_duration_millis": 20000}, 263 Children: []WantTraceSegment{}, 264 }}, 265 }, 266 }}) 267 } 268 269 func TestTxnTraceAsync(t *testing.T) { 270 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 271 txndata := &TxnData{ 272 TraceIDGenerator: NewTraceIDGenerator(12345), 273 } 274 thread1 := &Thread{} 275 txndata.TxnTrace.Enabled = true 276 txndata.TxnTrace.StackTraceThreshold = 1 * time.Hour 277 txndata.TxnTrace.SegmentThreshold = 0 278 txndata.BetterCAT.Sampled = true 279 txndata.SpanEventsEnabled = true 280 txndata.LazilyCalculateSampled = func() bool { return true } 281 282 t1s1 := StartSegment(txndata, thread1, start.Add(1*time.Second)) 283 t1s2 := StartSegment(txndata, thread1, start.Add(2*time.Second)) 284 thread2 := NewThread(txndata) 285 t2s1 := StartSegment(txndata, thread2, start.Add(3*time.Second)) 286 EndBasicSegment(txndata, thread1, t1s2, start.Add(4*time.Second), "thread1.segment2") 287 EndBasicSegment(txndata, thread2, t2s1, start.Add(5*time.Second), "thread2.segment1") 288 thread3 := NewThread(txndata) 289 t3s1 := StartSegment(txndata, thread3, start.Add(6*time.Second)) 290 t3s2 := StartSegment(txndata, thread3, start.Add(7*time.Second)) 291 EndBasicSegment(txndata, thread1, t1s1, start.Add(8*time.Second), "thread1.segment1") 292 EndBasicSegment(txndata, thread3, t3s2, start.Add(9*time.Second), "thread3.segment2") 293 EndBasicSegment(txndata, thread3, t3s1, start.Add(10*time.Second), "thread3.segment1") 294 295 if tt := thread1.TotalTime(); tt != 7*time.Second { 296 t.Error(tt) 297 } 298 if tt := thread2.TotalTime(); tt != 2*time.Second { 299 t.Error(tt) 300 } 301 if tt := thread3.TotalTime(); tt != 4*time.Second { 302 t.Error(tt) 303 } 304 305 if len(txndata.spanEvents) != 5 { 306 t.Fatal(txndata.spanEvents) 307 } 308 for _, e := range txndata.spanEvents { 309 if e.GUID == "" || e.ParentID == "" { 310 t.Error(e.GUID, e.ParentID) 311 } 312 } 313 spanEventT1S2 := txndata.spanEvents[0] 314 spanEventT2S1 := txndata.spanEvents[1] 315 spanEventT1S1 := txndata.spanEvents[2] 316 spanEventT3S2 := txndata.spanEvents[3] 317 spanEventT3S1 := txndata.spanEvents[4] 318 319 if txndata.rootSpanID == "" { 320 t.Error(txndata.rootSpanID) 321 } 322 if spanEventT1S1.ParentID != txndata.rootSpanID { 323 t.Error(spanEventT1S1.ParentID, txndata.rootSpanID) 324 } 325 if spanEventT1S2.ParentID != spanEventT1S1.GUID { 326 t.Error(spanEventT1S2.ParentID, spanEventT1S1.GUID) 327 } 328 if spanEventT2S1.ParentID != txndata.rootSpanID { 329 t.Error(spanEventT2S1.ParentID, txndata.rootSpanID) 330 } 331 if spanEventT3S1.ParentID != txndata.rootSpanID { 332 t.Error(spanEventT3S1.ParentID, txndata.rootSpanID) 333 } 334 if spanEventT3S2.ParentID != spanEventT3S1.GUID { 335 t.Error(spanEventT3S2.ParentID, spanEventT3S1.GUID) 336 } 337 338 ht := newHarvestTraces() 339 ht.regular.addTxnTrace(&HarvestTrace{ 340 TxnEvent: TxnEvent{ 341 Start: start, 342 Duration: 20 * time.Second, 343 TotalTime: 30 * time.Second, 344 FinalName: "WebTransaction/Go/hello", 345 Attrs: nil, 346 BetterCAT: BetterCAT{ 347 Enabled: true, 348 ID: "txn-id", 349 Priority: 0.5, 350 }, 351 }, 352 Trace: txndata.TxnTrace, 353 }) 354 355 ExpectTxnTraces(t, ht, []WantTxnTrace{{ 356 MetricName: "WebTransaction/Go/hello", 357 UserAttributes: map[string]interface{}{}, 358 AgentAttributes: map[string]interface{}{}, 359 Intrinsics: map[string]interface{}{ 360 "totalTime": 30, 361 "guid": "txn-id", 362 "traceId": "txn-id", 363 "priority": 0.500000, 364 "sampled": false, 365 }, 366 Root: WantTraceSegment{ 367 SegmentName: "ROOT", 368 RelativeStartMillis: 0, 369 RelativeStopMillis: 20000, 370 Attributes: map[string]interface{}{}, 371 Children: []WantTraceSegment{{ 372 SegmentName: "WebTransaction/Go/hello", 373 RelativeStartMillis: 0, 374 RelativeStopMillis: 20000, 375 Attributes: map[string]interface{}{"exclusive_duration_millis": 20000}, 376 Children: []WantTraceSegment{ 377 { 378 SegmentName: "Custom/thread1.segment1", 379 RelativeStartMillis: 1000, 380 RelativeStopMillis: 8000, 381 Attributes: map[string]interface{}{}, 382 Children: []WantTraceSegment{ 383 { 384 SegmentName: "Custom/thread1.segment2", 385 RelativeStartMillis: 2000, 386 RelativeStopMillis: 4000, 387 Attributes: map[string]interface{}{}, 388 Children: []WantTraceSegment{}, 389 }, 390 }, 391 }, 392 { 393 SegmentName: "Custom/thread2.segment1", 394 RelativeStartMillis: 3000, 395 RelativeStopMillis: 5000, 396 Attributes: map[string]interface{}{}, 397 Children: []WantTraceSegment{}, 398 }, 399 { 400 SegmentName: "Custom/thread3.segment1", 401 RelativeStartMillis: 6000, 402 RelativeStopMillis: 10000, 403 Attributes: map[string]interface{}{}, 404 Children: []WantTraceSegment{ 405 { 406 SegmentName: "Custom/thread3.segment2", 407 RelativeStartMillis: 7000, 408 RelativeStopMillis: 9000, 409 Attributes: map[string]interface{}{}, 410 Children: []WantTraceSegment{}, 411 }, 412 }, 413 }, 414 }, 415 }}, 416 }, 417 }}) 418 } 419 420 func TestTxnTraceOldCAT(t *testing.T) { 421 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 422 txndata := &TxnData{} 423 thread := &Thread{} 424 txndata.TxnTrace.Enabled = true 425 txndata.TxnTrace.StackTraceThreshold = 1 * time.Hour 426 txndata.TxnTrace.SegmentThreshold = 0 427 428 txndata.CrossProcess.Init(true, false, replyAccountOne) 429 txndata.CrossProcess.GUID = "0123456789" 430 appData, err := txndata.CrossProcess.CreateAppData("WebTransaction/Go/otherService", 2*time.Second, 3*time.Second, 123) 431 if nil != err { 432 t.Fatal(err) 433 } 434 resp := &http.Response{ 435 Header: AppDataToHTTPHeader(appData), 436 } 437 t3 := StartSegment(txndata, thread, start.Add(4*time.Second)) 438 EndExternalSegment(EndExternalParams{ 439 TxnData: txndata, 440 Thread: thread, 441 Start: t3, 442 Now: start.Add(5 * time.Second), 443 URL: parseURL("http://example.com/zip/zap?secret=shhh"), 444 Response: resp, 445 Logger: logger.ShimLogger{}, 446 }) 447 448 acfg := CreateAttributeConfig(sampleAttributeConfigInput, true) 449 attr := NewAttributes(acfg) 450 attr.Agent.Add(attributeRequestURI, "/url", nil) 451 AddUserAttribute(attr, "zap", 123, DestAll) 452 453 ht := newHarvestTraces() 454 ht.regular.addTxnTrace(&HarvestTrace{ 455 TxnEvent: TxnEvent{ 456 Start: start, 457 Duration: 20 * time.Second, 458 TotalTime: 30 * time.Second, 459 FinalName: "WebTransaction/Go/hello", 460 Attrs: attr, 461 }, 462 Trace: txndata.TxnTrace, 463 }) 464 465 ExpectTxnTraces(t, ht, []WantTxnTrace{{ 466 MetricName: "WebTransaction/Go/hello", 467 UserAttributes: map[string]interface{}{"zap": 123}, 468 AgentAttributes: map[string]interface{}{"request.uri": "/url"}, 469 Intrinsics: map[string]interface{}{"totalTime": 30}, 470 Root: WantTraceSegment{ 471 SegmentName: "ROOT", 472 RelativeStartMillis: 0, 473 RelativeStopMillis: 20000, 474 Attributes: map[string]interface{}{}, 475 Children: []WantTraceSegment{{ 476 SegmentName: "WebTransaction/Go/hello", 477 RelativeStartMillis: 0, 478 RelativeStopMillis: 20000, 479 Attributes: map[string]interface{}{"exclusive_duration_millis": 20000}, 480 Children: []WantTraceSegment{ 481 { 482 SegmentName: "ExternalTransaction/example.com/1#1/WebTransaction/Go/otherService", 483 RelativeStartMillis: 4000, 484 RelativeStopMillis: 5000, 485 Attributes: map[string]interface{}{ 486 "http.url": "http://example.com/zip/zap", 487 "transaction_guid": "0123456789", 488 }, 489 Children: []WantTraceSegment{}, 490 }, 491 }, 492 }}, 493 }, 494 }}) 495 } 496 497 func TestTxnTraceExcludeURI(t *testing.T) { 498 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 499 tr := &TxnData{} 500 tr.TxnTrace.Enabled = true 501 tr.TxnTrace.StackTraceThreshold = 1 * time.Hour 502 tr.TxnTrace.SegmentThreshold = 0 503 504 c := sampleAttributeConfigInput 505 c.TransactionTracer.Exclude = []string{"request.uri"} 506 acfg := CreateAttributeConfig(c, true) 507 attr := NewAttributes(acfg) 508 attr.Agent.Add(attributeRequestURI, "/url", nil) 509 510 ht := newHarvestTraces() 511 ht.regular.addTxnTrace(&HarvestTrace{ 512 TxnEvent: TxnEvent{ 513 Start: start, 514 Duration: 20 * time.Second, 515 FinalName: "WebTransaction/Go/hello", 516 Attrs: attr, 517 BetterCAT: BetterCAT{ 518 Enabled: true, 519 ID: "txn-id", 520 Priority: 0.5, 521 }, 522 }, 523 Trace: tr.TxnTrace, 524 }) 525 526 ExpectTxnTraces(t, ht, []WantTxnTrace{{ 527 MetricName: "WebTransaction/Go/hello", 528 UserAttributes: map[string]interface{}{}, 529 AgentAttributes: map[string]interface{}{}, 530 Intrinsics: map[string]interface{}{ 531 "totalTime": 0, 532 "guid": "txn-id", 533 "traceId": "txn-id", 534 "priority": 0.500000, 535 "sampled": false, 536 }, 537 Root: WantTraceSegment{ 538 SegmentName: "ROOT", 539 RelativeStartMillis: 0, 540 RelativeStopMillis: 20000, 541 Attributes: map[string]interface{}{}, 542 Children: []WantTraceSegment{{ 543 SegmentName: "WebTransaction/Go/hello", 544 RelativeStartMillis: 0, 545 RelativeStopMillis: 20000, 546 Attributes: map[string]interface{}{"exclusive_duration_millis": 20000}, 547 Children: []WantTraceSegment{}, 548 }}, 549 }, 550 }}) 551 } 552 553 func TestTxnTraceNoSegmentsNoAttributes(t *testing.T) { 554 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 555 txndata := &TxnData{} 556 txndata.TxnTrace.Enabled = true 557 txndata.TxnTrace.StackTraceThreshold = 1 * time.Hour 558 txndata.TxnTrace.SegmentThreshold = 0 559 560 acfg := CreateAttributeConfig(sampleAttributeConfigInput, true) 561 attr := NewAttributes(acfg) 562 563 ht := newHarvestTraces() 564 ht.regular.addTxnTrace(&HarvestTrace{ 565 TxnEvent: TxnEvent{ 566 Start: start, 567 Duration: 20 * time.Second, 568 TotalTime: 30 * time.Second, 569 FinalName: "WebTransaction/Go/hello", 570 Attrs: attr, 571 BetterCAT: BetterCAT{ 572 Enabled: true, 573 ID: "txn-id", 574 Priority: 0.5, 575 }, 576 }, 577 Trace: txndata.TxnTrace, 578 }) 579 580 ExpectTxnTraces(t, ht, []WantTxnTrace{{ 581 MetricName: "WebTransaction/Go/hello", 582 UserAttributes: map[string]interface{}{}, 583 AgentAttributes: map[string]interface{}{}, 584 Intrinsics: map[string]interface{}{ 585 "totalTime": 30, 586 "guid": "txn-id", 587 "traceId": "txn-id", 588 "priority": 0.500000, 589 "sampled": false, 590 }, 591 Root: WantTraceSegment{ 592 SegmentName: "ROOT", 593 RelativeStartMillis: 0, 594 RelativeStopMillis: 20000, 595 Attributes: map[string]interface{}{}, 596 Children: []WantTraceSegment{{ 597 SegmentName: "WebTransaction/Go/hello", 598 RelativeStartMillis: 0, 599 RelativeStopMillis: 20000, 600 Attributes: map[string]interface{}{"exclusive_duration_millis": 20000}, 601 Children: []WantTraceSegment{}, 602 }}, 603 }, 604 }}) 605 } 606 607 func TestTxnTraceSlowestNodesSaved(t *testing.T) { 608 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 609 txndata := &TxnData{} 610 thread := &Thread{} 611 txndata.TxnTrace.Enabled = true 612 txndata.TxnTrace.StackTraceThreshold = 1 * time.Hour 613 txndata.TxnTrace.SegmentThreshold = 0 614 txndata.TxnTrace.maxNodes = 5 615 616 durations := []int{5, 4, 6, 3, 7, 2, 8, 1, 9} 617 now := start 618 for _, d := range durations { 619 s := StartSegment(txndata, thread, now) 620 now = now.Add(time.Duration(d) * time.Second) 621 EndBasicSegment(txndata, thread, s, now, strconv.Itoa(d)) 622 } 623 624 acfg := CreateAttributeConfig(sampleAttributeConfigInput, true) 625 attr := NewAttributes(acfg) 626 attr.Agent.Add(attributeRequestURI, "/url", nil) 627 628 ht := newHarvestTraces() 629 ht.regular.addTxnTrace(&HarvestTrace{ 630 TxnEvent: TxnEvent{ 631 Start: start, 632 Duration: 123 * time.Second, 633 TotalTime: 200 * time.Second, 634 FinalName: "WebTransaction/Go/hello", 635 Attrs: attr, 636 BetterCAT: BetterCAT{ 637 Enabled: true, 638 ID: "txn-id", 639 Priority: 0.5, 640 }, 641 }, 642 Trace: txndata.TxnTrace, 643 }) 644 645 ExpectTxnTraces(t, ht, []WantTxnTrace{{ 646 MetricName: "WebTransaction/Go/hello", 647 UserAttributes: map[string]interface{}{}, 648 AgentAttributes: map[string]interface{}{"request.uri": "/url"}, 649 Intrinsics: map[string]interface{}{ 650 "totalTime": 200, 651 "guid": "txn-id", 652 "traceId": "txn-id", 653 "priority": 0.500000, 654 "sampled": false, 655 }, 656 Root: WantTraceSegment{ 657 SegmentName: "ROOT", 658 RelativeStartMillis: 0, 659 RelativeStopMillis: 123000, 660 Attributes: map[string]interface{}{}, 661 Children: []WantTraceSegment{{ 662 SegmentName: "WebTransaction/Go/hello", 663 RelativeStartMillis: 0, 664 RelativeStopMillis: 123000, 665 Attributes: map[string]interface{}{"exclusive_duration_millis": 123000}, 666 Children: []WantTraceSegment{ 667 { 668 SegmentName: "Custom/5", 669 RelativeStartMillis: 0, 670 RelativeStopMillis: 5000, 671 Attributes: map[string]interface{}{}, 672 Children: []WantTraceSegment{}, 673 }, 674 { 675 SegmentName: "Custom/6", 676 RelativeStartMillis: 9000, 677 RelativeStopMillis: 15000, 678 Attributes: map[string]interface{}{}, 679 Children: []WantTraceSegment{}, 680 }, 681 { 682 SegmentName: "Custom/7", 683 RelativeStartMillis: 18000, 684 RelativeStopMillis: 25000, 685 Attributes: map[string]interface{}{}, 686 Children: []WantTraceSegment{}, 687 }, 688 { 689 SegmentName: "Custom/8", 690 RelativeStartMillis: 27000, 691 RelativeStopMillis: 35000, 692 Attributes: map[string]interface{}{}, 693 Children: []WantTraceSegment{}, 694 }, 695 { 696 SegmentName: "Custom/9", 697 RelativeStartMillis: 36000, 698 RelativeStopMillis: 45000, 699 Attributes: map[string]interface{}{}, 700 Children: []WantTraceSegment{}, 701 }, 702 }, 703 }}, 704 }, 705 }}) 706 } 707 708 func TestTxnTraceSegmentThreshold(t *testing.T) { 709 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 710 txndata := &TxnData{} 711 thread := &Thread{} 712 txndata.TxnTrace.Enabled = true 713 txndata.TxnTrace.StackTraceThreshold = 1 * time.Hour 714 txndata.TxnTrace.SegmentThreshold = 7 * time.Second 715 txndata.TxnTrace.maxNodes = 5 716 717 durations := []int{5, 4, 6, 3, 7, 2, 8, 1, 9} 718 now := start 719 for _, d := range durations { 720 s := StartSegment(txndata, thread, now) 721 now = now.Add(time.Duration(d) * time.Second) 722 EndBasicSegment(txndata, thread, s, now, strconv.Itoa(d)) 723 } 724 725 acfg := CreateAttributeConfig(sampleAttributeConfigInput, true) 726 attr := NewAttributes(acfg) 727 attr.Agent.Add(attributeRequestURI, "/url", nil) 728 729 ht := newHarvestTraces() 730 ht.regular.addTxnTrace(&HarvestTrace{ 731 TxnEvent: TxnEvent{ 732 Start: start, 733 Duration: 123 * time.Second, 734 TotalTime: 200 * time.Second, 735 FinalName: "WebTransaction/Go/hello", 736 Attrs: attr, 737 BetterCAT: BetterCAT{ 738 Enabled: true, 739 ID: "txn-id", 740 Priority: 0.5, 741 }, 742 }, 743 Trace: txndata.TxnTrace, 744 }) 745 746 ExpectTxnTraces(t, ht, []WantTxnTrace{{ 747 MetricName: "WebTransaction/Go/hello", 748 UserAttributes: map[string]interface{}{}, 749 AgentAttributes: map[string]interface{}{"request.uri": "/url"}, 750 Intrinsics: map[string]interface{}{ 751 "totalTime": 200, 752 "guid": "txn-id", 753 "traceId": "txn-id", 754 "priority": 0.500000, 755 "sampled": false, 756 }, 757 Root: WantTraceSegment{ 758 SegmentName: "ROOT", 759 RelativeStartMillis: 0, 760 RelativeStopMillis: 123000, 761 Attributes: map[string]interface{}{}, 762 Children: []WantTraceSegment{{ 763 SegmentName: "WebTransaction/Go/hello", 764 RelativeStartMillis: 0, 765 RelativeStopMillis: 123000, 766 Attributes: map[string]interface{}{"exclusive_duration_millis": 123000}, 767 Children: []WantTraceSegment{ 768 { 769 SegmentName: "Custom/7", 770 RelativeStartMillis: 18000, 771 RelativeStopMillis: 25000, 772 Attributes: map[string]interface{}{}, 773 Children: []WantTraceSegment{}, 774 }, 775 { 776 SegmentName: "Custom/8", 777 RelativeStartMillis: 27000, 778 RelativeStopMillis: 35000, 779 Attributes: map[string]interface{}{}, 780 Children: []WantTraceSegment{}, 781 }, 782 { 783 SegmentName: "Custom/9", 784 RelativeStartMillis: 36000, 785 RelativeStopMillis: 45000, 786 Attributes: map[string]interface{}{}, 787 Children: []WantTraceSegment{}, 788 }, 789 }, 790 }}, 791 }, 792 }}) 793 } 794 795 func TestEmptyHarvestTraces(t *testing.T) { 796 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 797 ht := newHarvestTraces() 798 js, err := ht.Data("12345", start) 799 if nil != err || nil != js { 800 t.Error(string(js), err) 801 } 802 } 803 804 func TestLongestTraceSaved(t *testing.T) { 805 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 806 txndata := &TxnData{} 807 txndata.TxnTrace.Enabled = true 808 809 acfg := CreateAttributeConfig(sampleAttributeConfigInput, true) 810 attr := NewAttributes(acfg) 811 attr.Agent.Add(attributeRequestURI, "/url", nil) 812 ht := newHarvestTraces() 813 814 ht.Witness(HarvestTrace{ 815 TxnEvent: TxnEvent{ 816 Start: start, 817 Duration: 3 * time.Second, 818 TotalTime: 4 * time.Second, 819 FinalName: "WebTransaction/Go/3", 820 Attrs: attr, 821 BetterCAT: BetterCAT{ 822 Enabled: true, 823 ID: "txn-id-3", 824 Priority: 0.5, 825 }, 826 }, 827 Trace: txndata.TxnTrace, 828 }) 829 ht.Witness(HarvestTrace{ 830 TxnEvent: TxnEvent{ 831 Start: start, 832 Duration: 5 * time.Second, 833 TotalTime: 6 * time.Second, 834 FinalName: "WebTransaction/Go/5", 835 Attrs: attr, 836 BetterCAT: BetterCAT{ 837 Enabled: true, 838 ID: "txn-id-5", 839 Priority: 0.5, 840 }, 841 }, 842 Trace: txndata.TxnTrace, 843 }) 844 ht.Witness(HarvestTrace{ 845 TxnEvent: TxnEvent{ 846 Start: start, 847 Duration: 4 * time.Second, 848 TotalTime: 7 * time.Second, 849 FinalName: "WebTransaction/Go/4", 850 Attrs: attr, 851 BetterCAT: BetterCAT{ 852 Enabled: true, 853 ID: "txn-id-4", 854 Priority: 0.5, 855 }, 856 }, 857 Trace: txndata.TxnTrace, 858 }) 859 860 ExpectTxnTraces(t, ht, []WantTxnTrace{{ 861 MetricName: "WebTransaction/Go/5", 862 UserAttributes: map[string]interface{}{}, 863 AgentAttributes: map[string]interface{}{"request.uri": "/url"}, 864 Intrinsics: map[string]interface{}{ 865 "totalTime": 6, 866 "guid": "txn-id-5", 867 "traceId": "txn-id-5", 868 "priority": 0.500000, 869 "sampled": false, 870 }, 871 Root: WantTraceSegment{ 872 SegmentName: "ROOT", 873 RelativeStartMillis: 0, 874 RelativeStopMillis: 5000, 875 Attributes: map[string]interface{}{}, 876 Children: []WantTraceSegment{{ 877 SegmentName: "WebTransaction/Go/5", 878 RelativeStartMillis: 0, 879 RelativeStopMillis: 5000, 880 Attributes: map[string]interface{}{"exclusive_duration_millis": 5000}, 881 Children: []WantTraceSegment{}, 882 }}, 883 }, 884 }}) 885 } 886 887 func TestTxnTraceStackTraceThreshold(t *testing.T) { 888 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 889 txndata := &TxnData{} 890 thread := &Thread{} 891 txndata.TxnTrace.Enabled = true 892 txndata.TxnTrace.StackTraceThreshold = 2 * time.Second 893 txndata.TxnTrace.SegmentThreshold = 0 894 txndata.TxnTrace.maxNodes = 5 895 896 // below stack trace threshold 897 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 898 EndBasicSegment(txndata, thread, t1, start.Add(2*time.Second), "t1") 899 900 // not above stack trace threshold w/out params 901 t2 := StartSegment(txndata, thread, start.Add(2*time.Second)) 902 EndBasicSegment(txndata, thread, t2, start.Add(4*time.Second), "t2") 903 904 // node above stack trace threshold w/ params 905 t3 := StartSegment(txndata, thread, start.Add(4*time.Second)) 906 EndExternalSegment(EndExternalParams{ 907 TxnData: txndata, 908 Thread: thread, 909 Start: t3, 910 Now: start.Add(6 * time.Second), 911 URL: parseURL("http://example.com/zip/zap?secret=shhh"), 912 Logger: logger.ShimLogger{}, 913 }) 914 915 ht := newHarvestTraces() 916 ht.Witness(HarvestTrace{ 917 TxnEvent: TxnEvent{ 918 Start: start, 919 Duration: 3 * time.Second, 920 TotalTime: 4 * time.Second, 921 FinalName: "WebTransaction/Go/3", 922 }, 923 Trace: txndata.TxnTrace, 924 }) 925 926 ExpectTxnTraces(t, ht, []WantTxnTrace{ 927 { 928 MetricName: "WebTransaction/Go/3", 929 UserAttributes: map[string]interface{}{}, 930 AgentAttributes: map[string]interface{}{}, 931 Intrinsics: map[string]interface{}{"totalTime": 4}, 932 Root: WantTraceSegment{ 933 SegmentName: "ROOT", 934 RelativeStartMillis: 0, 935 RelativeStopMillis: 3000, 936 Attributes: map[string]interface{}{}, 937 Children: []WantTraceSegment{{ 938 SegmentName: "WebTransaction/Go/3", 939 RelativeStartMillis: 0, 940 RelativeStopMillis: 3000, 941 Attributes: map[string]interface{}{"exclusive_duration_millis": 3000}, 942 Children: []WantTraceSegment{ 943 { 944 SegmentName: "Custom/t1", 945 RelativeStartMillis: 1000, 946 RelativeStopMillis: 2000, 947 Attributes: map[string]interface{}{}, 948 Children: []WantTraceSegment{}, 949 }, 950 { 951 SegmentName: "Custom/t2", 952 RelativeStartMillis: 2000, 953 RelativeStopMillis: 4000, 954 Attributes: map[string]interface{}{"backtrace": MatchAnything}, 955 Children: []WantTraceSegment{}, 956 }, 957 { 958 SegmentName: "External/example.com/http", 959 RelativeStartMillis: 4000, 960 RelativeStopMillis: 6000, 961 Attributes: map[string]interface{}{ 962 "backtrace": MatchAnything, 963 "http.url": "http://example.com/zip/zap", 964 }, 965 Children: []WantTraceSegment{}, 966 }, 967 }, 968 }}, 969 }, 970 }, 971 }) 972 } 973 974 func TestTxnTraceSynthetics(t *testing.T) { 975 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 976 txndata := &TxnData{} 977 txndata.TxnTrace.Enabled = true 978 979 acfg := CreateAttributeConfig(sampleAttributeConfigInput, true) 980 attr := NewAttributes(acfg) 981 attr.Agent.Add(attributeRequestURI, "/url", nil) 982 ht := newHarvestTraces() 983 984 ht.Witness(HarvestTrace{ 985 TxnEvent: TxnEvent{ 986 Start: start, 987 Duration: 3 * time.Second, 988 TotalTime: 4 * time.Second, 989 FinalName: "WebTransaction/Go/3", 990 Attrs: attr, 991 CrossProcess: TxnCrossProcess{ 992 Type: txnCrossProcessSynthetics, 993 Synthetics: &cat.SyntheticsHeader{ 994 ResourceID: "resource", 995 }, 996 }, 997 }, 998 Trace: txndata.TxnTrace, 999 }) 1000 ht.Witness(HarvestTrace{ 1001 TxnEvent: TxnEvent{ 1002 Start: start, 1003 Duration: 5 * time.Second, 1004 TotalTime: 6 * time.Second, 1005 FinalName: "WebTransaction/Go/5", 1006 Attrs: attr, 1007 CrossProcess: TxnCrossProcess{ 1008 Type: txnCrossProcessSynthetics, 1009 Synthetics: &cat.SyntheticsHeader{ 1010 ResourceID: "resource", 1011 }, 1012 }, 1013 }, 1014 Trace: txndata.TxnTrace, 1015 }) 1016 ht.Witness(HarvestTrace{ 1017 TxnEvent: TxnEvent{ 1018 Start: start, 1019 Duration: 4 * time.Second, 1020 TotalTime: 5 * time.Second, 1021 FinalName: "WebTransaction/Go/4", 1022 Attrs: attr, 1023 CrossProcess: TxnCrossProcess{ 1024 Type: txnCrossProcessSynthetics, 1025 Synthetics: &cat.SyntheticsHeader{ 1026 ResourceID: "resource", 1027 }, 1028 }, 1029 }, 1030 Trace: txndata.TxnTrace, 1031 }) 1032 1033 ExpectTxnTraces(t, ht, []WantTxnTrace{ 1034 { 1035 MetricName: "WebTransaction/Go/3", 1036 UserAttributes: map[string]interface{}{}, 1037 AgentAttributes: map[string]interface{}{"request.uri": "/url"}, 1038 Intrinsics: map[string]interface{}{ 1039 "totalTime": 4, 1040 "synthetics_resource_id": "resource", 1041 }, 1042 Root: WantTraceSegment{ 1043 SegmentName: "ROOT", 1044 RelativeStartMillis: 0, 1045 RelativeStopMillis: 3000, 1046 Attributes: map[string]interface{}{}, 1047 Children: []WantTraceSegment{{ 1048 SegmentName: "WebTransaction/Go/3", 1049 RelativeStartMillis: 0, 1050 RelativeStopMillis: 3000, 1051 Attributes: map[string]interface{}{"exclusive_duration_millis": 3000}, 1052 Children: []WantTraceSegment{}, 1053 }}, 1054 }, 1055 }, 1056 { 1057 MetricName: "WebTransaction/Go/5", 1058 UserAttributes: map[string]interface{}{}, 1059 AgentAttributes: map[string]interface{}{"request.uri": "/url"}, 1060 Intrinsics: map[string]interface{}{ 1061 "totalTime": 6, 1062 "synthetics_resource_id": "resource", 1063 }, 1064 Root: WantTraceSegment{ 1065 SegmentName: "ROOT", 1066 RelativeStartMillis: 0, 1067 RelativeStopMillis: 5000, 1068 Attributes: map[string]interface{}{}, 1069 Children: []WantTraceSegment{{ 1070 SegmentName: "WebTransaction/Go/5", 1071 RelativeStartMillis: 0, 1072 RelativeStopMillis: 5000, 1073 Attributes: map[string]interface{}{"exclusive_duration_millis": 5000}, 1074 Children: []WantTraceSegment{}, 1075 }}, 1076 }, 1077 }, 1078 { 1079 MetricName: "WebTransaction/Go/4", 1080 UserAttributes: map[string]interface{}{}, 1081 AgentAttributes: map[string]interface{}{"request.uri": "/url"}, 1082 Intrinsics: map[string]interface{}{ 1083 "totalTime": 5, 1084 "synthetics_resource_id": "resource", 1085 }, 1086 Root: WantTraceSegment{ 1087 SegmentName: "ROOT", 1088 RelativeStartMillis: 0, 1089 RelativeStopMillis: 4000, 1090 Attributes: map[string]interface{}{}, 1091 Children: []WantTraceSegment{{ 1092 SegmentName: "WebTransaction/Go/4", 1093 RelativeStartMillis: 0, 1094 RelativeStopMillis: 4000, 1095 Attributes: map[string]interface{}{"exclusive_duration_millis": 4000}, 1096 Children: []WantTraceSegment{}, 1097 }}, 1098 }, 1099 }, 1100 }) 1101 } 1102 1103 func TestTraceJSON(t *testing.T) { 1104 // Have one test compare exact JSON to ensure that all misc fields (such 1105 // as the trailing `null,false,null,""`) are what we expect. 1106 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 1107 txndata := &TxnData{} 1108 txndata.TxnTrace.Enabled = true 1109 ht := newHarvestTraces() 1110 ht.Witness(HarvestTrace{ 1111 TxnEvent: TxnEvent{ 1112 Start: start, 1113 Duration: 3 * time.Second, 1114 TotalTime: 4 * time.Second, 1115 FinalName: "WebTransaction/Go/trace", 1116 Attrs: nil, 1117 }, 1118 Trace: txndata.TxnTrace, 1119 }) 1120 1121 expect := `[ 1122 "12345", 1123 [ 1124 [ 1125 1417136460000000, 1126 3000, 1127 "WebTransaction/Go/trace", 1128 null, 1129 [0,{},{}, 1130 [ 1131 0, 1132 3000, 1133 "ROOT", 1134 {}, 1135 [[0,3000,"WebTransaction/Go/trace",{"exclusive_duration_millis":3000},[]]] 1136 ], 1137 { 1138 "agentAttributes":{}, 1139 "userAttributes":{}, 1140 "intrinsics":{"totalTime":4} 1141 } 1142 ],"",null,false,null,"" 1143 ] 1144 ] 1145 ]` 1146 1147 js, err := ht.Data("12345", start) 1148 if nil != err { 1149 t.Fatal(err) 1150 } 1151 testExpectedJSON(t, expect, string(js)) 1152 } 1153 1154 func TestTraceCatGUID(t *testing.T) { 1155 // Test catGUID is properly set in outbound json when CAT is enabled 1156 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 1157 txndata := &TxnData{} 1158 txndata.TxnTrace.Enabled = true 1159 ht := newHarvestTraces() 1160 ht.Witness(HarvestTrace{ 1161 TxnEvent: TxnEvent{ 1162 Start: start, 1163 Duration: 3 * time.Second, 1164 TotalTime: 4 * time.Second, 1165 FinalName: "WebTransaction/Go/trace", 1166 Attrs: nil, 1167 CrossProcess: TxnCrossProcess{ 1168 Type: 1, 1169 GUID: "this is guid", 1170 }, 1171 }, 1172 Trace: txndata.TxnTrace, 1173 }) 1174 1175 expect := `[ 1176 "12345", 1177 [ 1178 [ 1179 1417136460000000, 1180 3000, 1181 "WebTransaction/Go/trace", 1182 null, 1183 [0,{},{}, 1184 [ 1185 0, 1186 3000, 1187 "ROOT", 1188 {}, 1189 [[0,3000,"WebTransaction/Go/trace",{"exclusive_duration_millis":3000},[]]] 1190 ], 1191 { 1192 "agentAttributes":{}, 1193 "userAttributes":{}, 1194 "intrinsics":{"totalTime":4} 1195 } 1196 ],"this is guid",null,false,null,"" 1197 ] 1198 ] 1199 ]` 1200 1201 js, err := ht.Data("12345", start) 1202 if nil != err { 1203 t.Fatal(err) 1204 } 1205 testExpectedJSON(t, expect, string(js)) 1206 } 1207 1208 func TestTraceDistributedTracingGUID(t *testing.T) { 1209 // Test catGUID is properly set in outbound json when DT is enabled 1210 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 1211 txndata := &TxnData{} 1212 txndata.TxnTrace.Enabled = true 1213 ht := newHarvestTraces() 1214 ht.Witness(HarvestTrace{ 1215 TxnEvent: TxnEvent{ 1216 Start: start, 1217 Duration: 3 * time.Second, 1218 TotalTime: 4 * time.Second, 1219 FinalName: "WebTransaction/Go/trace", 1220 Attrs: nil, 1221 BetterCAT: BetterCAT{ 1222 Enabled: true, 1223 ID: "this is guid", 1224 }, 1225 }, 1226 Trace: txndata.TxnTrace, 1227 }) 1228 1229 expect := `[ 1230 "12345", 1231 [ 1232 [ 1233 1417136460000000, 1234 3000, 1235 "WebTransaction/Go/trace", 1236 null, 1237 [0,{},{}, 1238 [ 1239 0, 1240 3000, 1241 "ROOT", 1242 {}, 1243 [[0,3000,"WebTransaction/Go/trace",{"exclusive_duration_millis":3000},[]]] 1244 ], 1245 { 1246 "agentAttributes":{}, 1247 "userAttributes":{}, 1248 "intrinsics":{ 1249 "totalTime":4, 1250 "guid":"this is guid", 1251 "traceId":"this is guid", 1252 "priority":0.000000, 1253 "sampled":false 1254 } 1255 } 1256 ],"this is guid",null,false,null,"" 1257 ] 1258 ] 1259 ]` 1260 1261 js, err := ht.Data("12345", start) 1262 if nil != err { 1263 t.Fatal(err) 1264 } 1265 testExpectedJSON(t, expect, string(js)) 1266 } 1267 1268 func BenchmarkWitnessNode(b *testing.B) { 1269 trace := &TxnTrace{ 1270 Enabled: true, 1271 SegmentThreshold: 0, // save all segments 1272 StackTraceThreshold: 1 * time.Hour, // no stack traces 1273 maxNodes: 100 * 1000, 1274 } 1275 1276 b.ResetTimer() 1277 b.ReportAllocs() 1278 1279 for i := 0; i < b.N; i++ { 1280 end := segmentEnd{ 1281 duration: time.Duration(RandUint32()) * time.Millisecond, 1282 exclusive: 0, 1283 } 1284 trace.witnessNode(end, "myNode", nil, "") 1285 } 1286 }