github.com/mier85/go-sensor@v1.30.1-0.20220920111756-9bf41b3bc7e0/instrumentation_http_test.go (about) 1 // (c) Copyright IBM Corp. 2021 2 // (c) Copyright Instana Inc. 2020 3 4 package instana_test 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "net/http" 11 "net/http/httptest" 12 "strings" 13 "testing" 14 15 "github.com/instana/testify/assert" 16 "github.com/instana/testify/require" 17 instana "github.com/mier85/go-sensor" 18 "github.com/mier85/go-sensor/w3ctrace" 19 ) 20 21 func BenchmarkTracingNamedHandlerFunc(b *testing.B) { 22 recorder := instana.NewTestRecorder() 23 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{ 24 Service: "go-sensor-test", 25 }, recorder)) 26 27 h := instana.TracingNamedHandlerFunc(s, "action", "/{action}", func(w http.ResponseWriter, req *http.Request) { 28 fmt.Fprintln(w, "Ok") 29 }) 30 31 req := httptest.NewRequest(http.MethodGet, "/test?q=term", nil) 32 33 rec := httptest.NewRecorder() 34 35 b.ResetTimer() 36 37 for i := 0; i < b.N; i++ { 38 h.ServeHTTP(rec, req) 39 } 40 } 41 42 func TestTracingNamedHandlerFunc_Write(t *testing.T) { 43 recorder := instana.NewTestRecorder() 44 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{ 45 Service: "go-sensor-test", 46 }, recorder)) 47 48 h := instana.TracingNamedHandlerFunc(s, "action", "/{action}", func(w http.ResponseWriter, req *http.Request) { 49 w.Header().Set("X-Response", "true") 50 w.Header().Set("X-Custom-Header-2", "response") 51 fmt.Fprintln(w, "Ok") 52 }) 53 54 req := httptest.NewRequest(http.MethodGet, "/test?q=term", nil) 55 req.Header.Set("Authorization", "Basic blah") 56 req.Header.Set("X-Custom-Header-1", "request") 57 58 rec := httptest.NewRecorder() 59 h.ServeHTTP(rec, req) 60 61 assert.Equal(t, http.StatusOK, rec.Code) 62 assert.Equal(t, "Ok\n", rec.Body.String()) 63 64 spans := recorder.GetQueuedSpans() 65 require.Len(t, spans, 1) 66 67 span := spans[0] 68 assert.Equal(t, 0, span.Ec) 69 assert.EqualValues(t, instana.EntrySpanKind, span.Kind) 70 assert.False(t, span.Synthetic) 71 assert.Empty(t, span.CorrelationType) 72 assert.Empty(t, span.CorrelationID) 73 assert.False(t, span.ForeignTrace) 74 assert.Empty(t, span.Ancestor) 75 76 require.IsType(t, instana.HTTPSpanData{}, span.Data) 77 data := span.Data.(instana.HTTPSpanData) 78 79 assert.Equal(t, instana.HTTPSpanTags{ 80 Host: "example.com", 81 Status: http.StatusOK, 82 Method: "GET", 83 Path: "/test", 84 Params: "q=term", 85 Headers: map[string]string{ 86 "x-custom-header-1": "request", 87 "x-custom-header-2": "response", 88 }, 89 PathTemplate: "/{action}", 90 RouteID: "action", 91 }, data.Tags) 92 93 // check whether the trace context has been sent back to the client 94 assert.Equal(t, instana.FormatID(span.TraceID), rec.Header().Get(instana.FieldT)) 95 assert.Equal(t, instana.FormatID(span.SpanID), rec.Header().Get(instana.FieldS)) 96 97 // w3c trace context 98 traceparent := rec.Header().Get(w3ctrace.TraceParentHeader) 99 assert.Contains(t, traceparent, instana.FormatLongID(span.TraceIDHi, span.TraceID)) 100 assert.Contains(t, traceparent, instana.FormatID(span.SpanID)) 101 102 tracestate := rec.Header().Get(w3ctrace.TraceStateHeader) 103 assert.True(t, strings.HasPrefix( 104 tracestate, 105 "in="+instana.FormatID(span.TraceID)+";"+instana.FormatID(span.SpanID), 106 ), tracestate) 107 } 108 109 func TestTracingNamedHandlerFunc_InstanaFieldLPriorityOverTraceParentHeader(t *testing.T) { 110 type testCase struct { 111 headers http.Header 112 traceParentHeaderSuffix string 113 } 114 115 testCases := map[string]testCase{ 116 "traceparent is suppressed, x-instana-l is not suppressed": { 117 headers: http.Header{ 118 w3ctrace.TraceParentHeader: []string{"00-00000000000000000000000000000001-0000000000000001-00"}, 119 instana.FieldL: []string{"1"}, 120 }, 121 traceParentHeaderSuffix: "-01", 122 }, 123 "traceparent is suppressed, x-instana-l is absent (is not suppressed by default)": { 124 headers: http.Header{ 125 w3ctrace.TraceParentHeader: []string{"00-00000000000000000000000000000001-0000000000000001-00"}, 126 }, 127 traceParentHeaderSuffix: "-01", 128 }, 129 "traceparent is not suppressed, x-instana-l is absent (tracing enabled by default)": { 130 headers: http.Header{ 131 w3ctrace.TraceParentHeader: []string{"00-00000000000000000000000000000001-0000000000000001-01"}, 132 }, 133 traceParentHeaderSuffix: "-01", 134 }, 135 "traceparent is not suppressed, x-instana-l is not suppressed": { 136 headers: http.Header{ 137 w3ctrace.TraceParentHeader: []string{"00-00000000000000000000000000000001-0000000000000001-01"}, 138 instana.FieldL: []string{"1"}, 139 }, 140 traceParentHeaderSuffix: "-01", 141 }, 142 "traceparent is suppressed, x-instana-l is suppressed": { 143 headers: http.Header{ 144 w3ctrace.TraceParentHeader: []string{"00-00000000000000000000000000000001-0000000000000001-00"}, 145 instana.FieldL: []string{"0"}, 146 }, 147 traceParentHeaderSuffix: "-00", 148 }, 149 "traceparent is not suppressed, x-instana-l is suppressed": { 150 headers: http.Header{ 151 w3ctrace.TraceParentHeader: []string{"00-00000000000000000000000000000001-0000000000000001-01"}, 152 instana.FieldL: []string{"0"}, 153 }, 154 traceParentHeaderSuffix: "-00", 155 }, 156 } 157 158 recorder := instana.NewTestRecorder() 159 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{ 160 Service: "go-sensor-test", 161 }, recorder)) 162 163 h := instana.TracingNamedHandlerFunc(s, "action", "/test", func(w http.ResponseWriter, req *http.Request) {}) 164 165 for name, testCase := range testCases { 166 req := httptest.NewRequest(http.MethodGet, "/test", nil) 167 req.Header = testCase.headers 168 169 rec := httptest.NewRecorder() 170 h.ServeHTTP(rec, req) 171 172 assert.Equal(t, http.StatusOK, rec.Code) 173 assert.True(t, strings.HasSuffix(rec.Header().Get(w3ctrace.TraceParentHeader), testCase.traceParentHeaderSuffix), "case '"+name+"' failed") 174 } 175 } 176 177 func TestTracingNamedHandlerFunc_WriteHeaders(t *testing.T) { 178 recorder := instana.NewTestRecorder() 179 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{}, recorder)) 180 181 h := instana.TracingNamedHandlerFunc(s, "test", "/test", func(w http.ResponseWriter, req *http.Request) { 182 w.WriteHeader(http.StatusNotFound) 183 }) 184 185 rec := httptest.NewRecorder() 186 h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/test?q=term", nil)) 187 188 assert.Equal(t, http.StatusNotFound, rec.Code) 189 190 spans := recorder.GetQueuedSpans() 191 require.Len(t, spans, 1) 192 193 span := spans[0] 194 assert.Equal(t, 0, span.Ec) 195 assert.EqualValues(t, instana.EntrySpanKind, span.Kind) 196 assert.False(t, span.Synthetic) 197 assert.Empty(t, span.CorrelationType) 198 assert.Empty(t, span.CorrelationID) 199 assert.False(t, span.ForeignTrace) 200 assert.Empty(t, span.Ancestor) 201 202 require.IsType(t, instana.HTTPSpanData{}, span.Data) 203 data := span.Data.(instana.HTTPSpanData) 204 205 assert.Equal(t, instana.HTTPSpanTags{ 206 Status: http.StatusNotFound, 207 Method: "GET", 208 Host: "example.com", 209 Path: "/test", 210 Params: "q=term", 211 RouteID: "test", 212 }, data.Tags) 213 214 // check whether the trace context has been sent back to the client 215 assert.Equal(t, instana.FormatID(span.TraceID), rec.Header().Get(instana.FieldT)) 216 assert.Equal(t, instana.FormatID(span.SpanID), rec.Header().Get(instana.FieldS)) 217 218 // w3c trace context 219 traceparent := rec.Header().Get(w3ctrace.TraceParentHeader) 220 assert.Contains(t, traceparent, instana.FormatLongID(span.TraceIDHi, span.TraceID)) 221 assert.Contains(t, traceparent, instana.FormatID(span.SpanID)) 222 223 tracestate := rec.Header().Get(w3ctrace.TraceStateHeader) 224 assert.True(t, strings.HasPrefix( 225 tracestate, 226 "in="+instana.FormatID(span.TraceID)+";"+instana.FormatID(span.SpanID), 227 ), tracestate) 228 } 229 230 func TestTracingNamedHandlerFunc_W3CTraceContext(t *testing.T) { 231 recorder := instana.NewTestRecorder() 232 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{}, recorder)) 233 234 h := instana.TracingNamedHandlerFunc(s, "test", "/test", func(w http.ResponseWriter, req *http.Request) { 235 fmt.Fprintln(w, "Ok") 236 }) 237 238 rec := httptest.NewRecorder() 239 240 req := httptest.NewRequest(http.MethodGet, "/test", nil) 241 req.Header.Set(w3ctrace.TraceParentHeader, "00-00000000000000010000000000000002-0000000000000003-01") 242 req.Header.Set(w3ctrace.TraceStateHeader, "in=1234;5678,rojo=00f067aa0ba902b7") 243 244 h.ServeHTTP(rec, req) 245 246 assert.Equal(t, http.StatusOK, rec.Code) 247 248 spans := recorder.GetQueuedSpans() 249 require.Len(t, spans, 1) 250 251 span := spans[0] 252 253 assert.EqualValues(t, 0x1, span.TraceIDHi) 254 assert.EqualValues(t, 0x2, span.TraceID) 255 assert.EqualValues(t, 0x3, span.ParentID) 256 257 assert.Equal(t, 0, span.Ec) 258 assert.EqualValues(t, instana.EntrySpanKind, span.Kind) 259 assert.False(t, span.Synthetic) 260 assert.Empty(t, span.CorrelationType) 261 assert.Empty(t, span.CorrelationID) 262 assert.True(t, span.ForeignTrace) 263 assert.Equal(t, &instana.TraceReference{ 264 TraceID: "1234", 265 ParentID: "5678", 266 }, span.Ancestor) 267 268 require.IsType(t, instana.HTTPSpanData{}, span.Data) 269 data := span.Data.(instana.HTTPSpanData) 270 271 assert.Equal(t, instana.HTTPSpanTags{ 272 Host: "example.com", 273 Status: http.StatusOK, 274 Method: "GET", 275 Path: "/test", 276 RouteID: "test", 277 }, data.Tags) 278 279 // check whether the trace context has been sent back to the client 280 assert.Equal(t, instana.FormatID(span.TraceID), rec.Header().Get(instana.FieldT)) 281 assert.Equal(t, instana.FormatID(span.SpanID), rec.Header().Get(instana.FieldS)) 282 283 // w3c trace context 284 traceparent := rec.Header().Get(w3ctrace.TraceParentHeader) 285 assert.Contains(t, traceparent, instana.FormatLongID(span.TraceIDHi, span.TraceID)) 286 assert.Contains(t, traceparent, instana.FormatID(span.SpanID)) 287 288 tracestate := rec.Header().Get(w3ctrace.TraceStateHeader) 289 assert.True(t, strings.HasPrefix( 290 tracestate, 291 "in="+instana.FormatID(span.TraceID)+";"+instana.FormatID(span.SpanID), 292 ), tracestate) 293 } 294 295 func TestTracingHandlerFunc_SecretsFiltering(t *testing.T) { 296 recorder := instana.NewTestRecorder() 297 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{ 298 Service: "go-sensor-test", 299 }, recorder)) 300 301 h := instana.TracingNamedHandlerFunc(s, "action", "/{action}", func(w http.ResponseWriter, req *http.Request) { 302 fmt.Fprintln(w, "Ok") 303 }) 304 305 req := httptest.NewRequest(http.MethodGet, "/test?q=term&sensitive_key=s3cr3t&myPassword=qwerty&SECRET_VALUE=1", nil) 306 307 rec := httptest.NewRecorder() 308 h.ServeHTTP(rec, req) 309 310 assert.Equal(t, http.StatusOK, rec.Code) 311 assert.Equal(t, "Ok\n", rec.Body.String()) 312 313 spans := recorder.GetQueuedSpans() 314 require.Len(t, spans, 1) 315 316 span := spans[0] 317 assert.Equal(t, 0, span.Ec) 318 assert.EqualValues(t, instana.EntrySpanKind, span.Kind) 319 assert.False(t, span.Synthetic) 320 assert.Empty(t, span.CorrelationType) 321 assert.Empty(t, span.CorrelationID) 322 323 require.IsType(t, instana.HTTPSpanData{}, span.Data) 324 data := span.Data.(instana.HTTPSpanData) 325 326 assert.Equal(t, instana.HTTPSpanTags{ 327 Host: "example.com", 328 Status: http.StatusOK, 329 Method: "GET", 330 Path: "/test", 331 Params: "SECRET_VALUE=%3Credacted%3E&myPassword=%3Credacted%3E&q=term&sensitive_key=%3Credacted%3E", 332 PathTemplate: "/{action}", 333 RouteID: "action", 334 }, data.Tags) 335 336 // check whether the trace context has been sent back to the client 337 assert.Equal(t, instana.FormatID(span.TraceID), rec.Header().Get(instana.FieldT)) 338 assert.Equal(t, instana.FormatID(span.SpanID), rec.Header().Get(instana.FieldS)) 339 } 340 341 func TestTracingHandlerFunc_Error(t *testing.T) { 342 recorder := instana.NewTestRecorder() 343 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{}, recorder)) 344 345 h := instana.TracingNamedHandlerFunc(s, "test", "/test", func(w http.ResponseWriter, req *http.Request) { 346 http.Error(w, "something went wrong", http.StatusInternalServerError) 347 }) 348 349 rec := httptest.NewRecorder() 350 h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/test", nil)) 351 352 assert.Equal(t, http.StatusInternalServerError, rec.Code) 353 354 spans := recorder.GetQueuedSpans() 355 require.Len(t, spans, 2) 356 357 span, logSpan := spans[0], spans[1] 358 assert.Equal(t, 1, span.Ec) 359 assert.EqualValues(t, instana.EntrySpanKind, span.Kind) 360 assert.False(t, span.Synthetic) 361 362 require.IsType(t, instana.HTTPSpanData{}, span.Data) 363 data := span.Data.(instana.HTTPSpanData) 364 365 assert.Equal(t, instana.HTTPSpanTags{ 366 Status: http.StatusInternalServerError, 367 Method: "GET", 368 Host: "example.com", 369 Path: "/test", 370 RouteID: "test", 371 Error: "Internal Server Error", 372 }, data.Tags) 373 374 assert.Equal(t, span.TraceID, logSpan.TraceID) 375 assert.Equal(t, span.SpanID, logSpan.ParentID) 376 assert.Equal(t, "log.go", logSpan.Name) 377 378 // assert that log message has been recorded within the span interval 379 assert.GreaterOrEqual(t, logSpan.Timestamp, span.Timestamp) 380 assert.LessOrEqual(t, logSpan.Duration, span.Duration) 381 382 require.IsType(t, instana.LogSpanData{}, logSpan.Data) 383 logData := logSpan.Data.(instana.LogSpanData) 384 385 assert.Equal(t, instana.LogSpanTags{ 386 Level: "ERROR", 387 Message: `error: "Internal Server Error"`, 388 }, logData.Tags) 389 } 390 391 func TestTracingHandlerFunc_SyntheticCall(t *testing.T) { 392 recorder := instana.NewTestRecorder() 393 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{}, recorder)) 394 395 h := instana.TracingNamedHandlerFunc(s, "test-handler", "/", func(w http.ResponseWriter, req *http.Request) { 396 fmt.Fprintln(w, "Ok") 397 }) 398 399 rec := httptest.NewRecorder() 400 401 req := httptest.NewRequest(http.MethodGet, "/test", nil) 402 req.Header.Set(instana.FieldSynthetic, "1") 403 404 h.ServeHTTP(rec, req) 405 406 assert.Equal(t, http.StatusOK, rec.Code) 407 408 spans := recorder.GetQueuedSpans() 409 require.Len(t, spans, 1) 410 assert.True(t, spans[0].Synthetic) 411 } 412 413 func TestTracingHandlerFunc_EUMCall(t *testing.T) { 414 recorder := instana.NewTestRecorder() 415 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{}, recorder)) 416 417 h := instana.TracingNamedHandlerFunc(s, "test-handler", "/", func(w http.ResponseWriter, req *http.Request) { 418 fmt.Fprintln(w, "Ok") 419 }) 420 421 rec := httptest.NewRecorder() 422 423 req := httptest.NewRequest(http.MethodGet, "/test", nil) 424 req.Header.Set(instana.FieldL, "1,correlationType=web;correlationId=eum correlation id") 425 426 h.ServeHTTP(rec, req) 427 428 assert.Equal(t, http.StatusOK, rec.Code) 429 430 spans := recorder.GetQueuedSpans() 431 require.Len(t, spans, 1) 432 assert.Equal(t, "web", spans[0].CorrelationType) 433 assert.Equal(t, "eum correlation id", spans[0].CorrelationID) 434 } 435 436 func TestTracingHandlerFunc_PanicHandling(t *testing.T) { 437 recorder := instana.NewTestRecorder() 438 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{}, recorder)) 439 440 h := instana.TracingNamedHandlerFunc(s, "test", "/test", func(w http.ResponseWriter, req *http.Request) { 441 panic("something went wrong") 442 }) 443 444 rec := httptest.NewRecorder() 445 assert.Panics(t, func() { 446 h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/test?q=term", nil)) 447 }) 448 449 spans := recorder.GetQueuedSpans() 450 require.Len(t, spans, 2) 451 452 span, logSpan := spans[0], spans[1] 453 assert.Equal(t, 1, span.Ec) 454 assert.EqualValues(t, instana.EntrySpanKind, span.Kind) 455 assert.False(t, span.Synthetic) 456 457 require.IsType(t, instana.HTTPSpanData{}, span.Data) 458 data := span.Data.(instana.HTTPSpanData) 459 460 assert.Equal(t, instana.HTTPSpanTags{ 461 Status: http.StatusInternalServerError, 462 Method: "GET", 463 Host: "example.com", 464 Path: "/test", 465 Params: "q=term", 466 RouteID: "test", 467 Error: "something went wrong", 468 }, data.Tags) 469 470 assert.Equal(t, span.TraceID, logSpan.TraceID) 471 assert.Equal(t, span.SpanID, logSpan.ParentID) 472 assert.Equal(t, "log.go", logSpan.Name) 473 474 // assert that log message has been recorded within the span interval 475 assert.GreaterOrEqual(t, logSpan.Timestamp, span.Timestamp) 476 assert.LessOrEqual(t, logSpan.Duration, span.Duration) 477 478 require.IsType(t, instana.LogSpanData{}, logSpan.Data) 479 logData := logSpan.Data.(instana.LogSpanData) 480 481 assert.Equal(t, instana.LogSpanTags{ 482 Level: "ERROR", 483 Message: `error: "something went wrong"`, 484 }, logData.Tags) 485 } 486 487 func TestRoundTripper(t *testing.T) { 488 recorder := instana.NewTestRecorder() 489 tracer := instana.NewTracerWithEverything(&instana.Options{}, recorder) 490 s := instana.NewSensorWithTracer(tracer) 491 492 parentSpan := tracer.StartSpan("parent") 493 494 var traceIDHeader, spanIDHeader string 495 rt := instana.RoundTripper(s, testRoundTripper(func(req *http.Request) (*http.Response, error) { 496 traceIDHeader = req.Header.Get(instana.FieldT) 497 spanIDHeader = req.Header.Get(instana.FieldS) 498 499 return &http.Response{ 500 Status: http.StatusText(http.StatusNotImplemented), 501 StatusCode: http.StatusNotImplemented, 502 Header: http.Header{ 503 "X-Response": []string{"true"}, 504 "X-Custom-Header-2": []string{"response"}, 505 }, 506 }, nil 507 })) 508 509 ctx := instana.ContextWithSpan(context.Background(), parentSpan) 510 req := httptest.NewRequest("GET", "http://user:password@example.com/hello?q=term&sensitive_key=s3cr3t&myPassword=qwerty&SECRET_VALUE=1", nil) 511 req.Header.Set("X-Custom-Header-1", "request") 512 req.Header.Set("Authorization", "Basic blah") 513 514 _, err := rt.RoundTrip(req.WithContext(ctx)) 515 require.NoError(t, err) 516 517 parentSpan.Finish() 518 519 spans := recorder.GetQueuedSpans() 520 require.Len(t, spans, 2) 521 522 cSpan, pSpan := spans[0], spans[1] 523 assert.Equal(t, 0, cSpan.Ec) 524 assert.EqualValues(t, instana.ExitSpanKind, cSpan.Kind) 525 526 assert.Equal(t, pSpan.TraceID, cSpan.TraceID) 527 assert.Equal(t, pSpan.SpanID, cSpan.ParentID) 528 529 assert.Equal(t, instana.FormatID(cSpan.TraceID), traceIDHeader) 530 assert.Equal(t, instana.FormatID(cSpan.SpanID), spanIDHeader) 531 532 require.IsType(t, instana.HTTPSpanData{}, cSpan.Data) 533 data := cSpan.Data.(instana.HTTPSpanData) 534 535 assert.Equal(t, instana.HTTPSpanTags{ 536 Method: "GET", 537 Status: http.StatusNotImplemented, 538 URL: "http://example.com/hello", 539 Params: "SECRET_VALUE=%3Credacted%3E&myPassword=%3Credacted%3E&q=term&sensitive_key=%3Credacted%3E", 540 Headers: map[string]string{ 541 "x-custom-header-1": "request", 542 "x-custom-header-2": "response", 543 }, 544 }, data.Tags) 545 } 546 547 func TestRoundTripper_WithoutParentSpan(t *testing.T) { 548 recorder := instana.NewTestRecorder() 549 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{}, recorder)) 550 551 rt := instana.RoundTripper(s, testRoundTripper(func(req *http.Request) (*http.Response, error) { 552 assert.Empty(t, req.Header.Get(instana.FieldT)) 553 assert.Empty(t, req.Header.Get(instana.FieldS)) 554 555 return &http.Response{ 556 Status: http.StatusText(http.StatusNotImplemented), 557 StatusCode: http.StatusNotImplemented, 558 }, nil 559 })) 560 561 resp, err := rt.RoundTrip(httptest.NewRequest("GET", "http://example.com/hello", nil)) 562 require.NoError(t, err) 563 assert.Equal(t, http.StatusNotImplemented, resp.StatusCode) 564 565 assert.Empty(t, recorder.GetQueuedSpans()) 566 } 567 568 func TestRoundTripper_Error(t *testing.T) { 569 serverErr := errors.New("something went wrong") 570 571 recorder := instana.NewTestRecorder() 572 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{}, recorder)) 573 574 rt := instana.RoundTripper(s, testRoundTripper(func(req *http.Request) (*http.Response, error) { 575 return nil, serverErr 576 })) 577 578 ctx := instana.ContextWithSpan(context.Background(), s.Tracer().StartSpan("parent")) 579 req := httptest.NewRequest("GET", "http://example.com/hello?q=term&key=s3cr3t", nil) 580 581 _, err := rt.RoundTrip(req.WithContext(ctx)) 582 assert.Error(t, err) 583 584 spans := recorder.GetQueuedSpans() 585 require.Len(t, spans, 2) 586 587 span, logSpan := spans[0], spans[1] 588 assert.Equal(t, 1, span.Ec) 589 assert.EqualValues(t, instana.ExitSpanKind, span.Kind) 590 591 require.IsType(t, instana.HTTPSpanData{}, span.Data) 592 data := span.Data.(instana.HTTPSpanData) 593 594 assert.Equal(t, instana.HTTPSpanTags{ 595 Method: "GET", 596 URL: "http://example.com/hello", 597 Params: "key=%3Credacted%3E&q=term", 598 Error: "something went wrong", 599 }, data.Tags) 600 601 assert.Equal(t, span.TraceID, logSpan.TraceID) 602 assert.Equal(t, span.SpanID, logSpan.ParentID) 603 assert.Equal(t, "log.go", logSpan.Name) 604 605 // assert that log message has been recorded within the span interval 606 assert.GreaterOrEqual(t, logSpan.Timestamp, span.Timestamp) 607 assert.LessOrEqual(t, logSpan.Duration, span.Duration) 608 609 require.IsType(t, instana.LogSpanData{}, logSpan.Data) 610 logData := logSpan.Data.(instana.LogSpanData) 611 612 assert.Equal(t, instana.LogSpanTags{ 613 Level: "ERROR", 614 Message: `error: "something went wrong"`, 615 }, logData.Tags) 616 } 617 618 func TestRoundTripper_DefaultTransport(t *testing.T) { 619 recorder := instana.NewTestRecorder() 620 s := instana.NewSensorWithTracer(instana.NewTracerWithEverything(&instana.Options{}, recorder)) 621 622 var numCalls int 623 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 624 numCalls++ 625 626 assert.NotEmpty(t, req.Header.Get(instana.FieldT)) 627 assert.NotEmpty(t, req.Header.Get(instana.FieldS)) 628 629 w.Write([]byte("OK")) 630 })) 631 defer ts.Close() 632 633 rt := instana.RoundTripper(s, nil) 634 635 ctx := instana.ContextWithSpan(context.Background(), s.Tracer().StartSpan("parent")) 636 req := httptest.NewRequest("GET", ts.URL+"/hello", nil) 637 638 resp, err := rt.RoundTrip(req.WithContext(ctx)) 639 require.NoError(t, err) 640 assert.Equal(t, http.StatusOK, resp.StatusCode) 641 642 assert.Equal(t, 1, numCalls) 643 644 spans := recorder.GetQueuedSpans() 645 require.Len(t, spans, 1) 646 647 span := spans[0] 648 assert.Equal(t, 0, span.Ec) 649 assert.EqualValues(t, instana.ExitSpanKind, span.Kind) 650 651 require.IsType(t, instana.HTTPSpanData{}, span.Data) 652 data := span.Data.(instana.HTTPSpanData) 653 654 assert.Equal(t, instana.HTTPSpanTags{ 655 Status: http.StatusOK, 656 Method: "GET", 657 URL: ts.URL + "/hello", 658 }, data.Tags) 659 } 660 661 type testRoundTripper func(*http.Request) (*http.Response, error) 662 663 func (rt testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { 664 return rt(req) 665 }