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  }