1// Copyright (c) 2019 The Jaeger 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 15package jaeger 16 17import ( 18 "fmt" 19 "testing" 20 21 "github.com/opentracing/opentracing-go" 22 "github.com/opentracing/opentracing-go/ext" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25) 26 27var ( 28 _ Sampler = new(SamplerV2Base) 29 _ SamplerV2 = new(ConstSampler) 30 _ SamplerV2 = new(ProbabilisticSampler) 31 _ SamplerV2 = new(RateLimitingSampler) 32 _ SamplerV2 = new(PerOperationSampler) 33 // Note: GuaranteedThroughputProbabilisticSampler is currently not V2 34 35 _ Sampler = new(retryableTestSampler) 36 _ SamplerV2 = new(retryableTestSampler) 37) 38 39// retryableTestSampler leaves the sampling unfinalized (retryable) after OnCreateSpan, 40// and makes it final after OnSetOperationName. 41type retryableTestSampler struct { 42 SamplerV2Base 43 decision bool 44 tags []Tag 45} 46 47func newRetryableSampler(decision bool) *retryableTestSampler { 48 return &retryableTestSampler{ 49 decision: decision, 50 tags: makeSamplerTags("retryable", decision), 51 } 52} 53 54func (s *retryableTestSampler) OnCreateSpan(span *Span) SamplingDecision { 55 return SamplingDecision{Sample: s.decision, Retryable: true, Tags: s.tags} 56} 57 58func (s *retryableTestSampler) OnSetOperationName(span *Span, operationName string) SamplingDecision { 59 return SamplingDecision{Sample: s.decision, Retryable: false, Tags: s.tags} 60} 61 62func (s *retryableTestSampler) OnSetTag(span *Span, key string, value interface{}) SamplingDecision { 63 return SamplingDecision{Sample: s.decision, Retryable: true, Tags: s.tags} 64} 65 66func (s *retryableTestSampler) OnFinishSpan(span *Span) SamplingDecision { 67 return SamplingDecision{Sample: s.decision, Retryable: true, Tags: s.tags} 68} 69 70func TestSpanRemainsWriteable(t *testing.T) { 71 tracer, closer := NewTracer("test-service", newRetryableSampler(false), NewNullReporter()) 72 defer closer.Close() 73 74 span := tracer.StartSpan("op1").(*Span) 75 assert.True(t, span.context.isWriteable(), "span is writeable when created") 76 assert.False(t, span.context.isSamplingFinalized(), "span is not finalized when created") 77 78 span.SetTag("k1", "v1") 79 assert.True(t, span.context.isWriteable(), "span is writeable after setting tags") 80 assert.Equal(t, opentracing.Tags{"k1": "v1"}, span.Tags()) 81 82 span.SetOperationName("op2") 83 assert.False(t, span.context.isWriteable(), "span is not writeable after sampler returns retryable=true") 84 assert.True(t, span.context.isSamplingFinalized(), "span is finalized after sampler returns retryable=true") 85} 86 87func TestSpanSharesSamplingStateWithChildren(t *testing.T) { 88 tracer, closer := NewTracer("test-service", newRetryableSampler(false), NewNullReporter()) 89 defer closer.Close() 90 91 sp1 := tracer.StartSpan("op1").(*Span) 92 assert.False(t, sp1.context.isSamplingFinalized(), "span is not finalized when created") 93 94 sp2 := tracer.StartSpan("op2", opentracing.ChildOf(sp1.Context())).(*Span) 95 assert.False(t, sp2.context.isSamplingFinalized(), "child span is not finalized when created") 96 97 sp2.SetOperationName("op3") 98 assert.True(t, sp2.context.isSamplingFinalized(), "child span is finalized after changing operation name") 99 assert.True(t, sp1.context.isSamplingFinalized(), "parent span is also finalized after child is finalized") 100} 101 102func TestSamplingIsFinalizedOnSamplingPriorityTag(t *testing.T) { 103 tracer, closer := NewTracer("test-service", newRetryableSampler(false), NewNullReporter()) 104 defer closer.Close() 105 106 tests := []struct { 107 name string 108 priority uint16 109 expectedSampled bool 110 expectedTags opentracing.Tags 111 }{ 112 { 113 name: "sampling.priority=1", 114 priority: 1, 115 expectedSampled: true, 116 expectedTags: opentracing.Tags{"sampling.priority": uint16(1)}, 117 }, 118 { 119 name: "sampling.priority=0", 120 expectedSampled: false, 121 priority: 0, 122 expectedTags: opentracing.Tags{}, 123 }, 124 } 125 for _, tt := range tests { 126 t.Run(tt.name, func(t *testing.T) { 127 span := tracer.StartSpan("op1").(*Span) 128 assert.False(t, span.context.isSamplingFinalized(), "span is not finalized when created") 129 130 ext.SamplingPriority.Set(span, tt.priority) 131 assert.True(t, span.context.isSamplingFinalized(), "span is finalized after sampling.priority tag") 132 assert.Equal(t, tt.expectedSampled, span.context.IsSampled(), "span is sampled after sampling.priority tag") 133 assert.Equal(t, tt.expectedTags, span.Tags(), "sampling.priority tag in the span") 134 }) 135 } 136} 137 138func TestSamplingIsFinalizedWithV1Samplers(t *testing.T) { 139 probabilistic, err := NewProbabilisticSampler(0.5) 140 require.NoError(t, err) 141 samplers := []Sampler{ 142 NewConstSampler(false), 143 probabilistic, 144 NewRateLimitingSampler(1), 145 } 146 for _, sampler := range samplers { 147 t.Run(fmt.Sprintf("%s", sampler), func(t *testing.T) { 148 tracer, closer := NewTracer("test-service", sampler, NewNullReporter()) 149 defer closer.Close() 150 151 span := tracer.StartSpan("op1").(*Span) 152 assert.True(t, span.context.isSamplingFinalized(), "span is finalized when created with V1 sampler") 153 }) 154 } 155} 156 157func TestSamplingIsNotFinalizedWhenContextIsInjected(t *testing.T) { 158 tracer, closer := NewTracer("test-service", newRetryableSampler(false), NewNullReporter()) 159 defer closer.Close() 160 161 span := tracer.StartSpan("op1").(*Span) 162 assert.False(t, span.context.isSamplingFinalized(), "span is not finalized when created") 163 164 err := tracer.Inject(span.Context(), opentracing.TextMap, opentracing.TextMapCarrier{}) 165 require.NoError(t, err) 166 167 assert.False(t, span.context.isSamplingFinalized(), "span is not finalized after context is serialized") 168} 169 170func TestSamplingIsFinalizedInChildSpanOfRemoteParent(t *testing.T) { 171 for _, sampled := range []bool{false, true} { 172 t.Run(fmt.Sprintf("sampled=%t", sampled), func(t *testing.T) { 173 tracer, closer := NewTracer("test-service", newRetryableSampler(sampled), NewNullReporter()) 174 defer closer.Close() 175 176 span := tracer.StartSpan("parent").(*Span) 177 assert.False(t, span.context.isSamplingFinalized(), "span is not finalized when created") 178 assert.Equal(t, sampled, span.context.IsSampled()) 179 180 carrier := opentracing.TextMapCarrier{} 181 err := tracer.Inject(span.Context(), opentracing.TextMap, carrier) 182 require.NoError(t, err) 183 184 parentContext, err := tracer.Extract(opentracing.TextMap, carrier) 185 require.NoError(t, err) 186 childSpan := tracer.StartSpan("child", opentracing.ChildOf(parentContext)).(*Span) 187 assert.True(t, childSpan.context.isSamplingFinalized(), "child span is finalized") 188 assert.Equal(t, sampled, childSpan.context.IsSampled()) 189 }) 190 } 191} 192 193func TestSpanIsWriteableIfSampledOrNotFinalized(t *testing.T) { 194 tracer, closer := NewTracer("test-service", newRetryableSampler(false), NewNullReporter()) 195 defer closer.Close() 196 197 span := tracer.StartSpan("span").(*Span) 198 assert.False(t, span.context.isSamplingFinalized(), "span is not finalized when created") 199 assert.False(t, span.context.IsSampled(), "span is not sampled") 200 assert.True(t, span.context.isWriteable(), "span is writeable") 201 202 tracer.(*Tracer).sampler = NewConstSampler(true) 203 span = tracer.StartSpan("span").(*Span) 204 assert.True(t, span.context.isSamplingFinalized(), "span is finalized when created") 205 assert.True(t, span.context.IsSampled(), "span is sampled") 206 assert.True(t, span.context.isWriteable(), "span is writeable") 207} 208 209func TestSetOperationOverridesOperationOnSampledSpan(t *testing.T) { 210 tracer, closer := NewTracer("test-service", NewConstSampler(true), NewNullReporter()) 211 defer closer.Close() 212 213 span := tracer.StartSpan("op1").(*Span) 214 assert.True(t, span.context.isSamplingFinalized(), "span is finalized when created") 215 assert.True(t, span.context.IsSampled(), "span is sampled") 216 assert.Equal(t, "op1", span.OperationName()) 217 assert.Equal(t, makeSamplerTags("const", true), span.tags) 218 219 span.tags = []Tag{} // easier to check that tags are not re-added 220 span.SetOperationName("op2") 221 assert.True(t, span.context.isSamplingFinalized(), "span is finalized when created") 222 assert.True(t, span.context.IsSampled(), "span is sampled") 223 assert.Equal(t, "op2", span.OperationName()) 224 assert.Equal(t, []Tag{}, span.tags, "sampling tags are not re-added") 225} 226