github.com/instana/go-sensor@v1.62.2-0.20240520081010-4919868049e1/span_test.go (about) 1 // (c) Copyright IBM Corp. 2021 2 // (c) Copyright Instana Inc. 2017 3 4 package instana_test 5 6 import ( 7 "errors" 8 "os" 9 "testing" 10 "time" 11 12 instana "github.com/instana/go-sensor" 13 ot "github.com/opentracing/opentracing-go" 14 "github.com/opentracing/opentracing-go/ext" 15 "github.com/opentracing/opentracing-go/log" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func TestBasicSpan(t *testing.T) { 21 instana.InitSensor(&instana.Options{ 22 Service: TestServiceName, 23 Tracer: instana.TracerOptions{ 24 CollectableHTTPHeaders: []string{"x-custom-header-1", "x-custom-header-2"}, 25 }, 26 AgentClient: alwaysReadyClient{}, 27 }) 28 defer instana.ShutdownSensor() 29 30 recorder := instana.NewTestRecorder() 31 tracer := instana.NewTracerWithEverything(&instana.Options{AgentClient: alwaysReadyClient{}}, recorder) 32 33 start := time.Now() 34 sp := tracer.StartSpan("test") 35 time.Sleep(10 * time.Millisecond) 36 sp.Finish() 37 elapsed := time.Since(start) 38 39 spans := recorder.GetQueuedSpans() 40 require.Len(t, spans, 1) 41 span := spans[0] 42 43 assert.NotEmpty(t, span.SpanID) 44 assert.NotEmpty(t, span.TraceID) 45 assert.NotEmpty(t, span.Timestamp) 46 assert.LessOrEqual(t, uint64(10), span.Duration) 47 assert.LessOrEqual(t, span.Duration, uint64(elapsed)) 48 assert.Equal(t, "sdk", span.Name) 49 50 require.IsType(t, instana.SDKSpanData{}, span.Data) 51 data := span.Data.(instana.SDKSpanData) 52 assert.Equal(t, TestServiceName, data.Service) 53 54 assert.Equal(t, "test", data.Tags.Name) 55 assert.Nil(t, data.Tags.Custom["tags"]) 56 assert.Nil(t, data.Tags.Custom["baggage"]) 57 } 58 59 func TestSpanHeritage(t *testing.T) { 60 recorder := instana.NewTestRecorder() 61 tracer := instana.NewTracerWithEverything(&instana.Options{AgentClient: alwaysReadyClient{}}, recorder) 62 defer instana.ShutdownSensor() 63 64 parentSpan := tracer.StartSpan("parent") 65 66 childSpan := tracer.StartSpan("child", ot.ChildOf(parentSpan.Context())) 67 childSpan.Finish() 68 69 parentSpan.Finish() 70 71 spans := recorder.GetQueuedSpans() 72 require.Len(t, spans, 2) 73 74 cSpan, pSpan := spans[0], spans[1] 75 76 // Parent should not have a parent 77 assert.Empty(t, pSpan.ParentID) 78 79 // Child must have parent ID set as parent 80 assert.Equal(t, pSpan.SpanID, cSpan.ParentID) 81 82 // Must be root span 83 assert.Equal(t, pSpan.TraceID, pSpan.SpanID) 84 85 // Trace ID must be consistent across spans 86 assert.Equal(t, cSpan.TraceID, pSpan.TraceID) 87 88 require.IsType(t, cSpan.Data, instana.SDKSpanData{}) 89 cData := cSpan.Data.(instana.SDKSpanData) 90 assert.Equal(t, "child", cData.Tags.Name) 91 92 require.IsType(t, pSpan.Data, instana.SDKSpanData{}) 93 pData := pSpan.Data.(instana.SDKSpanData) 94 assert.Equal(t, "parent", pData.Tags.Name) 95 } 96 97 func TestSpanBaggage(t *testing.T) { 98 const op = "test" 99 recorder := instana.NewTestRecorder() 100 tracer := instana.NewTracerWithEverything(&instana.Options{AgentClient: alwaysReadyClient{}}, recorder) 101 defer instana.ShutdownSensor() 102 103 sp := tracer.StartSpan(op) 104 sp.SetBaggageItem("foo", "bar") 105 sp.Finish() 106 107 spans := recorder.GetQueuedSpans() 108 require.Len(t, spans, 1) 109 span := spans[0] 110 111 require.IsType(t, instana.SDKSpanData{}, span.Data) 112 data := span.Data.(instana.SDKSpanData) 113 114 assert.Equal(t, map[string]string{"foo": "bar"}, data.Tags.Custom["baggage"]) 115 } 116 117 func TestSpanTags(t *testing.T) { 118 const op = "test" 119 recorder := instana.NewTestRecorder() 120 tracer := instana.NewTracerWithEverything(&instana.Options{AgentClient: alwaysReadyClient{}}, recorder) 121 defer instana.ShutdownSensor() 122 123 sp := tracer.StartSpan(op) 124 sp.SetTag("foo", "bar") 125 sp.Finish() 126 127 spans := recorder.GetQueuedSpans() 128 require.Len(t, spans, 1) 129 span := spans[0] 130 131 require.IsType(t, instana.SDKSpanData{}, span.Data) 132 data := span.Data.(instana.SDKSpanData) 133 134 assert.Equal(t, ot.Tags{"foo": "bar"}, data.Tags.Custom["tags"]) 135 } 136 137 func TestOTLogError(t *testing.T) { 138 recorder := instana.NewTestRecorder() 139 tracer := instana.NewTracerWithEverything(&instana.Options{AgentClient: alwaysReadyClient{}}, recorder) 140 defer instana.ShutdownSensor() 141 142 sp := tracer.StartSpan("test") 143 ext.Error.Set(sp, true) 144 sp.Finish() 145 146 spans := recorder.GetQueuedSpans() 147 require.Equal(t, len(spans), 1) 148 149 span := spans[0] 150 assert.Equal(t, 1, span.Ec) 151 152 require.IsType(t, instana.SDKSpanData{}, span.Data) 153 data := span.Data.(instana.SDKSpanData) 154 155 assert.Equal(t, map[string]interface{}{ 156 "tags": ot.Tags{"error": true}, 157 }, data.Tags.Custom) 158 } 159 160 func TestSpanErrorLogKV(t *testing.T) { 161 recorder := instana.NewTestRecorder() 162 tracer := instana.NewTracerWithEverything(&instana.Options{AgentClient: alwaysReadyClient{}}, recorder) 163 defer instana.ShutdownSensor() 164 165 sp := tracer.StartSpan("test") 166 sp.LogKV("error", "simulated error") 167 sp.Finish() 168 169 spans := recorder.GetQueuedSpans() 170 require.Len(t, spans, 2) 171 172 span, logSpan := spans[0], spans[1] 173 assert.Equal(t, 1, span.Ec) 174 175 assert.Equal(t, span.TraceID, logSpan.TraceID) 176 assert.Equal(t, span.SpanID, logSpan.ParentID) 177 assert.Equal(t, "log.go", logSpan.Name) 178 179 // assert that log message has been recorded within the span interval 180 assert.GreaterOrEqual(t, logSpan.Timestamp, span.Timestamp) 181 assert.LessOrEqual(t, logSpan.Duration, span.Duration) 182 183 require.IsType(t, instana.LogSpanData{}, logSpan.Data) 184 logData := logSpan.Data.(instana.LogSpanData) 185 186 assert.Equal(t, instana.LogSpanTags{ 187 Level: "ERROR", 188 Message: `error: "simulated error"`, 189 }, logData.Tags) 190 } 191 192 func TestSpan_LogFields(t *testing.T) { 193 recorder := instana.NewTestRecorder() 194 tracer := instana.NewTracerWithEverything(&instana.Options{AgentClient: alwaysReadyClient{}}, recorder) 195 defer instana.ShutdownSensor() 196 197 examples := map[string]struct { 198 Fields []log.Field 199 ExpectedErrorCount int 200 ExpectedTags instana.LogSpanTags 201 }{ 202 "error object": { 203 Fields: []log.Field{ 204 log.Error(errors.New("simulated error")), 205 log.String("function", "ErrorFunc"), 206 }, 207 ExpectedErrorCount: 1, 208 ExpectedTags: instana.LogSpanTags{ 209 Level: "ERROR", 210 Message: `error.object: "simulated error" function: "ErrorFunc"`, 211 }, 212 }, 213 "error log": { 214 Fields: []log.Field{ 215 log.String("error.object", "simulated error"), 216 log.String("function", "ErrorFunc"), 217 }, 218 ExpectedErrorCount: 1, 219 ExpectedTags: instana.LogSpanTags{ 220 Level: "ERROR", 221 Message: `error.object: "simulated error" function: "ErrorFunc"`, 222 }, 223 }, 224 "warn log": { 225 Fields: []log.Field{ 226 log.String("warn", "simulated warning"), 227 log.String("function", "WarnFunc"), 228 }, 229 ExpectedTags: instana.LogSpanTags{ 230 Level: "WARN", 231 Message: `warn: "simulated warning" function: "WarnFunc"`, 232 }, 233 }, 234 } 235 236 for name, example := range examples { 237 t.Run(name, func(t *testing.T) { 238 sp := tracer.StartSpan("test") 239 sp.LogFields(example.Fields...) 240 sp.Finish() 241 242 spans := recorder.GetQueuedSpans() 243 require.Len(t, spans, 2) 244 245 span, logSpan := spans[0], spans[1] 246 assert.Equal(t, example.ExpectedErrorCount, span.Ec) 247 248 assert.Equal(t, span.TraceID, logSpan.TraceID) 249 assert.Equal(t, span.SpanID, logSpan.ParentID) 250 assert.Equal(t, "log.go", logSpan.Name) 251 252 // assert that log message has been recorded within the span interval 253 assert.GreaterOrEqual(t, logSpan.Timestamp, span.Timestamp) 254 assert.LessOrEqual(t, logSpan.Duration, span.Duration) 255 256 require.IsType(t, instana.LogSpanData{}, logSpan.Data) 257 logData := logSpan.Data.(instana.LogSpanData) 258 259 assert.Equal(t, example.ExpectedTags, logData.Tags) 260 }) 261 } 262 } 263 264 func TestSpan_Suppressed_StartSpanOption(t *testing.T) { 265 recorder := instana.NewTestRecorder() 266 tracer := instana.NewTracerWithEverything(&instana.Options{AgentClient: alwaysReadyClient{}}, recorder) 267 defer instana.ShutdownSensor() 268 269 sp := tracer.StartSpan("test", instana.SuppressTracing()) 270 sp.Finish() 271 272 assert.Empty(t, recorder.GetQueuedSpans()) 273 } 274 275 func TestSpan_Suppressed_SetTag(t *testing.T) { 276 recorder := instana.NewTestRecorder() 277 tracer := instana.NewTracerWithEverything(&instana.Options{AgentClient: alwaysReadyClient{}}, recorder) 278 defer instana.ShutdownSensor() 279 280 sp := tracer.StartSpan("test") 281 instana.SuppressTracing().Set(sp) 282 sp.Finish() 283 284 assert.Empty(t, recorder.GetQueuedSpans()) 285 } 286 287 func Test_tracerS_SuppressTracing(t *testing.T) { 288 opName := "my_operation" 289 suppressTracingTag := "suppress_tracing" 290 exitSpan := ext.SpanKindRPCClientEnum 291 entrySpan := ext.SpanKindRPCServerEnum 292 allowRootExitSpanEnv := "INSTANA_ALLOW_ROOT_EXIT_SPAN" 293 294 tracer := instana.NewTracerWithEverything(&instana.Options{AgentClient: alwaysReadyClient{}}, nil) 295 parentSpan := tracer.StartSpan("parent-span") 296 297 getSpanTags := func(kind ext.SpanKindEnum, suppressTracing bool) ot.Tags { 298 return ot.Tags{ 299 "span.kind": kind, 300 suppressTracingTag: suppressTracing, 301 } 302 } 303 304 type args struct { 305 operationName string 306 opts ot.StartSpanOptions 307 } 308 tests := []struct { 309 name string 310 exportEnv bool 311 args args 312 want int 313 }{ 314 { 315 name: "env_unset_suppress_false_spanType_exit", 316 exportEnv: false, 317 args: args{ 318 operationName: opName, 319 opts: ot.StartSpanOptions{ 320 Tags: getSpanTags(exitSpan, false), 321 }, 322 }, 323 want: 0, 324 }, 325 { 326 name: "env_unset_suppress_true_spanType_exit", 327 exportEnv: false, 328 args: args{ 329 operationName: opName, 330 opts: ot.StartSpanOptions{ 331 Tags: getSpanTags(exitSpan, true), 332 }, 333 }, 334 want: 0, 335 }, 336 { 337 name: "env_set_suppress_false_spanType_exit", 338 exportEnv: true, 339 args: args{ 340 operationName: opName, 341 opts: ot.StartSpanOptions{ 342 Tags: getSpanTags(exitSpan, false), 343 }, 344 }, 345 want: 1, 346 }, 347 { 348 name: "env_set_suppress_true_spanType_exit", 349 exportEnv: true, 350 args: args{ 351 operationName: opName, 352 opts: ot.StartSpanOptions{ 353 Tags: getSpanTags(exitSpan, true), 354 }, 355 }, 356 want: 0, 357 }, 358 { 359 name: "env_unset_suppress_false_spanType_entry", 360 exportEnv: false, 361 args: args{ 362 operationName: opName, 363 opts: ot.StartSpanOptions{ 364 Tags: getSpanTags(entrySpan, false), 365 }, 366 }, 367 want: 1, 368 }, 369 { 370 name: "env_unset_suppress_true_spanType_entry", 371 exportEnv: false, 372 args: args{ 373 operationName: opName, 374 opts: ot.StartSpanOptions{ 375 Tags: getSpanTags(entrySpan, true), 376 }, 377 }, 378 want: 0, 379 }, 380 { 381 name: "env_set_suppress_false_spanType_entry", 382 exportEnv: true, 383 args: args{ 384 operationName: opName, 385 opts: ot.StartSpanOptions{ 386 Tags: getSpanTags(entrySpan, false), 387 }, 388 }, 389 want: 1, 390 }, 391 { 392 name: "env_set_suppress_true_spanType_entry", 393 exportEnv: true, 394 args: args{ 395 operationName: opName, 396 opts: ot.StartSpanOptions{ 397 Tags: getSpanTags(entrySpan, true), 398 }, 399 }, 400 want: 0, 401 }, 402 { 403 name: "env_unset_suppress_false_spanType_ExitSpanButNotRoot", 404 exportEnv: false, 405 args: args{ 406 operationName: opName, 407 opts: ot.StartSpanOptions{ 408 Tags: getSpanTags(exitSpan, false), 409 References: []ot.SpanReference{ 410 ot.ChildOf(parentSpan.Context()), 411 }, 412 }, 413 }, 414 want: 1, 415 }, 416 { 417 name: "env_set_suppress_false_spanType_ExitSpanButNotRoot", 418 exportEnv: true, 419 args: args{ 420 operationName: opName, 421 opts: ot.StartSpanOptions{ 422 Tags: getSpanTags(exitSpan, false), 423 References: []ot.SpanReference{ 424 ot.ChildOf(parentSpan.Context()), 425 }, 426 }, 427 }, 428 want: 1, 429 }, 430 } 431 for _, tt := range tests { 432 t.Run(tt.name, func(t *testing.T) { 433 if tt.exportEnv { 434 os.Setenv(allowRootExitSpanEnv, "1") 435 436 defer func() { 437 os.Unsetenv(allowRootExitSpanEnv) 438 }() 439 } 440 441 recorder := instana.NewTestRecorder() 442 tracer := instana.NewTracerWithEverything(&instana.Options{AgentClient: alwaysReadyClient{}}, recorder) 443 sp := tracer.StartSpanWithOptions(tt.args.operationName, tt.args.opts) 444 sp.Finish() 445 assert.Equal(t, tt.want, len(recorder.GetQueuedSpans())) 446 }) 447 } 448 }