github.com/thanos-io/thanos@v0.32.5/pkg/tracing/jaeger/jaeger_test.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package jaeger 5 6 import ( 7 "context" 8 "os" 9 "strings" 10 "testing" 11 12 "github.com/efficientgo/core/testutil" 13 "github.com/opentracing/opentracing-go" 14 "github.com/thanos-io/thanos/pkg/tracing" 15 "github.com/thanos-io/thanos/pkg/tracing/migration" 16 17 "github.com/go-kit/log" 18 "go.opentelemetry.io/otel/attribute" 19 tracesdk "go.opentelemetry.io/otel/sdk/trace" 20 "go.opentelemetry.io/otel/sdk/trace/tracetest" 21 ) 22 23 var parentConfig = ParentBasedSamplerConfig{LocalParentSampled: true} 24 25 // This test shows that if sample factor will enable tracing on client process, even when it would be disabled on server 26 // it will be still enabled for all spans within this span. 27 func TestContextTracing_ClientEnablesProbabilisticTracing(t *testing.T) { 28 exp := tracetest.NewInMemoryExporter() 29 config := Config{ 30 SamplerType: "probabilistic", 31 SamplerParam: 1.0, 32 SamplerParentConfig: parentConfig, 33 } 34 sampler := getSampler(config) 35 36 tracerOtel := newTraceProvider( 37 context.Background(), 38 "tracerOtel", 39 log.NewNopLogger(), 40 tracesdk.NewSimpleSpanProcessor(exp), 41 sampler, 42 []attribute.KeyValue{}, 43 ) 44 tracer, _ := migration.Bridge(tracerOtel, log.NewNopLogger()) 45 clientRoot, clientCtx := tracing.StartSpan(tracing.ContextWithTracer(context.Background(), tracer), "a") 46 47 config.SamplerParam = 0.0 48 sampler2 := getSampler(config) 49 // Simulate Server process with different tracer, but with client span in context. 50 srvTracerOtel := newTraceProvider( 51 context.Background(), 52 "srvTracerOtel", 53 log.NewNopLogger(), 54 tracesdk.NewSimpleSpanProcessor(exp), 55 sampler2, // never sample 56 []attribute.KeyValue{}, 57 ) 58 srvTracer, _ := migration.Bridge(srvTracerOtel, log.NewNopLogger()) 59 60 srvRoot, srvCtx := tracing.StartSpan(tracing.ContextWithTracer(clientCtx, srvTracer), "b") 61 srvChild, _ := tracing.StartSpan(srvCtx, "bb") 62 63 tracing.CountSpans_ClientEnablesTracing(t, exp, clientRoot, srvRoot, srvChild) 64 } 65 66 // This test shows that if sample factor will disable tracing on client process, when it would be enabled on server 67 // it will be still disabled for all spans within this span. 68 func TestContextTracing_ClientDisablesProbabilisticTracing(t *testing.T) { 69 exp := tracetest.NewInMemoryExporter() 70 71 config := Config{ 72 SamplerType: "probabilistic", 73 SamplerParam: 0.0, 74 SamplerParentConfig: parentConfig, 75 } 76 sampler := getSampler(config) 77 tracerOtel := newTraceProvider( 78 context.Background(), 79 "tracerOtel", 80 log.NewNopLogger(), 81 tracesdk.NewSimpleSpanProcessor(exp), 82 sampler, // never sample 83 []attribute.KeyValue{}, 84 ) 85 tracer, _ := migration.Bridge(tracerOtel, log.NewNopLogger()) 86 87 clientRoot, clientCtx := tracing.StartSpan(tracing.ContextWithTracer(context.Background(), tracer), "a") 88 89 config.SamplerParam = 1.0 90 sampler2 := getSampler(config) 91 // Simulate Server process with different tracer, but with client span in context. 92 srvTracerOtel := newTraceProvider( 93 context.Background(), 94 "srvTracerOtel", 95 log.NewNopLogger(), 96 tracesdk.NewSimpleSpanProcessor(exp), 97 sampler2, // never sample 98 []attribute.KeyValue{}, 99 ) 100 srvTracer, _ := migration.Bridge(srvTracerOtel, log.NewNopLogger()) 101 102 srvRoot, srvCtx := tracing.StartSpan(tracing.ContextWithTracer(clientCtx, srvTracer), "b") 103 srvChild, _ := tracing.StartSpan(srvCtx, "bb") 104 105 tracing.ContextTracing_ClientDisablesTracing(t, exp, clientRoot, srvRoot, srvChild) 106 } 107 108 func TestContextTracing_ClientDisablesAlwaysOnSampling(t *testing.T) { 109 exp := tracetest.NewInMemoryExporter() 110 111 config := Config{ 112 SamplerType: SamplerTypeConstant, 113 SamplerParam: 0, 114 } 115 sampler := getSampler(config) 116 tracerOtel := newTraceProvider( 117 context.Background(), 118 "tracerOtel", 119 log.NewNopLogger(), 120 tracesdk.NewSimpleSpanProcessor(exp), 121 sampler, // never sample 122 []attribute.KeyValue{}, 123 ) 124 tracer, _ := migration.Bridge(tracerOtel, log.NewNopLogger()) 125 126 clientRoot, clientCtx := tracing.StartSpan(tracing.ContextWithTracer(context.Background(), tracer), "a") 127 128 config.SamplerParam = 1 129 sampler2 := getSampler(config) 130 // Simulate Server process with different tracer, but with client span in context. 131 srvTracerOtel := newTraceProvider( 132 context.Background(), 133 "srvTracerOtel", 134 log.NewNopLogger(), 135 tracesdk.NewSimpleSpanProcessor(exp), 136 sampler2, // never sample 137 []attribute.KeyValue{}, 138 ) 139 srvTracer, _ := migration.Bridge(srvTracerOtel, log.NewNopLogger()) 140 141 srvRoot, srvCtx := tracing.StartSpan(tracing.ContextWithTracer(clientCtx, srvTracer), "b") 142 srvChild, _ := tracing.StartSpan(srvCtx, "bb") 143 144 tracing.ContextTracing_ClientDisablesTracing(t, exp, clientRoot, srvRoot, srvChild) 145 } 146 147 // This test shows that if span will contain special baggage (for example from special HTTP header), even when sample 148 // factor will disable client & server tracing, it will be still enabled for all spans within this span. 149 func TestContextTracing_ForceTracing(t *testing.T) { 150 exp := tracetest.NewInMemoryExporter() 151 config := Config{ 152 SamplerType: "probabilistic", 153 SamplerParam: 0.0, 154 SamplerParentConfig: parentConfig, 155 } 156 sampler := getSampler(config) 157 tracerOtel := newTraceProvider( 158 context.Background(), 159 "tracerOtel", 160 log.NewNopLogger(), 161 tracesdk.NewSimpleSpanProcessor(exp), 162 sampler, 163 []attribute.KeyValue{}, 164 ) 165 tracer, _ := migration.Bridge(tracerOtel, log.NewNopLogger()) 166 167 // Start the root span with the tag to force tracing. 168 clientRoot, clientCtx := tracing.StartSpan( 169 tracing.ContextWithTracer(context.Background(), tracer), 170 "a", 171 opentracing.Tag{Key: migration.ForceTracingAttributeKey, Value: "true"}, 172 ) 173 174 // Simulate Server process with different tracer, but with client span in context. 175 srvTracerOtel := newTraceProvider( 176 context.Background(), 177 "srvTracerOtel", 178 log.NewNopLogger(), 179 tracesdk.NewSimpleSpanProcessor(exp), 180 sampler, 181 []attribute.KeyValue{}, 182 ) 183 srvTracer, _ := migration.Bridge(srvTracerOtel, log.NewNopLogger()) 184 185 srvRoot, srvCtx := tracing.StartSpan(tracing.ContextWithTracer(clientCtx, srvTracer), "b") 186 srvChild, _ := tracing.StartSpan(srvCtx, "bb") 187 188 tracing.ContextTracing_ForceTracing(t, exp, clientRoot, srvRoot, srvChild) 189 } 190 191 func TestParseTags(t *testing.T) { 192 for _, tcase := range []struct { 193 input string 194 expected []attribute.KeyValue 195 }{ 196 { 197 input: "key=value", 198 expected: []attribute.KeyValue{attribute.String("key", "value")}, 199 }, 200 { 201 input: "key1=value1,key2=value2", 202 expected: []attribute.KeyValue{attribute.String("key1", "value1"), 203 attribute.String("key2", "value2")}, 204 }, 205 { 206 input: "", 207 expected: []attribute.KeyValue{}, 208 }, 209 { 210 // Incorrectly formatted string with leading comma still yields the right tags. 211 input: ",key=value", 212 expected: []attribute.KeyValue{attribute.String("key", "value")}, 213 }, 214 { 215 // Incorrectly formatted string with trailing comma still yields the right tags. 216 input: "key=value,", 217 expected: []attribute.KeyValue{attribute.String("key", "value")}, 218 }, 219 { 220 // Leading and trailing spaces in tags are trimmed. 221 input: " key=value ", 222 expected: []attribute.KeyValue{attribute.String("key", "value")}, 223 }, 224 { 225 input: "key=${env:default_val}", 226 expected: []attribute.KeyValue{attribute.String("key", "default_val")}, 227 }, 228 } { 229 if ok := t.Run("", func(t *testing.T) { 230 exists := false 231 envVal := "" 232 envVar := "" 233 // Check if env vars are used. 234 if strings.Contains(tcase.input, "${") { 235 envVal, envVar, exists = extractValueOfEnvVar(tcase.input) 236 // Set a temporary value just for testing. 237 tempEnvVal := "temp_val" 238 os.Setenv(envVar, tempEnvVal) 239 tcase.expected = []attribute.KeyValue{attribute.String("key", tempEnvVal)} 240 } 241 attrs := parseTags(tcase.input) 242 testutil.Equals(t, tcase.expected, attrs) 243 244 // Reset the env var to the old value, if needed. 245 if exists { 246 os.Setenv(envVar, envVal) 247 } 248 }); !ok { 249 return 250 } 251 } 252 } 253 254 func extractValueOfEnvVar(input string) (string, string, bool) { 255 kv := strings.SplitN(input, "=", 2) 256 _, v := strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]) 257 258 if strings.HasPrefix(v, "${") && strings.HasSuffix(v, "}") { 259 ed := strings.SplitN(v[2:len(v)-1], ":", 2) 260 e, d := ed[0], ed[1] 261 envVal, exists := os.LookupEnv(e) 262 if !exists { 263 return d, e, exists 264 } 265 return envVal, e, exists 266 } 267 268 return "", "", false 269 }