github.com/newrelic/go-agent@v3.26.0+incompatible/internal_span_events_test.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package newrelic 5 6 import ( 7 "net/http" 8 "testing" 9 10 "github.com/newrelic/go-agent/internal" 11 ) 12 13 func TestSpanEventSuccess(t *testing.T) { 14 // Test that a basic segment creates a span event, and that a 15 // transaction has a root span event. 16 replyfn := func(reply *internal.ConnectReply) { 17 reply.AdaptiveSampler = internal.SampleEverything{} 18 reply.TraceIDGenerator = internal.NewTraceIDGenerator(12345) 19 } 20 cfgfn := func(cfg *Config) { 21 cfg.DistributedTracer.Enabled = true 22 } 23 app := testApp(replyfn, cfgfn, t) 24 txn := app.StartTransaction("hello", nil, nil) 25 segment := StartSegment(txn, "mySegment") 26 segment.End() 27 txn.End() 28 app.ExpectSpanEvents(t, []internal.WantEvent{ 29 { 30 Intrinsics: map[string]interface{}{ 31 "name": "OtherTransaction/Go/hello", 32 "sampled": true, 33 "category": "generic", 34 "priority": internal.MatchAnything, 35 "guid": "0e97aeb2f79d5d27", 36 "transactionId": "d9466896a525ccbf", 37 "nr.entryPoint": true, 38 "traceId": "d9466896a525ccbf", 39 }, 40 UserAttributes: map[string]interface{}{}, 41 AgentAttributes: map[string]interface{}{}, 42 }, 43 { 44 Intrinsics: map[string]interface{}{ 45 "name": "Custom/mySegment", 46 "sampled": true, 47 "category": "generic", 48 "priority": internal.MatchAnything, 49 "guid": "bcfb32e050b264b8", 50 "transactionId": "d9466896a525ccbf", 51 "traceId": "d9466896a525ccbf", 52 "parentId": "0e97aeb2f79d5d27", 53 }, 54 UserAttributes: map[string]interface{}{}, 55 AgentAttributes: map[string]interface{}{}, 56 }, 57 }) 58 } 59 60 func TestSpanEventsLocallyDisabled(t *testing.T) { 61 // Test that span events do not get created if Config.SpanEvents.Enabled 62 // is false. 63 replyfn := func(reply *internal.ConnectReply) { 64 reply.AdaptiveSampler = internal.SampleEverything{} 65 } 66 cfgfn := func(cfg *Config) { 67 cfg.DistributedTracer.Enabled = true 68 cfg.SpanEvents.Enabled = false 69 } 70 app := testApp(replyfn, cfgfn, t) 71 txn := app.StartTransaction("hello", nil, nil) 72 segment := StartSegment(txn, "mySegment") 73 segment.End() 74 txn.End() 75 app.ExpectSpanEvents(t, []internal.WantEvent{}) 76 } 77 78 func TestSpanEventsRemotelyDisabled(t *testing.T) { 79 // Test that span events do not get created if the connect reply 80 // disables span events. 81 replyfn := func(reply *internal.ConnectReply) { 82 reply.AdaptiveSampler = internal.SampleEverything{} 83 reply.CollectSpanEvents = false 84 } 85 cfgfn := func(cfg *Config) { 86 cfg.DistributedTracer.Enabled = true 87 } 88 app := testApp(replyfn, cfgfn, t) 89 txn := app.StartTransaction("hello", nil, nil) 90 segment := StartSegment(txn, "mySegment") 91 segment.End() 92 txn.End() 93 app.ExpectSpanEvents(t, []internal.WantEvent{}) 94 } 95 96 func TestSpanEventsDisabledWithoutDistributedTracing(t *testing.T) { 97 // Test that span events do not get created distributed tracing is not 98 // enabled. 99 replyfn := func(reply *internal.ConnectReply) { 100 reply.AdaptiveSampler = internal.SampleEverything{} 101 } 102 cfgfn := func(cfg *Config) { 103 cfg.DistributedTracer.Enabled = false 104 } 105 app := testApp(replyfn, cfgfn, t) 106 txn := app.StartTransaction("hello", nil, nil) 107 segment := StartSegment(txn, "mySegment") 108 segment.End() 109 txn.End() 110 app.ExpectSpanEvents(t, []internal.WantEvent{}) 111 } 112 113 func TestSpanEventDatastoreExternal(t *testing.T) { 114 // Test that a datastore and external segments creates the correct span 115 // events. 116 replyfn := func(reply *internal.ConnectReply) { 117 reply.AdaptiveSampler = internal.SampleEverything{} 118 } 119 cfgfn := func(cfg *Config) { 120 cfg.DistributedTracer.Enabled = true 121 } 122 app := testApp(replyfn, cfgfn, t) 123 txn := app.StartTransaction("hello", nil, nil) 124 segment := DatastoreSegment{ 125 StartTime: StartSegmentNow(txn), 126 Product: DatastoreMySQL, 127 Collection: "mycollection", 128 Operation: "myoperation", 129 ParameterizedQuery: "myquery", 130 Host: "myhost", 131 PortPathOrID: "myport", 132 DatabaseName: "dbname", 133 } 134 segment.End() 135 req, _ := http.NewRequest("GET", "http://example.com?ignore=me", nil) 136 s := StartExternalSegment(txn, req) 137 s.End() 138 txn.End() 139 app.ExpectSpanEvents(t, []internal.WantEvent{ 140 { 141 Intrinsics: map[string]interface{}{ 142 "name": "OtherTransaction/Go/hello", 143 "sampled": true, 144 "category": "generic", 145 "nr.entryPoint": true, 146 }, 147 UserAttributes: map[string]interface{}{}, 148 AgentAttributes: map[string]interface{}{}, 149 }, 150 { 151 Intrinsics: map[string]interface{}{ 152 "parentId": internal.MatchAnything, 153 "sampled": true, 154 "name": "Datastore/statement/MySQL/mycollection/myoperation", 155 "category": "datastore", 156 "component": "MySQL", 157 "span.kind": "client", 158 }, 159 UserAttributes: map[string]interface{}{}, 160 AgentAttributes: map[string]interface{}{ 161 "db.statement": "myquery", 162 "db.instance": "dbname", 163 "db.collection": "mycollection", 164 "peer.address": "myhost:myport", 165 "peer.hostname": "myhost", 166 }, 167 }, 168 { 169 Intrinsics: map[string]interface{}{ 170 "parentId": internal.MatchAnything, 171 "name": "External/example.com/http/GET", 172 "category": "http", 173 "component": "http", 174 "span.kind": "client", 175 }, 176 UserAttributes: map[string]interface{}{}, 177 AgentAttributes: map[string]interface{}{ 178 "http.url": "http://example.com", 179 "http.method": "GET", 180 }, 181 }, 182 }) 183 } 184 185 func TestSpanEventAttributesDisabled(t *testing.T) { 186 // Test that SpanEvents.Attributes.Enabled correctly disables span 187 // attributes. 188 replyfn := func(reply *internal.ConnectReply) { 189 reply.AdaptiveSampler = internal.SampleEverything{} 190 } 191 cfgfn := func(cfg *Config) { 192 cfg.DistributedTracer.Enabled = true 193 cfg.SpanEvents.Attributes.Enabled = false 194 } 195 app := testApp(replyfn, cfgfn, t) 196 txn := app.StartTransaction("hello", nil, nil) 197 segment := DatastoreSegment{ 198 StartTime: StartSegmentNow(txn), 199 Product: DatastoreMySQL, 200 Collection: "mycollection", 201 Operation: "myoperation", 202 ParameterizedQuery: "myquery", 203 Host: "myhost", 204 PortPathOrID: "myport", 205 DatabaseName: "dbname", 206 } 207 segment.End() 208 req, _ := http.NewRequest("GET", "http://example.com?ignore=me", nil) 209 s := StartExternalSegment(txn, req) 210 s.End() 211 txn.End() 212 app.ExpectSpanEvents(t, []internal.WantEvent{ 213 { 214 Intrinsics: map[string]interface{}{ 215 "name": "OtherTransaction/Go/hello", 216 "sampled": true, 217 "category": "generic", 218 "nr.entryPoint": true, 219 }, 220 UserAttributes: map[string]interface{}{}, 221 AgentAttributes: map[string]interface{}{}, 222 }, 223 { 224 Intrinsics: map[string]interface{}{ 225 "parentId": internal.MatchAnything, 226 "sampled": true, 227 "name": "Datastore/statement/MySQL/mycollection/myoperation", 228 "category": "datastore", 229 "component": "MySQL", 230 "span.kind": "client", 231 }, 232 UserAttributes: map[string]interface{}{}, 233 AgentAttributes: map[string]interface{}{}, 234 }, 235 { 236 Intrinsics: map[string]interface{}{ 237 "parentId": internal.MatchAnything, 238 "name": "External/example.com/http/GET", 239 "category": "http", 240 "component": "http", 241 "span.kind": "client", 242 }, 243 UserAttributes: map[string]interface{}{}, 244 AgentAttributes: map[string]interface{}{}, 245 }, 246 }) 247 } 248 249 func TestSpanEventAttributesSpecificallyExcluded(t *testing.T) { 250 // Test that SpanEvents.Attributes.Exclude excludes span attributes. 251 replyfn := func(reply *internal.ConnectReply) { 252 reply.AdaptiveSampler = internal.SampleEverything{} 253 } 254 cfgfn := func(cfg *Config) { 255 cfg.DistributedTracer.Enabled = true 256 cfg.SpanEvents.Attributes.Exclude = []string{ 257 SpanAttributeDBStatement, 258 SpanAttributeDBInstance, 259 SpanAttributeDBCollection, 260 SpanAttributePeerAddress, 261 SpanAttributePeerHostname, 262 SpanAttributeHTTPURL, 263 SpanAttributeHTTPMethod, 264 } 265 } 266 app := testApp(replyfn, cfgfn, t) 267 txn := app.StartTransaction("hello", nil, nil) 268 segment := DatastoreSegment{ 269 StartTime: StartSegmentNow(txn), 270 Product: DatastoreMySQL, 271 Collection: "mycollection", 272 Operation: "myoperation", 273 ParameterizedQuery: "myquery", 274 Host: "myhost", 275 PortPathOrID: "myport", 276 DatabaseName: "dbname", 277 } 278 segment.End() 279 req, _ := http.NewRequest("GET", "http://example.com?ignore=me", nil) 280 s := StartExternalSegment(txn, req) 281 s.End() 282 txn.End() 283 app.ExpectSpanEvents(t, []internal.WantEvent{ 284 { 285 Intrinsics: map[string]interface{}{ 286 "name": "OtherTransaction/Go/hello", 287 "sampled": true, 288 "category": "generic", 289 "nr.entryPoint": true, 290 }, 291 UserAttributes: map[string]interface{}{}, 292 AgentAttributes: map[string]interface{}{}, 293 }, 294 { 295 Intrinsics: map[string]interface{}{ 296 "parentId": internal.MatchAnything, 297 "sampled": true, 298 "name": "Datastore/statement/MySQL/mycollection/myoperation", 299 "category": "datastore", 300 "component": "MySQL", 301 "span.kind": "client", 302 }, 303 UserAttributes: map[string]interface{}{}, 304 AgentAttributes: map[string]interface{}{}, 305 }, 306 { 307 Intrinsics: map[string]interface{}{ 308 "parentId": internal.MatchAnything, 309 "name": "External/example.com/http/GET", 310 "category": "http", 311 "component": "http", 312 "span.kind": "client", 313 }, 314 UserAttributes: map[string]interface{}{}, 315 AgentAttributes: map[string]interface{}{}, 316 }, 317 }) 318 } 319 320 func TestSpanEventAttributesExcluded(t *testing.T) { 321 // Test that Attributes.Exclude excludes span attributes. 322 replyfn := func(reply *internal.ConnectReply) { 323 reply.AdaptiveSampler = internal.SampleEverything{} 324 } 325 cfgfn := func(cfg *Config) { 326 cfg.DistributedTracer.Enabled = true 327 cfg.Attributes.Exclude = []string{ 328 SpanAttributeDBStatement, 329 SpanAttributeDBInstance, 330 SpanAttributeDBCollection, 331 SpanAttributePeerAddress, 332 SpanAttributePeerHostname, 333 SpanAttributeHTTPURL, 334 SpanAttributeHTTPMethod, 335 } 336 } 337 app := testApp(replyfn, cfgfn, t) 338 txn := app.StartTransaction("hello", nil, nil) 339 segment := DatastoreSegment{ 340 StartTime: StartSegmentNow(txn), 341 Product: DatastoreMySQL, 342 Collection: "mycollection", 343 Operation: "myoperation", 344 ParameterizedQuery: "myquery", 345 Host: "myhost", 346 PortPathOrID: "myport", 347 DatabaseName: "dbname", 348 } 349 segment.End() 350 req, _ := http.NewRequest("GET", "http://example.com?ignore=me", nil) 351 s := StartExternalSegment(txn, req) 352 s.End() 353 txn.End() 354 app.ExpectSpanEvents(t, []internal.WantEvent{ 355 { 356 Intrinsics: map[string]interface{}{ 357 "name": "OtherTransaction/Go/hello", 358 "sampled": true, 359 "category": "generic", 360 "nr.entryPoint": true, 361 }, 362 UserAttributes: map[string]interface{}{}, 363 AgentAttributes: map[string]interface{}{}, 364 }, 365 { 366 Intrinsics: map[string]interface{}{ 367 "parentId": internal.MatchAnything, 368 "sampled": true, 369 "name": "Datastore/statement/MySQL/mycollection/myoperation", 370 "category": "datastore", 371 "component": "MySQL", 372 "span.kind": "client", 373 }, 374 UserAttributes: map[string]interface{}{}, 375 AgentAttributes: map[string]interface{}{}, 376 }, 377 { 378 Intrinsics: map[string]interface{}{ 379 "parentId": internal.MatchAnything, 380 "name": "External/example.com/http/GET", 381 "category": "http", 382 "component": "http", 383 "span.kind": "client", 384 }, 385 UserAttributes: map[string]interface{}{}, 386 AgentAttributes: map[string]interface{}{}, 387 }, 388 }) 389 } 390 391 func TestSpanEventAttributesLASP(t *testing.T) { 392 // Test that security policies prevent the capture of the input query 393 // statement. 394 replyfn := func(reply *internal.ConnectReply) { 395 reply.AdaptiveSampler = internal.SampleEverything{} 396 reply.SecurityPolicies.RecordSQL.SetEnabled(false) 397 } 398 cfgfn := func(cfg *Config) { 399 cfg.DistributedTracer.Enabled = true 400 } 401 app := testApp(replyfn, cfgfn, t) 402 txn := app.StartTransaction("hello", nil, nil) 403 segment := DatastoreSegment{ 404 StartTime: StartSegmentNow(txn), 405 Product: DatastoreMySQL, 406 Collection: "mycollection", 407 Operation: "myoperation", 408 ParameterizedQuery: "myquery", 409 Host: "myhost", 410 PortPathOrID: "myport", 411 DatabaseName: "dbname", 412 } 413 segment.End() 414 req, _ := http.NewRequest("GET", "http://example.com?ignore=me", nil) 415 s := StartExternalSegment(txn, req) 416 s.End() 417 txn.End() 418 app.ExpectSpanEvents(t, []internal.WantEvent{ 419 { 420 Intrinsics: map[string]interface{}{ 421 "name": "OtherTransaction/Go/hello", 422 "sampled": true, 423 "category": "generic", 424 "nr.entryPoint": true, 425 }, 426 UserAttributes: map[string]interface{}{}, 427 AgentAttributes: map[string]interface{}{}, 428 }, 429 { 430 Intrinsics: map[string]interface{}{ 431 "parentId": internal.MatchAnything, 432 "sampled": true, 433 "name": "Datastore/statement/MySQL/mycollection/myoperation", 434 "category": "datastore", 435 "component": "MySQL", 436 "span.kind": "client", 437 }, 438 UserAttributes: map[string]interface{}{}, 439 AgentAttributes: map[string]interface{}{ 440 "db.instance": "dbname", 441 "db.collection": "mycollection", 442 "peer.address": "myhost:myport", 443 "peer.hostname": "myhost", 444 "db.statement": "'myoperation' on 'mycollection' using 'MySQL'", 445 }, 446 }, 447 { 448 Intrinsics: map[string]interface{}{ 449 "parentId": internal.MatchAnything, 450 "name": "External/example.com/http/GET", 451 "category": "http", 452 "component": "http", 453 "span.kind": "client", 454 }, 455 UserAttributes: map[string]interface{}{}, 456 AgentAttributes: map[string]interface{}{ 457 "http.url": "http://example.com", 458 "http.method": "GET", 459 }, 460 }, 461 }) 462 } 463 464 func TestAddAgentSpanAttribute(t *testing.T) { 465 // Test that AddAgentSpanAttribute successfully adds attributes to 466 // spans. 467 replyfn := func(reply *internal.ConnectReply) { 468 reply.AdaptiveSampler = internal.SampleEverything{} 469 } 470 cfgfn := func(cfg *Config) { 471 cfg.DistributedTracer.Enabled = true 472 } 473 app := testApp(replyfn, cfgfn, t) 474 txn := app.StartTransaction("hello", nil, nil) 475 s := StartSegment(txn, "hi") 476 internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSRegion, "west") 477 internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSRequestID, "123") 478 internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSOperation, "secret") 479 s.End() 480 txn.End() 481 app.ExpectSpanEvents(t, []internal.WantEvent{ 482 { 483 Intrinsics: map[string]interface{}{ 484 "name": "OtherTransaction/Go/hello", 485 "sampled": true, 486 "category": "generic", 487 "nr.entryPoint": true, 488 }, 489 UserAttributes: map[string]interface{}{}, 490 AgentAttributes: map[string]interface{}{}, 491 }, 492 { 493 Intrinsics: map[string]interface{}{ 494 "name": "Custom/hi", 495 "sampled": true, 496 "category": "generic", 497 "parentId": internal.MatchAnything, 498 }, 499 UserAttributes: map[string]interface{}{}, 500 AgentAttributes: map[string]interface{}{ 501 "aws.operation": "secret", 502 "aws.requestId": "123", 503 "aws.region": "west", 504 }, 505 }, 506 }) 507 } 508 509 func TestAddAgentSpanAttributeExcluded(t *testing.T) { 510 // Test that span attributes added by AddAgentSpanAttribute are subject 511 // to span attribute configuration. 512 replyfn := func(reply *internal.ConnectReply) { 513 reply.AdaptiveSampler = internal.SampleEverything{} 514 } 515 cfgfn := func(cfg *Config) { 516 cfg.DistributedTracer.Enabled = true 517 cfg.SpanEvents.Attributes.Exclude = []string{ 518 SpanAttributeAWSOperation, 519 SpanAttributeAWSRequestID, 520 SpanAttributeAWSRegion, 521 } 522 } 523 app := testApp(replyfn, cfgfn, t) 524 txn := app.StartTransaction("hello", nil, nil) 525 s := StartSegment(txn, "hi") 526 internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSRegion, "west") 527 internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSRequestID, "123") 528 internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSOperation, "secret") 529 s.End() 530 txn.End() 531 app.ExpectSpanEvents(t, []internal.WantEvent{ 532 { 533 Intrinsics: map[string]interface{}{ 534 "name": "OtherTransaction/Go/hello", 535 "sampled": true, 536 "category": "generic", 537 "nr.entryPoint": true, 538 }, 539 UserAttributes: map[string]interface{}{}, 540 AgentAttributes: map[string]interface{}{}, 541 }, 542 { 543 Intrinsics: map[string]interface{}{ 544 "name": "Custom/hi", 545 "sampled": true, 546 "category": "generic", 547 "parentId": internal.MatchAnything, 548 }, 549 UserAttributes: map[string]interface{}{}, 550 AgentAttributes: map[string]interface{}{}, 551 }, 552 }) 553 } 554 555 func TestAddSpanAttributeNoActiveSpan(t *testing.T) { 556 // Test that AddAgentSpanAttribute does not have problems if called when 557 // there is no active span. 558 replyfn := func(reply *internal.ConnectReply) { 559 reply.AdaptiveSampler = internal.SampleEverything{} 560 } 561 cfgfn := func(cfg *Config) { 562 cfg.DistributedTracer.Enabled = true 563 } 564 app := testApp(replyfn, cfgfn, t) 565 txn := app.StartTransaction("hello", nil, nil) 566 // Do not panic if there are no active spans! 567 internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSRegion, "west") 568 internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSRequestID, "123") 569 internal.AddAgentSpanAttribute(txn, internal.SpanAttributeAWSOperation, "secret") 570 txn.End() 571 app.ExpectSpanEvents(t, []internal.WantEvent{ 572 { 573 Intrinsics: map[string]interface{}{ 574 "name": "OtherTransaction/Go/hello", 575 "sampled": true, 576 "category": "generic", 577 "nr.entryPoint": true, 578 }, 579 UserAttributes: map[string]interface{}{}, 580 AgentAttributes: map[string]interface{}{}, 581 }, 582 }) 583 } 584 585 func TestAddSpanAttributeNilTransaction(t *testing.T) { 586 // Test that AddAgentSpanAttribute does not panic if the transaction is 587 // nil. 588 internal.AddAgentSpanAttribute(nil, internal.SpanAttributeAWSRegion, "west") 589 internal.AddAgentSpanAttribute(nil, internal.SpanAttributeAWSRequestID, "123") 590 internal.AddAgentSpanAttribute(nil, internal.SpanAttributeAWSOperation, "secret") 591 }