1// Copyright (c) 2017 Uber Technologies, Inc. 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 "errors" 19 "fmt" 20 "strconv" 21 "testing" 22 "time" 23 24 "github.com/opentracing/opentracing-go" 25 "github.com/opentracing/opentracing-go/ext" 26 "github.com/opentracing/opentracing-go/log" 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 30 "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" 31 "github.com/uber/jaeger-client-go/utils" 32) 33 34func TestThriftFirstInProcessSpan(t *testing.T) { 35 tracer, closer := NewTracer("DOOP", 36 NewConstSampler(true), 37 NewNullReporter()) 38 defer closer.Close() 39 40 sp1 := tracer.StartSpan("s1").(*Span) 41 sp2 := tracer.StartSpan("sp2", opentracing.ChildOf(sp1.Context())).(*Span) 42 sp2.Finish() 43 sp1.Finish() 44 45 tests := []struct { 46 span *Span 47 wantTags bool 48 }{ 49 {sp1, true}, 50 {sp2, false}, 51 } 52 53 for _, test := range tests { 54 var check func(assert.TestingT, interface{}, ...interface{}) bool 55 if test.wantTags { 56 check = assert.NotNil 57 } else { 58 check = assert.Nil 59 } 60 thriftSpan := BuildZipkinThrift(test.span) 61 version := findBinaryAnnotation(thriftSpan, JaegerClientVersionTagKey) 62 hostname := findBinaryAnnotation(thriftSpan, TracerHostnameTagKey) 63 check(t, version) 64 check(t, hostname) 65 } 66} 67 68func Test128bitTraceIDs(t *testing.T) { 69 tracer128, closer128 := NewTracer("OneTwentyEight", 70 NewConstSampler(true), 71 NewNullReporter(), 72 TracerOptions.Gen128Bit(true), 73 ) 74 defer closer128.Close() 75 76 tracer64, closer64 := NewTracer("SixtyFour", 77 NewConstSampler(true), 78 NewNullReporter(), 79 TracerOptions.Gen128Bit(false), 80 ) 81 defer closer64.Close() 82 83 sp1 := tracer128.StartSpan("s1").(*Span) 84 sp2 := tracer128.StartSpan("sp2", opentracing.ChildOf(sp1.Context())).(*Span) 85 sp2.Finish() 86 sp1.Finish() 87 88 thriftSpan1 := BuildZipkinThrift(sp1) 89 assert.NotNil(t, thriftSpan1.TraceIDHigh) 90 91 thriftSpan2 := BuildZipkinThrift(sp2) 92 assert.NotNil(t, thriftSpan2.TraceIDHigh) 93 94 sp3 := tracer64.StartSpan("s3").(*Span) 95 sp3.Finish() 96 thriftSpan3 := BuildZipkinThrift(sp3) 97 assert.Nil(t, thriftSpan3.TraceIDHigh) 98} 99 100func TestThriftForceSampled(t *testing.T) { 101 tracer, closer := NewTracer("DOOP", 102 NewConstSampler(false), // sample nothing 103 NewNullReporter()) 104 defer closer.Close() 105 106 sp := tracer.StartSpan("s1").(*Span) 107 ext.SamplingPriority.Set(sp, 1) 108 assert.True(t, sp.context.IsSampled()) 109 assert.True(t, sp.context.IsDebug()) 110 thriftSpan := BuildZipkinThrift(sp) 111 assert.True(t, thriftSpan.Debug) 112} 113 114func TestThriftSpanLogs(t *testing.T) { 115 tracer, closer := NewTracer("DOOP", 116 NewConstSampler(true), 117 NewNullReporter()) 118 defer closer.Close() 119 root := tracer.StartSpan("s1") 120 121 someTime := time.Now().Add(-time.Minute) 122 someTimeInt64 := utils.TimeToMicrosecondsSinceEpochInt64(someTime) 123 124 fields := func(fields ...log.Field) []log.Field { 125 return fields 126 } 127 tests := []struct { 128 fields []log.Field 129 logFunc func(sp opentracing.Span) 130 expected string 131 expectedTimestamp int64 132 disableSampling bool 133 }{ 134 {fields: fields(log.String("event", "happened")), expected: "happened"}, 135 {fields: fields(log.String("something", "happened")), expected: `{"something":"happened"}`}, 136 {fields: fields(log.Bool("something", true)), expected: `{"something":"true"}`}, 137 {fields: fields(log.Int("something", 123)), expected: `{"something":"123"}`}, 138 {fields: fields(log.Int32("something", 123)), expected: `{"something":"123"}`}, 139 {fields: fields(log.Int64("something", 123)), expected: `{"something":"123"}`}, 140 {fields: fields(log.Uint32("something", 123)), expected: `{"something":"123"}`}, 141 {fields: fields(log.Uint64("something", 123)), expected: `{"something":"123"}`}, 142 {fields: fields(log.Float32("something", 123)), expected: `{"something":"123.000000"}`}, 143 {fields: fields(log.Float64("something", 123)), expected: `{"something":"123.000000"}`}, 144 {fields: fields(log.Error(errors.New("drugs are baaad, m-k"))), 145 expected: `{"error.object":"drugs are baaad, m-k"}`}, 146 {fields: fields(log.Object("something", 123)), expected: `{"something":"123"}`}, 147 { 148 fields: fields(log.Lazy(func(fv log.Encoder) { 149 fv.EmitBool("something", true) 150 })), 151 expected: `{"something":"true"}`, 152 }, 153 { 154 logFunc: func(sp opentracing.Span) { 155 sp.LogKV("event", "something") 156 }, 157 expected: "something", 158 }, 159 { 160 logFunc: func(sp opentracing.Span) { 161 sp.LogKV("non-even number of arguments") 162 }, 163 // this is a bit fragile, but ¯\_(ツ)_/¯ 164 expected: `{"error.object":"non-even keyValues len: 1","function":"LogKV"}`, 165 }, 166 { 167 logFunc: func(sp opentracing.Span) { 168 sp.LogEvent("something") 169 }, 170 expected: "something", 171 }, 172 { 173 logFunc: func(sp opentracing.Span) { 174 sp.LogEventWithPayload("something", "payload") 175 }, 176 expected: `{"event":"something","payload":"payload"}`, 177 }, 178 { 179 logFunc: func(sp opentracing.Span) { 180 sp.Log(opentracing.LogData{Event: "something"}) 181 }, 182 expected: "something", 183 }, 184 { 185 logFunc: func(sp opentracing.Span) { 186 sp.Log(opentracing.LogData{Event: "something", Payload: "payload"}) 187 }, 188 expected: `{"event":"something","payload":"payload"}`, 189 }, 190 { 191 logFunc: func(sp opentracing.Span) { 192 sp.FinishWithOptions(opentracing.FinishOptions{ 193 LogRecords: []opentracing.LogRecord{ 194 { 195 Timestamp: someTime, 196 Fields: fields(log.String("event", "happened")), 197 }, 198 }, 199 }) 200 }, 201 expected: "happened", 202 expectedTimestamp: someTimeInt64, 203 }, 204 { 205 logFunc: func(sp opentracing.Span) { 206 sp.FinishWithOptions(opentracing.FinishOptions{ 207 BulkLogData: []opentracing.LogData{ 208 { 209 Timestamp: someTime, 210 Event: "happened", 211 }, 212 }, 213 }) 214 }, 215 expected: "happened", 216 expectedTimestamp: someTimeInt64, 217 }, 218 { 219 logFunc: func(sp opentracing.Span) { 220 sp.FinishWithOptions(opentracing.FinishOptions{ 221 BulkLogData: []opentracing.LogData{ 222 { 223 Timestamp: someTime, 224 Event: "happened", 225 Payload: "payload", 226 }, 227 }, 228 }) 229 }, 230 expected: `{"event":"happened","payload":"payload"}`, 231 expectedTimestamp: someTimeInt64, 232 }, 233 { 234 disableSampling: true, 235 fields: fields(log.String("event", "happened")), 236 expected: "", 237 }, 238 { 239 disableSampling: true, 240 logFunc: func(sp opentracing.Span) { 241 sp.LogKV("event", "something") 242 }, 243 expected: "", 244 }, 245 } 246 247 for i, test := range tests { 248 testName := fmt.Sprintf("test-%02d", i) 249 sp := tracer.StartSpan(testName, opentracing.ChildOf(root.Context())) 250 if test.disableSampling { 251 ext.SamplingPriority.Set(sp, 0) 252 } 253 if test.logFunc != nil { 254 test.logFunc(sp) 255 } else if len(test.fields) > 0 { 256 sp.LogFields(test.fields...) 257 } 258 thriftSpan := BuildZipkinThrift(sp.(*Span)) 259 if test.disableSampling { 260 assert.Equal(t, 0, len(thriftSpan.Annotations), testName) 261 continue 262 } 263 assert.Equal(t, 1, len(thriftSpan.Annotations), testName) 264 assert.Equal(t, test.expected, thriftSpan.Annotations[0].Value, testName) 265 if test.expectedTimestamp != 0 { 266 assert.Equal(t, test.expectedTimestamp, thriftSpan.Annotations[0].Timestamp, testName) 267 } 268 } 269} 270 271func TestThriftLocalComponentSpan(t *testing.T) { 272 tracer, closer := NewTracer("DOOP", 273 NewConstSampler(true), 274 NewNullReporter()) 275 defer closer.Close() 276 277 tests := []struct { 278 addComponentTag bool 279 wantAnnotation string 280 }{ 281 {false, "DOOP"}, // Without COMPONENT tag the value is the service name 282 {true, "c1"}, 283 } 284 285 for _, test := range tests { 286 sp := tracer.StartSpan("s1").(*Span) 287 if test.addComponentTag { 288 ext.Component.Set(sp, "c1") 289 } 290 sp.Finish() 291 thriftSpan := BuildZipkinThrift(sp) 292 293 anno := findBinaryAnnotation(thriftSpan, "lc") 294 require.NotNil(t, anno) 295 assert.EqualValues(t, test.wantAnnotation, anno.Value) 296 } 297} 298 299func TestSpecialTags(t *testing.T) { 300 tracer, closer := NewTracer("DOOP", 301 NewConstSampler(true), 302 NewNullReporter()) 303 defer closer.Close() 304 305 sp := tracer.StartSpan("s1").(*Span) 306 ext.SpanKindRPCServer.Set(sp) 307 ext.PeerService.Set(sp, "peer") 308 ext.PeerPort.Set(sp, 80) 309 ext.PeerHostIPv4.Set(sp, 2130706433) 310 sp.Finish() 311 312 thriftSpan := BuildZipkinThrift(sp) 313 // Special tags should not be copied over to binary annotations 314 assert.Nil(t, findBinaryAnnotation(thriftSpan, "span.kind")) 315 assert.Nil(t, findBinaryAnnotation(thriftSpan, "peer.service")) 316 assert.Nil(t, findBinaryAnnotation(thriftSpan, "peer.port")) 317 assert.Nil(t, findBinaryAnnotation(thriftSpan, "peer.ipv4")) 318 assert.Nil(t, findBinaryAnnotation(thriftSpan, "ip")) 319 320 anno := findBinaryAnnotation(thriftSpan, "ca") 321 assert.NotNil(t, anno) 322 assert.NotNil(t, anno.Host) 323 assert.EqualValues(t, 80, anno.Host.Port) 324 assert.EqualValues(t, 2130706433, anno.Host.Ipv4) 325 assert.EqualValues(t, "peer", anno.Host.ServiceName) 326 327 assert.NotNil(t, findAnnotation(thriftSpan, "sr")) 328 assert.NotNil(t, findAnnotation(thriftSpan, "ss")) 329} 330 331func TestBaggageLogs(t *testing.T) { 332 tracer, closer := NewTracer("DOOP", 333 NewConstSampler(true), 334 NewNullReporter()) 335 defer closer.Close() 336 337 sp := tracer.StartSpan("s1").(*Span) 338 sp.SetBaggageItem("auth.token", "token") 339 ext.SpanKindRPCServer.Set(sp) 340 sp.Finish() 341 342 thriftSpan := BuildZipkinThrift(sp) 343 assert.NotNil(t, findAnnotation(thriftSpan, `{"event":"baggage","key":"auth.token","value":"token"}`)) 344} 345 346func TestMaxTagValueLength(t *testing.T) { 347 value := make([]byte, 512) 348 tests := []struct { 349 tagValueLength int 350 value []byte 351 expected []byte 352 }{ 353 {256, value, value[:256]}, 354 {512, value, value}, 355 } 356 357 for _, test := range tests { 358 t.Run(strconv.Itoa(test.tagValueLength), func(t *testing.T) { 359 tracer, closer := NewTracer("DOOP", 360 NewConstSampler(true), 361 NewNullReporter(), 362 TracerOptions.MaxTagValueLength(test.tagValueLength)) 363 defer closer.Close() 364 sp := tracer.StartSpan("s1").(*Span) 365 sp.SetTag("tag.string", string(test.value)) 366 sp.SetTag("tag.bytes", test.value) 367 sp.Finish() 368 thriftSpan := BuildZipkinThrift(sp) 369 assert.Equal(t, test.expected, findBinaryAnnotation(thriftSpan, "tag.string").Value) 370 assert.Equal(t, test.expected, findBinaryAnnotation(thriftSpan, "tag.bytes").Value) 371 }) 372 } 373} 374 375func findAnnotation(span *zipkincore.Span, name string) *zipkincore.Annotation { 376 for _, a := range span.Annotations { 377 if a.Value == name { 378 return a 379 } 380 } 381 return nil 382} 383 384func findBinaryAnnotation(span *zipkincore.Span, name string) *zipkincore.BinaryAnnotation { 385 for _, a := range span.BinaryAnnotations { 386 if a.Key == name { 387 return a 388 } 389 } 390 return nil 391} 392