github.com/newrelic/go-agent@v3.26.0+incompatible/internal/tracing_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 "net/url" 9 "strconv" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/newrelic/go-agent/internal/cat" 15 "github.com/newrelic/go-agent/internal/crossagent" 16 "github.com/newrelic/go-agent/internal/logger" 17 ) 18 19 func TestStartEndSegment(t *testing.T) { 20 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 21 22 txndata := &TxnData{} 23 thread := &Thread{} 24 token := StartSegment(txndata, thread, start) 25 stop := start.Add(1 * time.Second) 26 end, err := endSegment(txndata, thread, token, stop) 27 if nil != err { 28 t.Error(err) 29 } 30 if end.exclusive != end.duration { 31 t.Error(end.exclusive, end.duration) 32 } 33 if end.duration != 1*time.Second { 34 t.Error(end.duration) 35 } 36 if end.start.Time != start { 37 t.Error(end.start, start) 38 } 39 if end.stop.Time != stop { 40 t.Error(end.stop, stop) 41 } 42 if 0 != len(txndata.spanEvents) { 43 t.Error(txndata.spanEvents) 44 } 45 } 46 47 func TestMultipleChildren(t *testing.T) { 48 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 49 txndata := &TxnData{} 50 thread := &Thread{} 51 52 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 53 t2 := StartSegment(txndata, thread, start.Add(2*time.Second)) 54 end2, err2 := endSegment(txndata, thread, t2, start.Add(3*time.Second)) 55 t3 := StartSegment(txndata, thread, start.Add(4*time.Second)) 56 end3, err3 := endSegment(txndata, thread, t3, start.Add(5*time.Second)) 57 end1, err1 := endSegment(txndata, thread, t1, start.Add(6*time.Second)) 58 t4 := StartSegment(txndata, thread, start.Add(7*time.Second)) 59 end4, err4 := endSegment(txndata, thread, t4, start.Add(8*time.Second)) 60 61 if nil != err1 || end1.duration != 5*time.Second || end1.exclusive != 3*time.Second { 62 t.Error(end1, err1) 63 } 64 if nil != err2 || end2.duration != end2.exclusive || end2.duration != time.Second { 65 t.Error(end2, err2) 66 } 67 if nil != err3 || end3.duration != end3.exclusive || end3.duration != time.Second { 68 t.Error(end3, err3) 69 } 70 if nil != err4 || end4.duration != end4.exclusive || end4.duration != time.Second { 71 t.Error(end4, err4) 72 } 73 if thread.TotalTime() != 7*time.Second { 74 t.Error(thread.TotalTime()) 75 } 76 } 77 78 func TestInvalidStart(t *testing.T) { 79 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 80 txndata := &TxnData{} 81 thread := &Thread{} 82 83 end, err := endSegment(txndata, thread, SegmentStartTime{}, start.Add(1*time.Second)) 84 if err != errMalformedSegment { 85 t.Error(end, err) 86 } 87 StartSegment(txndata, thread, start.Add(2*time.Second)) 88 end, err = endSegment(txndata, thread, SegmentStartTime{}, start.Add(3*time.Second)) 89 if err != errMalformedSegment { 90 t.Error(end, err) 91 } 92 } 93 94 func TestSegmentAlreadyEnded(t *testing.T) { 95 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 96 txndata := &TxnData{} 97 thread := &Thread{} 98 99 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 100 end, err := endSegment(txndata, thread, t1, start.Add(2*time.Second)) 101 if err != nil { 102 t.Error(end, err) 103 } 104 end, err = endSegment(txndata, thread, t1, start.Add(3*time.Second)) 105 if err != errSegmentOrder { 106 t.Error(end, err) 107 } 108 } 109 110 func TestSegmentBadStamp(t *testing.T) { 111 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 112 txndata := &TxnData{} 113 thread := &Thread{} 114 115 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 116 t1.Stamp++ 117 end, err := endSegment(txndata, thread, t1, start.Add(2*time.Second)) 118 if err != errSegmentOrder { 119 t.Error(end, err) 120 } 121 } 122 123 func TestSegmentBadDepth(t *testing.T) { 124 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 125 txndata := &TxnData{} 126 thread := &Thread{} 127 128 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 129 t1.Depth++ 130 end, err := endSegment(txndata, thread, t1, start.Add(2*time.Second)) 131 if err != errSegmentOrder { 132 t.Error(end, err) 133 } 134 } 135 136 func TestSegmentNegativeDepth(t *testing.T) { 137 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 138 txndata := &TxnData{} 139 thread := &Thread{} 140 141 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 142 t1.Depth = -1 143 end, err := endSegment(txndata, thread, t1, start.Add(2*time.Second)) 144 if err != errMalformedSegment { 145 t.Error(end, err) 146 } 147 } 148 149 func TestSegmentOutOfOrder(t *testing.T) { 150 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 151 txndata := &TxnData{} 152 thread := &Thread{} 153 154 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 155 t2 := StartSegment(txndata, thread, start.Add(2*time.Second)) 156 t3 := StartSegment(txndata, thread, start.Add(3*time.Second)) 157 end2, err2 := endSegment(txndata, thread, t2, start.Add(4*time.Second)) 158 end3, err3 := endSegment(txndata, thread, t3, start.Add(5*time.Second)) 159 t4 := StartSegment(txndata, thread, start.Add(6*time.Second)) 160 end4, err4 := endSegment(txndata, thread, t4, start.Add(7*time.Second)) 161 end1, err1 := endSegment(txndata, thread, t1, start.Add(8*time.Second)) 162 163 if nil != err1 || 164 end1.duration != 7*time.Second || 165 end1.exclusive != 4*time.Second { 166 t.Error(end1, err1) 167 } 168 if nil != err2 || end2.duration != end2.exclusive || end2.duration != 2*time.Second { 169 t.Error(end2, err2) 170 } 171 if err3 != errSegmentOrder { 172 t.Error(end3, err3) 173 } 174 if nil != err4 || end4.duration != end4.exclusive || end4.duration != 1*time.Second { 175 t.Error(end4, err4) 176 } 177 } 178 179 // |-t3-| |-t4-| 180 // |-t2-| |-never-finished---------- 181 // |-t1-| |--never-finished------------------------ 182 // |-------alpha------------------------------------------| 183 // 0 1 2 3 4 5 6 7 8 9 10 11 12 184 func TestLostChildren(t *testing.T) { 185 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 186 txndata := &TxnData{} 187 thread := &Thread{} 188 189 alpha := StartSegment(txndata, thread, start.Add(1*time.Second)) 190 t1 := StartSegment(txndata, thread, start.Add(2*time.Second)) 191 EndBasicSegment(txndata, thread, t1, start.Add(3*time.Second), "t1") 192 StartSegment(txndata, thread, start.Add(4*time.Second)) 193 t2 := StartSegment(txndata, thread, start.Add(5*time.Second)) 194 EndBasicSegment(txndata, thread, t2, start.Add(6*time.Second), "t2") 195 StartSegment(txndata, thread, start.Add(7*time.Second)) 196 t3 := StartSegment(txndata, thread, start.Add(8*time.Second)) 197 EndBasicSegment(txndata, thread, t3, start.Add(9*time.Second), "t3") 198 t4 := StartSegment(txndata, thread, start.Add(10*time.Second)) 199 EndBasicSegment(txndata, thread, t4, start.Add(11*time.Second), "t4") 200 EndBasicSegment(txndata, thread, alpha, start.Add(12*time.Second), "alpha") 201 202 metrics := newMetricTable(100, time.Now()) 203 txndata.FinalName = "WebTransaction/Go/zip" 204 txndata.IsWeb = true 205 MergeBreakdownMetrics(txndata, metrics) 206 ExpectMetrics(t, metrics, []WantMetric{ 207 {"Custom/alpha", "", false, []float64{1, 11, 7, 11, 11, 121}}, 208 {"Custom/t1", "", false, []float64{1, 1, 1, 1, 1, 1}}, 209 {"Custom/t2", "", false, []float64{1, 1, 1, 1, 1, 1}}, 210 {"Custom/t3", "", false, []float64{1, 1, 1, 1, 1, 1}}, 211 {"Custom/t4", "", false, []float64{1, 1, 1, 1, 1, 1}}, 212 {"Custom/alpha", txndata.FinalName, false, []float64{1, 11, 7, 11, 11, 121}}, 213 {"Custom/t1", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 214 {"Custom/t2", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 215 {"Custom/t3", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 216 {"Custom/t4", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 217 }) 218 } 219 220 // |-t3-| |-t4-| 221 // |-t2-| |-never-finished---------- 222 // |-t1-| |--never-finished------------------------ 223 // |-------root------------------------------------------------- 224 // 0 1 2 3 4 5 6 7 8 9 10 11 12 225 func TestLostChildrenRoot(t *testing.T) { 226 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 227 txndata := &TxnData{} 228 thread := &Thread{} 229 230 t1 := StartSegment(txndata, thread, start.Add(2*time.Second)) 231 EndBasicSegment(txndata, thread, t1, start.Add(3*time.Second), "t1") 232 StartSegment(txndata, thread, start.Add(4*time.Second)) 233 t2 := StartSegment(txndata, thread, start.Add(5*time.Second)) 234 EndBasicSegment(txndata, thread, t2, start.Add(6*time.Second), "t2") 235 StartSegment(txndata, thread, start.Add(7*time.Second)) 236 t3 := StartSegment(txndata, thread, start.Add(8*time.Second)) 237 EndBasicSegment(txndata, thread, t3, start.Add(9*time.Second), "t3") 238 t4 := StartSegment(txndata, thread, start.Add(10*time.Second)) 239 EndBasicSegment(txndata, thread, t4, start.Add(11*time.Second), "t4") 240 241 if thread.TotalTime() != 9*time.Second { 242 t.Error(thread.TotalTime()) 243 } 244 245 metrics := newMetricTable(100, time.Now()) 246 txndata.FinalName = "WebTransaction/Go/zip" 247 txndata.IsWeb = true 248 MergeBreakdownMetrics(txndata, metrics) 249 ExpectMetrics(t, metrics, []WantMetric{ 250 {"Custom/t1", "", false, []float64{1, 1, 1, 1, 1, 1}}, 251 {"Custom/t2", "", false, []float64{1, 1, 1, 1, 1, 1}}, 252 {"Custom/t3", "", false, []float64{1, 1, 1, 1, 1, 1}}, 253 {"Custom/t4", "", false, []float64{1, 1, 1, 1, 1, 1}}, 254 {"Custom/t1", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 255 {"Custom/t2", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 256 {"Custom/t3", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 257 {"Custom/t4", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 258 }) 259 } 260 261 func TestNilSpanEvent(t *testing.T) { 262 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 263 264 txndata := &TxnData{} 265 thread := &Thread{} 266 token := StartSegment(txndata, thread, start) 267 stop := start.Add(1 * time.Second) 268 end, err := endSegment(txndata, thread, token, stop) 269 if nil != err { 270 t.Error(err) 271 } 272 273 // A segment without a SpanId does not create a spanEvent. 274 if evt := end.spanEvent(); evt != nil { 275 t.Error(evt) 276 } 277 } 278 279 func TestDefaultSpanEvent(t *testing.T) { 280 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 281 282 txndata := &TxnData{} 283 thread := &Thread{} 284 token := StartSegment(txndata, thread, start) 285 stop := start.Add(1 * time.Second) 286 end, err := endSegment(txndata, thread, token, stop) 287 if nil != err { 288 t.Error(err) 289 } 290 end.SpanID = "123" 291 if evt := end.spanEvent(); evt != nil { 292 if evt.GUID != end.SpanID || 293 evt.ParentID != end.ParentID || 294 evt.Timestamp != end.start.Time || 295 evt.Duration != end.duration || 296 evt.IsEntrypoint { 297 t.Error(evt) 298 } 299 } 300 } 301 302 func TestGetRootSpanID(t *testing.T) { 303 txndata := &TxnData{ 304 TraceIDGenerator: NewTraceIDGenerator(12345), 305 } 306 if id := txndata.getRootSpanID(); id != "d9466896a525ccbf" { 307 t.Error(id) 308 } 309 if id := txndata.getRootSpanID(); id != "d9466896a525ccbf" { 310 t.Error(id) 311 } 312 } 313 314 func TestCurrentSpanIdentifier(t *testing.T) { 315 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 316 txndata := &TxnData{ 317 TraceIDGenerator: NewTraceIDGenerator(12345), 318 } 319 thread := &Thread{} 320 id := txndata.CurrentSpanIdentifier(thread) 321 if id != "d9466896a525ccbf" { 322 t.Error(id) 323 } 324 325 // After starting and ending a segment, the current span id is still the root. 326 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 327 _, err1 := endSegment(txndata, thread, t1, start.Add(3*time.Second)) 328 if nil != err1 { 329 t.Error(err1) 330 } 331 332 id = txndata.CurrentSpanIdentifier(thread) 333 if id != "d9466896a525ccbf" { 334 t.Error(id) 335 } 336 337 // After starting a new segment, there should be a new current span id. 338 StartSegment(txndata, thread, start.Add(2*time.Second)) 339 id2 := txndata.CurrentSpanIdentifier(thread) 340 if id2 != "bcfb32e050b264b8" { 341 t.Error(id2) 342 } 343 } 344 345 func TestDatastoreSpanAddress(t *testing.T) { 346 if s := datastoreSpanAddress("host", "portPathOrID"); s != "host:portPathOrID" { 347 t.Error(s) 348 } 349 if s := datastoreSpanAddress("host", ""); s != "host" { 350 t.Error(s) 351 } 352 if s := datastoreSpanAddress("", ""); s != "" { 353 t.Error(s) 354 } 355 } 356 357 func TestSegmentBasic(t *testing.T) { 358 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 359 txndata := &TxnData{} 360 thread := &Thread{} 361 362 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 363 t2 := StartSegment(txndata, thread, start.Add(2*time.Second)) 364 EndBasicSegment(txndata, thread, t2, start.Add(3*time.Second), "t2") 365 EndBasicSegment(txndata, thread, t1, start.Add(4*time.Second), "t1") 366 t3 := StartSegment(txndata, thread, start.Add(5*time.Second)) 367 t4 := StartSegment(txndata, thread, start.Add(6*time.Second)) 368 EndBasicSegment(txndata, thread, t3, start.Add(7*time.Second), "t3") 369 EndBasicSegment(txndata, thread, t4, start.Add(8*time.Second), "out-of-order") 370 t5 := StartSegment(txndata, thread, start.Add(9*time.Second)) 371 EndBasicSegment(txndata, thread, t5, start.Add(10*time.Second), "t1") 372 373 metrics := newMetricTable(100, time.Now()) 374 txndata.FinalName = "WebTransaction/Go/zip" 375 txndata.IsWeb = true 376 MergeBreakdownMetrics(txndata, metrics) 377 ExpectMetrics(t, metrics, []WantMetric{ 378 {"Custom/t1", "", false, []float64{2, 4, 3, 1, 3, 10}}, 379 {"Custom/t2", "", false, []float64{1, 1, 1, 1, 1, 1}}, 380 {"Custom/t3", "", false, []float64{1, 2, 2, 2, 2, 4}}, 381 {"Custom/t1", txndata.FinalName, false, []float64{2, 4, 3, 1, 3, 10}}, 382 {"Custom/t2", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 383 {"Custom/t3", txndata.FinalName, false, []float64{1, 2, 2, 2, 2, 4}}, 384 }) 385 } 386 387 func parseURL(raw string) *url.URL { 388 u, _ := url.Parse(raw) 389 return u 390 } 391 392 func TestSegmentExternal(t *testing.T) { 393 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 394 txndata := &TxnData{} 395 thread := &Thread{} 396 397 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 398 t2 := StartSegment(txndata, thread, start.Add(2*time.Second)) 399 EndExternalSegment(EndExternalParams{ 400 TxnData: txndata, 401 Thread: thread, 402 Start: t2, 403 Now: start.Add(3 * time.Second), 404 Logger: logger.ShimLogger{}, 405 }) 406 EndExternalSegment(EndExternalParams{ 407 TxnData: txndata, 408 Thread: thread, 409 Start: t1, 410 Now: start.Add(4 * time.Second), 411 URL: parseURL("http://f1.com"), 412 Host: "f1", 413 Logger: logger.ShimLogger{}, 414 }) 415 t3 := StartSegment(txndata, thread, start.Add(5*time.Second)) 416 EndExternalSegment(EndExternalParams{ 417 TxnData: txndata, 418 Thread: thread, 419 Start: t3, 420 Now: start.Add(6 * time.Second), 421 URL: parseURL("http://f1.com"), 422 Host: "f1", 423 Logger: logger.ShimLogger{}, 424 }) 425 t4 := StartSegment(txndata, thread, start.Add(7*time.Second)) 426 t4.Stamp++ 427 EndExternalSegment(EndExternalParams{ 428 TxnData: txndata, 429 Thread: thread, 430 Start: t4, 431 Now: start.Add(8 * time.Second), 432 URL: parseURL("http://invalid-token.com"), 433 Host: "invalid-token.com", 434 Logger: logger.ShimLogger{}, 435 }) 436 if txndata.externalCallCount != 3 { 437 t.Error(txndata.externalCallCount) 438 } 439 if txndata.externalDuration != 5*time.Second { 440 t.Error(txndata.externalDuration) 441 } 442 metrics := newMetricTable(100, time.Now()) 443 txndata.FinalName = "WebTransaction/Go/zip" 444 txndata.IsWeb = true 445 MergeBreakdownMetrics(txndata, metrics) 446 ExpectMetrics(t, metrics, []WantMetric{ 447 {"External/all", "", true, []float64{3, 5, 4, 1, 3, 11}}, 448 {"External/allWeb", "", true, []float64{3, 5, 4, 1, 3, 11}}, 449 {"External/f1/all", "", false, []float64{2, 4, 3, 1, 3, 10}}, 450 {"External/unknown/all", "", false, []float64{1, 1, 1, 1, 1, 1}}, 451 {"External/f1/http", txndata.FinalName, false, []float64{2, 4, 3, 1, 3, 10}}, 452 {"External/unknown/http", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 453 }) 454 455 metrics = newMetricTable(100, time.Now()) 456 txndata.FinalName = "OtherTransaction/Go/zip" 457 txndata.IsWeb = false 458 MergeBreakdownMetrics(txndata, metrics) 459 ExpectMetrics(t, metrics, []WantMetric{ 460 {"External/all", "", true, []float64{3, 5, 4, 1, 3, 11}}, 461 {"External/allOther", "", true, []float64{3, 5, 4, 1, 3, 11}}, 462 {"External/f1/all", "", false, []float64{2, 4, 3, 1, 3, 10}}, 463 {"External/unknown/all", "", false, []float64{1, 1, 1, 1, 1, 1}}, 464 {"External/f1/http", txndata.FinalName, false, []float64{2, 4, 3, 1, 3, 10}}, 465 {"External/unknown/http", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 466 }) 467 } 468 469 func TestSegmentDatastore(t *testing.T) { 470 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 471 txndata := &TxnData{} 472 thread := &Thread{} 473 474 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 475 t2 := StartSegment(txndata, thread, start.Add(2*time.Second)) 476 EndDatastoreSegment(EndDatastoreParams{ 477 TxnData: txndata, 478 Thread: thread, 479 Start: t2, 480 Now: start.Add(3 * time.Second), 481 Product: "MySQL", 482 Operation: "SELECT", 483 Collection: "my_table", 484 }) 485 EndDatastoreSegment(EndDatastoreParams{ 486 TxnData: txndata, 487 Thread: thread, 488 Start: t1, 489 Now: start.Add(4 * time.Second), 490 Product: "MySQL", 491 Operation: "SELECT", 492 // missing collection 493 }) 494 t3 := StartSegment(txndata, thread, start.Add(5*time.Second)) 495 EndDatastoreSegment(EndDatastoreParams{ 496 TxnData: txndata, 497 Thread: thread, 498 Start: t3, 499 Now: start.Add(6 * time.Second), 500 Product: "MySQL", 501 Operation: "SELECT", 502 // missing collection 503 }) 504 t4 := StartSegment(txndata, thread, start.Add(7*time.Second)) 505 t4.Stamp++ 506 EndDatastoreSegment(EndDatastoreParams{ 507 TxnData: txndata, 508 Thread: thread, 509 Start: t4, 510 Now: start.Add(8 * time.Second), 511 Product: "MySQL", 512 Operation: "invalid-token", 513 }) 514 t5 := StartSegment(txndata, thread, start.Add(9*time.Second)) 515 EndDatastoreSegment(EndDatastoreParams{ 516 TxnData: txndata, 517 Thread: thread, 518 Start: t5, 519 Now: start.Add(10 * time.Second), 520 // missing datastore, collection, and operation 521 }) 522 523 if txndata.datastoreCallCount != 4 { 524 t.Error(txndata.datastoreCallCount) 525 } 526 if txndata.datastoreDuration != 6*time.Second { 527 t.Error(txndata.datastoreDuration) 528 } 529 metrics := newMetricTable(100, time.Now()) 530 txndata.FinalName = "WebTransaction/Go/zip" 531 txndata.IsWeb = true 532 MergeBreakdownMetrics(txndata, metrics) 533 ExpectMetrics(t, metrics, []WantMetric{ 534 {"Datastore/all", "", true, []float64{4, 6, 5, 1, 3, 12}}, 535 {"Datastore/allWeb", "", true, []float64{4, 6, 5, 1, 3, 12}}, 536 {"Datastore/MySQL/all", "", true, []float64{3, 5, 4, 1, 3, 11}}, 537 {"Datastore/MySQL/allWeb", "", true, []float64{3, 5, 4, 1, 3, 11}}, 538 {"Datastore/Unknown/all", "", true, []float64{1, 1, 1, 1, 1, 1}}, 539 {"Datastore/Unknown/allWeb", "", true, []float64{1, 1, 1, 1, 1, 1}}, 540 {"Datastore/operation/MySQL/SELECT", "", false, []float64{3, 5, 4, 1, 3, 11}}, 541 {"Datastore/operation/MySQL/SELECT", txndata.FinalName, false, []float64{2, 4, 3, 1, 3, 10}}, 542 {"Datastore/operation/Unknown/other", "", false, []float64{1, 1, 1, 1, 1, 1}}, 543 {"Datastore/operation/Unknown/other", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 544 {"Datastore/statement/MySQL/my_table/SELECT", "", false, []float64{1, 1, 1, 1, 1, 1}}, 545 {"Datastore/statement/MySQL/my_table/SELECT", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 546 }) 547 548 metrics = newMetricTable(100, time.Now()) 549 txndata.FinalName = "OtherTransaction/Go/zip" 550 txndata.IsWeb = false 551 MergeBreakdownMetrics(txndata, metrics) 552 ExpectMetrics(t, metrics, []WantMetric{ 553 {"Datastore/all", "", true, []float64{4, 6, 5, 1, 3, 12}}, 554 {"Datastore/allOther", "", true, []float64{4, 6, 5, 1, 3, 12}}, 555 {"Datastore/MySQL/all", "", true, []float64{3, 5, 4, 1, 3, 11}}, 556 {"Datastore/MySQL/allOther", "", true, []float64{3, 5, 4, 1, 3, 11}}, 557 {"Datastore/Unknown/all", "", true, []float64{1, 1, 1, 1, 1, 1}}, 558 {"Datastore/Unknown/allOther", "", true, []float64{1, 1, 1, 1, 1, 1}}, 559 {"Datastore/operation/MySQL/SELECT", "", false, []float64{3, 5, 4, 1, 3, 11}}, 560 {"Datastore/operation/MySQL/SELECT", txndata.FinalName, false, []float64{2, 4, 3, 1, 3, 10}}, 561 {"Datastore/operation/Unknown/other", "", false, []float64{1, 1, 1, 1, 1, 1}}, 562 {"Datastore/operation/Unknown/other", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 563 {"Datastore/statement/MySQL/my_table/SELECT", "", false, []float64{1, 1, 1, 1, 1, 1}}, 564 {"Datastore/statement/MySQL/my_table/SELECT", txndata.FinalName, false, []float64{1, 1, 1, 1, 1, 1}}, 565 }) 566 } 567 568 func TestDatastoreInstancesCrossAgent(t *testing.T) { 569 var testcases []struct { 570 Name string `json:"name"` 571 SystemHostname string `json:"system_hostname"` 572 DBHostname string `json:"db_hostname"` 573 Product string `json:"product"` 574 Port int `json:"port"` 575 Socket string `json:"unix_socket"` 576 DatabasePath string `json:"database_path"` 577 ExpectedMetric string `json:"expected_instance_metric"` 578 } 579 580 err := crossagent.ReadJSON("datastores/datastore_instances.json", &testcases) 581 if err != nil { 582 t.Fatal(err) 583 } 584 585 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 586 587 for _, tc := range testcases { 588 portPathOrID := "" 589 if 0 != tc.Port { 590 portPathOrID = strconv.Itoa(tc.Port) 591 } else if "" != tc.Socket { 592 portPathOrID = tc.Socket 593 } else if "" != tc.DatabasePath { 594 portPathOrID = tc.DatabasePath 595 // These tests makes weird assumptions. 596 tc.DBHostname = "localhost" 597 } 598 599 txndata := &TxnData{} 600 thread := &Thread{} 601 602 s := StartSegment(txndata, thread, start) 603 EndDatastoreSegment(EndDatastoreParams{ 604 Thread: thread, 605 TxnData: txndata, 606 Start: s, 607 Now: start.Add(1 * time.Second), 608 Product: tc.Product, 609 Operation: "SELECT", 610 Collection: "my_table", 611 PortPathOrID: portPathOrID, 612 Host: tc.DBHostname, 613 }) 614 615 expect := strings.Replace(tc.ExpectedMetric, 616 tc.SystemHostname, ThisHost, -1) 617 618 metrics := newMetricTable(100, time.Now()) 619 txndata.FinalName = "OtherTransaction/Go/zip" 620 txndata.IsWeb = false 621 MergeBreakdownMetrics(txndata, metrics) 622 data := []float64{1, 1, 1, 1, 1, 1} 623 ExpectMetrics(ExtendValidator(t, tc.Name), metrics, []WantMetric{ 624 {"Datastore/all", "", true, data}, 625 {"Datastore/allOther", "", true, data}, 626 {"Datastore/" + tc.Product + "/all", "", true, data}, 627 {"Datastore/" + tc.Product + "/allOther", "", true, data}, 628 {"Datastore/operation/" + tc.Product + "/SELECT", "", false, data}, 629 {"Datastore/statement/" + tc.Product + "/my_table/SELECT", "", false, data}, 630 {"Datastore/statement/" + tc.Product + "/my_table/SELECT", txndata.FinalName, false, data}, 631 {expect, "", false, data}, 632 }) 633 } 634 } 635 636 func TestGenericSpanEventCreation(t *testing.T) { 637 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 638 txndata := &TxnData{ 639 TraceIDGenerator: NewTraceIDGenerator(12345), 640 } 641 thread := &Thread{} 642 643 // Enable that which is necessary to generate span events when segments are ended. 644 txndata.LazilyCalculateSampled = func() bool { return true } 645 txndata.SpanEventsEnabled = true 646 647 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 648 EndBasicSegment(txndata, thread, t1, start.Add(3*time.Second), "t1") 649 650 // Since a basic segment has just ended, there should be exactly one generic span event in txndata.spanEvents[] 651 if 1 != len(txndata.spanEvents) { 652 t.Error(txndata.spanEvents) 653 } 654 if txndata.spanEvents[0].Category != spanCategoryGeneric { 655 t.Error(txndata.spanEvents[0].Category) 656 } 657 } 658 659 func TestSpanEventNotSampled(t *testing.T) { 660 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 661 txndata := &TxnData{ 662 TraceIDGenerator: NewTraceIDGenerator(12345), 663 } 664 thread := &Thread{} 665 666 txndata.LazilyCalculateSampled = func() bool { return false } 667 txndata.SpanEventsEnabled = true 668 669 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 670 EndBasicSegment(txndata, thread, t1, start.Add(3*time.Second), "t1") 671 672 if 0 != len(txndata.spanEvents) { 673 t.Error(txndata.spanEvents) 674 } 675 } 676 677 func TestSpanEventNotEnabled(t *testing.T) { 678 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 679 txndata := &TxnData{ 680 TraceIDGenerator: NewTraceIDGenerator(12345), 681 } 682 thread := &Thread{} 683 684 txndata.LazilyCalculateSampled = func() bool { return true } 685 txndata.SpanEventsEnabled = false 686 687 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 688 EndBasicSegment(txndata, thread, t1, start.Add(3*time.Second), "t1") 689 690 if 0 != len(txndata.spanEvents) { 691 t.Error(txndata.spanEvents) 692 } 693 } 694 695 func TestDatastoreSpanEventCreation(t *testing.T) { 696 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 697 txndata := &TxnData{ 698 TraceIDGenerator: NewTraceIDGenerator(12345), 699 } 700 thread := &Thread{} 701 702 // Enable that which is necessary to generate span events when segments are ended. 703 txndata.LazilyCalculateSampled = func() bool { return true } 704 txndata.SpanEventsEnabled = true 705 706 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 707 EndDatastoreSegment(EndDatastoreParams{ 708 TxnData: txndata, 709 Thread: thread, 710 Start: t1, 711 Now: start.Add(3 * time.Second), 712 Product: "MySQL", 713 Operation: "SELECT", 714 Collection: "my_table", 715 }) 716 717 // Since a datastore segment has just ended, there should be exactly one datastore span event in txndata.spanEvents[] 718 if 1 != len(txndata.spanEvents) { 719 t.Error(txndata.spanEvents) 720 } 721 if txndata.spanEvents[0].Category != spanCategoryDatastore { 722 t.Error(txndata.spanEvents[0].Category) 723 } 724 } 725 726 func TestHTTPSpanEventCreation(t *testing.T) { 727 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 728 txndata := &TxnData{ 729 TraceIDGenerator: NewTraceIDGenerator(12345), 730 } 731 thread := &Thread{} 732 733 // Enable that which is necessary to generate span events when segments are ended. 734 txndata.LazilyCalculateSampled = func() bool { return true } 735 txndata.SpanEventsEnabled = true 736 737 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 738 EndExternalSegment(EndExternalParams{ 739 TxnData: txndata, 740 Thread: thread, 741 Start: t1, 742 Now: start.Add(3 * time.Second), 743 URL: nil, 744 Logger: logger.ShimLogger{}, 745 }) 746 747 // Since an external segment has just ended, there should be exactly one HTTP span event in txndata.spanEvents[] 748 if 1 != len(txndata.spanEvents) { 749 t.Error(txndata.spanEvents) 750 } 751 if txndata.spanEvents[0].Category != spanCategoryHTTP { 752 t.Error(txndata.spanEvents[0].Category) 753 } 754 } 755 756 func TestExternalSegmentCAT(t *testing.T) { 757 // Test that when the reading the response CAT headers fails, an external 758 // segment is still created. 759 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 760 txndata := &TxnData{ 761 TraceIDGenerator: NewTraceIDGenerator(12345), 762 } 763 txndata.CrossProcess.Enabled = true 764 thread := &Thread{} 765 766 resp := &http.Response{Header: http.Header{}} 767 resp.Header.Add(cat.NewRelicAppDataName, "bad header value") 768 769 t1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 770 err := EndExternalSegment(EndExternalParams{ 771 TxnData: txndata, 772 Thread: thread, 773 Start: t1, 774 Now: start.Add(4 * time.Second), 775 URL: parseURL("http://f1.com"), 776 Logger: logger.ShimLogger{}, 777 }) 778 779 if nil != err { 780 t.Error("EndExternalSegment returned an err:", err) 781 } 782 if txndata.externalCallCount != 1 { 783 t.Error(txndata.externalCallCount) 784 } 785 if txndata.externalDuration != 3*time.Second { 786 t.Error(txndata.externalDuration) 787 } 788 789 metrics := newMetricTable(100, time.Now()) 790 txndata.FinalName = "OtherTransaction/Go/zip" 791 txndata.IsWeb = false 792 MergeBreakdownMetrics(txndata, metrics) 793 ExpectMetrics(t, metrics, []WantMetric{ 794 {"External/all", "", true, []float64{1, 3, 3, 3, 3, 9}}, 795 {"External/allOther", "", true, []float64{1, 3, 3, 3, 3, 9}}, 796 {"External/f1.com/all", "", false, []float64{1, 3, 3, 3, 3, 9}}, 797 {"External/f1.com/http", txndata.FinalName, false, []float64{1, 3, 3, 3, 3, 9}}, 798 }) 799 } 800 801 func TestEndMessageSegment(t *testing.T) { 802 start := time.Date(2014, time.November, 28, 1, 1, 0, 0, time.UTC) 803 txndata := &TxnData{ 804 TraceIDGenerator: NewTraceIDGenerator(12345), 805 } 806 txndata.CrossProcess.Enabled = true 807 txndata.LazilyCalculateSampled = func() bool { return true } 808 txndata.SpanEventsEnabled = true 809 thread := &Thread{} 810 811 seg1 := StartSegment(txndata, thread, start.Add(1*time.Second)) 812 seg2 := StartSegment(txndata, thread, start.Add(2*time.Second)) 813 EndMessageSegment(EndMessageParams{ 814 TxnData: txndata, 815 Thread: thread, 816 Start: seg1, 817 Now: start.Add(3 * time.Second), 818 Logger: nil, 819 DestinationName: "MyTopic", 820 Library: "Kafka", 821 DestinationType: "Topic", 822 }) 823 EndMessageSegment(EndMessageParams{ 824 TxnData: txndata, 825 Thread: thread, 826 Start: seg2, 827 Now: start.Add(4 * time.Second), 828 Logger: nil, 829 DestinationName: "MyOtherTopic", 830 Library: "Kafka", 831 DestinationType: "Topic", 832 }) 833 834 metrics := newMetricTable(100, time.Now()) 835 txndata.FinalName = "WebTransaction/Go/zip" 836 txndata.IsWeb = true 837 MergeBreakdownMetrics(txndata, metrics) 838 ExpectMetrics(t, metrics, []WantMetric{ 839 {"MessageBroker/Kafka/Topic/Produce/Named/MyTopic", "WebTransaction/Go/zip", false, []float64{1, 2, 2, 2, 2, 4}}, 840 {"MessageBroker/Kafka/Topic/Produce/Named/MyTopic", "", false, []float64{1, 2, 2, 2, 2, 4}}, 841 }) 842 }