...

Source file src/go.opentelemetry.io/otel/sdk/trace/sampling_test.go

Documentation: go.opentelemetry.io/otel/sdk/trace

     1  // Copyright The OpenTelemetry Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package trace
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"math/rand"
    21  	"testing"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  
    26  	"go.opentelemetry.io/otel/trace"
    27  )
    28  
    29  func TestParentBasedDefaultLocalParentSampled(t *testing.T) {
    30  	sampler := ParentBased(AlwaysSample())
    31  	traceID, _ := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
    32  	spanID, _ := trace.SpanIDFromHex("00f067aa0ba902b7")
    33  	parentCtx := trace.ContextWithSpanContext(
    34  		context.Background(),
    35  		trace.NewSpanContext(trace.SpanContextConfig{
    36  			TraceID:    traceID,
    37  			SpanID:     spanID,
    38  			TraceFlags: trace.FlagsSampled,
    39  		}),
    40  	)
    41  	if sampler.ShouldSample(SamplingParameters{ParentContext: parentCtx}).Decision != RecordAndSample {
    42  		t.Error("Sampling decision should be RecordAndSample")
    43  	}
    44  }
    45  
    46  func TestParentBasedDefaultLocalParentNotSampled(t *testing.T) {
    47  	sampler := ParentBased(AlwaysSample())
    48  	traceID, _ := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
    49  	spanID, _ := trace.SpanIDFromHex("00f067aa0ba902b7")
    50  	parentCtx := trace.ContextWithSpanContext(
    51  		context.Background(),
    52  		trace.NewSpanContext(trace.SpanContextConfig{
    53  			TraceID: traceID,
    54  			SpanID:  spanID,
    55  		}),
    56  	)
    57  	if sampler.ShouldSample(SamplingParameters{ParentContext: parentCtx}).Decision != Drop {
    58  		t.Error("Sampling decision should be Drop")
    59  	}
    60  }
    61  
    62  func TestParentBasedWithNoParent(t *testing.T) {
    63  	params := SamplingParameters{}
    64  
    65  	sampler := ParentBased(AlwaysSample())
    66  	if sampler.ShouldSample(params).Decision != RecordAndSample {
    67  		t.Error("Sampling decision should be RecordAndSample")
    68  	}
    69  
    70  	sampler = ParentBased(NeverSample())
    71  	if sampler.ShouldSample(params).Decision != Drop {
    72  		t.Error("Sampling decision should be Drop")
    73  	}
    74  }
    75  
    76  func TestParentBasedWithSamplerOptions(t *testing.T) {
    77  	testCases := []struct {
    78  		name                            string
    79  		samplerOption                   ParentBasedSamplerOption
    80  		isParentRemote, isParentSampled bool
    81  		expectedDecision                SamplingDecision
    82  	}{
    83  		{
    84  			"localParentSampled",
    85  			WithLocalParentSampled(NeverSample()),
    86  			false,
    87  			true,
    88  			Drop,
    89  		},
    90  		{
    91  			"localParentNotSampled",
    92  			WithLocalParentNotSampled(AlwaysSample()),
    93  			false,
    94  			false,
    95  			RecordAndSample,
    96  		},
    97  		{
    98  			"remoteParentSampled",
    99  			WithRemoteParentSampled(NeverSample()),
   100  			true,
   101  			true,
   102  			Drop,
   103  		},
   104  		{
   105  			"remoteParentNotSampled",
   106  			WithRemoteParentNotSampled(AlwaysSample()),
   107  			true,
   108  			false,
   109  			RecordAndSample,
   110  		},
   111  	}
   112  
   113  	for _, tc := range testCases {
   114  		t.Run(tc.name, func(t *testing.T) {
   115  			traceID, _ := trace.TraceIDFromHex("4bf92f3577b34da6a3ce929d0e0e4736")
   116  			spanID, _ := trace.SpanIDFromHex("00f067aa0ba902b7")
   117  			pscc := trace.SpanContextConfig{
   118  				TraceID: traceID,
   119  				SpanID:  spanID,
   120  				Remote:  tc.isParentRemote,
   121  			}
   122  			if tc.isParentSampled {
   123  				pscc.TraceFlags = trace.FlagsSampled
   124  			}
   125  
   126  			params := SamplingParameters{
   127  				ParentContext: trace.ContextWithSpanContext(
   128  					context.Background(),
   129  					trace.NewSpanContext(pscc),
   130  				),
   131  			}
   132  
   133  			sampler := ParentBased(
   134  				nil,
   135  				tc.samplerOption,
   136  			)
   137  
   138  			var wantStr, gotStr string
   139  			switch tc.expectedDecision {
   140  			case RecordAndSample:
   141  				wantStr = "RecordAndSample"
   142  			case Drop:
   143  				wantStr = "Drop"
   144  			default:
   145  				wantStr = "unknown"
   146  			}
   147  
   148  			actualDecision := sampler.ShouldSample(params).Decision
   149  			switch actualDecision {
   150  			case RecordAndSample:
   151  				gotStr = "RecordAndSample"
   152  			case Drop:
   153  				gotStr = "Drop"
   154  			default:
   155  				gotStr = "unknown"
   156  			}
   157  
   158  			assert.Equalf(t, tc.expectedDecision, actualDecision, "want %s, got %s", wantStr, gotStr)
   159  		})
   160  	}
   161  }
   162  
   163  func TestParentBasedDefaultDescription(t *testing.T) {
   164  	sampler := ParentBased(AlwaysSample())
   165  
   166  	expectedDescription := fmt.Sprintf("ParentBased{root:%s,remoteParentSampled:%s,"+
   167  		"remoteParentNotSampled:%s,localParentSampled:%s,localParentNotSampled:%s}",
   168  		AlwaysSample().Description(),
   169  		AlwaysSample().Description(),
   170  		NeverSample().Description(),
   171  		AlwaysSample().Description(),
   172  		NeverSample().Description())
   173  
   174  	if sampler.Description() != expectedDescription {
   175  		t.Errorf("Sampler description should be %s, got '%s' instead",
   176  			expectedDescription,
   177  			sampler.Description(),
   178  		)
   179  	}
   180  }
   181  
   182  // TraceIDRatioBased sampler requirements state
   183  //
   184  //	"A TraceIDRatioBased sampler with a given sampling rate MUST also sample
   185  //	 all traces that any TraceIDRatioBased sampler with a lower sampling rate
   186  //	 would sample."
   187  func TestTraceIdRatioSamplesInclusively(t *testing.T) {
   188  	const (
   189  		numSamplers = 1000
   190  		numTraces   = 100
   191  	)
   192  	idg := defaultIDGenerator()
   193  
   194  	for i := 0; i < numSamplers; i++ {
   195  		ratioLo, ratioHi := rand.Float64(), rand.Float64()
   196  		if ratioHi < ratioLo {
   197  			ratioLo, ratioHi = ratioHi, ratioLo
   198  		}
   199  		samplerHi := TraceIDRatioBased(ratioHi)
   200  		samplerLo := TraceIDRatioBased(ratioLo)
   201  		for j := 0; j < numTraces; j++ {
   202  			traceID, _ := idg.NewIDs(context.Background())
   203  
   204  			params := SamplingParameters{TraceID: traceID}
   205  			if samplerLo.ShouldSample(params).Decision == RecordAndSample {
   206  				require.Equal(t, RecordAndSample, samplerHi.ShouldSample(params).Decision,
   207  					"%s sampled but %s did not", samplerLo.Description(), samplerHi.Description())
   208  			}
   209  		}
   210  	}
   211  }
   212  
   213  func TestTracestateIsPassed(t *testing.T) {
   214  	testCases := []struct {
   215  		name    string
   216  		sampler Sampler
   217  	}{
   218  		{
   219  			"notSampled",
   220  			NeverSample(),
   221  		},
   222  		{
   223  			"sampled",
   224  			AlwaysSample(),
   225  		},
   226  		{
   227  			"parentSampled",
   228  			ParentBased(AlwaysSample()),
   229  		},
   230  		{
   231  			"parentNotSampled",
   232  			ParentBased(NeverSample()),
   233  		},
   234  		{
   235  			"traceIDRatioSampler",
   236  			TraceIDRatioBased(.5),
   237  		},
   238  	}
   239  
   240  	for _, tc := range testCases {
   241  		t.Run(tc.name, func(t *testing.T) {
   242  			traceState, err := trace.ParseTraceState("k=v")
   243  			if err != nil {
   244  				t.Error(err)
   245  			}
   246  
   247  			params := SamplingParameters{
   248  				ParentContext: trace.ContextWithSpanContext(
   249  					context.Background(),
   250  					trace.NewSpanContext(trace.SpanContextConfig{
   251  						TraceState: traceState,
   252  					}),
   253  				),
   254  			}
   255  
   256  			require.Equal(t, traceState, tc.sampler.ShouldSample(params).Tracestate, "TraceState is not equal")
   257  		})
   258  	}
   259  }
   260  

View as plain text