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 15package opentracing // import "go.opentelemetry.io/otel/bridge/opentracing" 16 17import ( 18 "context" 19 "fmt" 20 "net/http" 21 "strings" 22 "sync" 23 24 ot "github.com/opentracing/opentracing-go" 25 otext "github.com/opentracing/opentracing-go/ext" 26 otlog "github.com/opentracing/opentracing-go/log" 27 28 "go.opentelemetry.io/otel" 29 "go.opentelemetry.io/otel/attribute" 30 "go.opentelemetry.io/otel/bridge/opentracing/migration" 31 "go.opentelemetry.io/otel/codes" 32 "go.opentelemetry.io/otel/internal/baggage" 33 "go.opentelemetry.io/otel/internal/trace/noop" 34 otelparent "go.opentelemetry.io/otel/internal/trace/parent" 35 "go.opentelemetry.io/otel/propagation" 36 "go.opentelemetry.io/otel/trace" 37) 38 39type bridgeSpanContext struct { 40 baggageItems baggage.Map 41 otelSpanContext trace.SpanContext 42} 43 44var _ ot.SpanContext = &bridgeSpanContext{} 45 46func newBridgeSpanContext(otelSpanContext trace.SpanContext, parentOtSpanContext ot.SpanContext) *bridgeSpanContext { 47 bCtx := &bridgeSpanContext{ 48 baggageItems: baggage.NewEmptyMap(), 49 otelSpanContext: otelSpanContext, 50 } 51 if parentOtSpanContext != nil { 52 parentOtSpanContext.ForeachBaggageItem(func(key, value string) bool { 53 bCtx.setBaggageItem(key, value) 54 return true 55 }) 56 } 57 return bCtx 58} 59 60func (c *bridgeSpanContext) ForeachBaggageItem(handler func(k, v string) bool) { 61 c.baggageItems.Foreach(func(kv attribute.KeyValue) bool { 62 return handler(string(kv.Key), kv.Value.Emit()) 63 }) 64} 65 66func (c *bridgeSpanContext) setBaggageItem(restrictedKey, value string) { 67 crk := http.CanonicalHeaderKey(restrictedKey) 68 c.baggageItems = c.baggageItems.Apply(baggage.MapUpdate{SingleKV: attribute.String(crk, value)}) 69} 70 71func (c *bridgeSpanContext) baggageItem(restrictedKey string) string { 72 crk := http.CanonicalHeaderKey(restrictedKey) 73 val, _ := c.baggageItems.Value(attribute.Key(crk)) 74 return val.Emit() 75} 76 77type bridgeSpan struct { 78 otelSpan trace.Span 79 ctx *bridgeSpanContext 80 tracer *BridgeTracer 81 skipDeferHook bool 82 extraBaggageItems map[string]string 83} 84 85var _ ot.Span = &bridgeSpan{} 86 87func newBridgeSpan(otelSpan trace.Span, bridgeSC *bridgeSpanContext, tracer *BridgeTracer) *bridgeSpan { 88 return &bridgeSpan{ 89 otelSpan: otelSpan, 90 ctx: bridgeSC, 91 tracer: tracer, 92 skipDeferHook: false, 93 extraBaggageItems: nil, 94 } 95} 96 97func (s *bridgeSpan) Finish() { 98 s.otelSpan.End() 99} 100 101func (s *bridgeSpan) FinishWithOptions(opts ot.FinishOptions) { 102 var otelOpts []trace.SpanOption 103 104 if !opts.FinishTime.IsZero() { 105 otelOpts = append(otelOpts, trace.WithTimestamp(opts.FinishTime)) 106 } 107 for _, record := range opts.LogRecords { 108 s.logRecord(record) 109 } 110 for _, data := range opts.BulkLogData { 111 s.logRecord(data.ToLogRecord()) 112 } 113 s.otelSpan.End(otelOpts...) 114} 115 116func (s *bridgeSpan) logRecord(record ot.LogRecord) { 117 s.otelSpan.AddEvent( 118 "", 119 trace.WithTimestamp(record.Timestamp), 120 trace.WithAttributes(otLogFieldsToOTelLabels(record.Fields)...), 121 ) 122} 123 124func (s *bridgeSpan) Context() ot.SpanContext { 125 return s.ctx 126} 127 128func (s *bridgeSpan) SetOperationName(operationName string) ot.Span { 129 s.otelSpan.SetName(operationName) 130 return s 131} 132 133// SetTag method adds a tag to the span. 134// 135// Note about the following value conversions: 136// - int -> int64 137// - uint -> string 138// - int32 -> int64 139// - uint32 -> int64 140// - uint64 -> string 141// - float32 -> float64 142func (s *bridgeSpan) SetTag(key string, value interface{}) ot.Span { 143 switch key { 144 case string(otext.SpanKind): 145 // TODO: Should we ignore it? 146 case string(otext.Error): 147 if b, ok := value.(bool); ok && b { 148 s.otelSpan.SetStatus(codes.Error, "") 149 } 150 default: 151 s.otelSpan.SetAttributes(otTagToOTelLabel(key, value)) 152 } 153 return s 154} 155 156func (s *bridgeSpan) LogFields(fields ...otlog.Field) { 157 s.otelSpan.AddEvent( 158 "", 159 trace.WithAttributes(otLogFieldsToOTelLabels(fields)...), 160 ) 161} 162 163type bridgeFieldEncoder struct { 164 pairs []attribute.KeyValue 165} 166 167var _ otlog.Encoder = &bridgeFieldEncoder{} 168 169func (e *bridgeFieldEncoder) EmitString(key, value string) { 170 e.emitCommon(key, value) 171} 172 173func (e *bridgeFieldEncoder) EmitBool(key string, value bool) { 174 e.emitCommon(key, value) 175} 176 177func (e *bridgeFieldEncoder) EmitInt(key string, value int) { 178 e.emitCommon(key, value) 179} 180 181func (e *bridgeFieldEncoder) EmitInt32(key string, value int32) { 182 e.emitCommon(key, value) 183} 184 185func (e *bridgeFieldEncoder) EmitInt64(key string, value int64) { 186 e.emitCommon(key, value) 187} 188 189func (e *bridgeFieldEncoder) EmitUint32(key string, value uint32) { 190 e.emitCommon(key, value) 191} 192 193func (e *bridgeFieldEncoder) EmitUint64(key string, value uint64) { 194 e.emitCommon(key, value) 195} 196 197func (e *bridgeFieldEncoder) EmitFloat32(key string, value float32) { 198 e.emitCommon(key, value) 199} 200 201func (e *bridgeFieldEncoder) EmitFloat64(key string, value float64) { 202 e.emitCommon(key, value) 203} 204 205func (e *bridgeFieldEncoder) EmitObject(key string, value interface{}) { 206 e.emitCommon(key, value) 207} 208 209func (e *bridgeFieldEncoder) EmitLazyLogger(value otlog.LazyLogger) { 210 value(e) 211} 212 213func (e *bridgeFieldEncoder) emitCommon(key string, value interface{}) { 214 e.pairs = append(e.pairs, otTagToOTelLabel(key, value)) 215} 216 217func otLogFieldsToOTelLabels(fields []otlog.Field) []attribute.KeyValue { 218 encoder := &bridgeFieldEncoder{} 219 for _, field := range fields { 220 field.Marshal(encoder) 221 } 222 return encoder.pairs 223} 224 225func (s *bridgeSpan) LogKV(alternatingKeyValues ...interface{}) { 226 fields, err := otlog.InterleavedKVToFields(alternatingKeyValues...) 227 if err != nil { 228 return 229 } 230 s.LogFields(fields...) 231} 232 233func (s *bridgeSpan) SetBaggageItem(restrictedKey, value string) ot.Span { 234 s.updateOTelContext(restrictedKey, value) 235 s.setBaggageItemOnly(restrictedKey, value) 236 return s 237} 238 239func (s *bridgeSpan) setBaggageItemOnly(restrictedKey, value string) { 240 s.ctx.setBaggageItem(restrictedKey, value) 241} 242 243func (s *bridgeSpan) updateOTelContext(restrictedKey, value string) { 244 if s.extraBaggageItems == nil { 245 s.extraBaggageItems = make(map[string]string) 246 } 247 s.extraBaggageItems[restrictedKey] = value 248} 249 250func (s *bridgeSpan) BaggageItem(restrictedKey string) string { 251 return s.ctx.baggageItem(restrictedKey) 252} 253 254func (s *bridgeSpan) Tracer() ot.Tracer { 255 return s.tracer 256} 257 258func (s *bridgeSpan) LogEvent(event string) { 259 s.LogEventWithPayload(event, nil) 260} 261 262func (s *bridgeSpan) LogEventWithPayload(event string, payload interface{}) { 263 data := ot.LogData{ 264 Event: event, 265 Payload: payload, 266 } 267 s.Log(data) 268} 269 270func (s *bridgeSpan) Log(data ot.LogData) { 271 record := data.ToLogRecord() 272 s.LogFields(record.Fields...) 273} 274 275type bridgeSetTracer struct { 276 isSet bool 277 otelTracer trace.Tracer 278 279 warningHandler BridgeWarningHandler 280 warnOnce sync.Once 281} 282 283func (s *bridgeSetTracer) tracer() trace.Tracer { 284 if !s.isSet { 285 s.warnOnce.Do(func() { 286 s.warningHandler("The OpenTelemetry tracer is not set, default no-op tracer is used! Call SetOpenTelemetryTracer to set it up.\n") 287 }) 288 } 289 return s.otelTracer 290} 291 292// BridgeWarningHandler is a type of handler that receives warnings 293// from the BridgeTracer. 294type BridgeWarningHandler func(msg string) 295 296// BridgeTracer is an implementation of the OpenTracing tracer, which 297// translates the calls to the OpenTracing API into OpenTelemetry 298// counterparts and calls the underlying OpenTelemetry tracer. 299type BridgeTracer struct { 300 setTracer bridgeSetTracer 301 302 warningHandler BridgeWarningHandler 303 warnOnce sync.Once 304 305 propagator propagation.TextMapPropagator 306} 307 308var _ ot.Tracer = &BridgeTracer{} 309var _ ot.TracerContextWithSpanExtension = &BridgeTracer{} 310 311// NewBridgeTracer creates a new BridgeTracer. The new tracer forwards 312// the calls to the OpenTelemetry Noop tracer, so it should be 313// overridden with the SetOpenTelemetryTracer function. The warnings 314// handler does nothing by default, so to override it use the 315// SetWarningHandler function. 316func NewBridgeTracer() *BridgeTracer { 317 return &BridgeTracer{ 318 setTracer: bridgeSetTracer{ 319 otelTracer: noop.Tracer, 320 }, 321 warningHandler: func(msg string) {}, 322 propagator: nil, 323 } 324} 325 326// SetWarningHandler overrides the warning handler. 327func (t *BridgeTracer) SetWarningHandler(handler BridgeWarningHandler) { 328 t.setTracer.warningHandler = handler 329 t.warningHandler = handler 330} 331 332// SetOpenTelemetryTracer overrides the underlying OpenTelemetry 333// tracer. The passed tracer should know how to operate in the 334// environment that uses OpenTracing API. 335func (t *BridgeTracer) SetOpenTelemetryTracer(tracer trace.Tracer) { 336 t.setTracer.otelTracer = tracer 337 t.setTracer.isSet = true 338} 339 340func (t *BridgeTracer) SetTextMapPropagator(propagator propagation.TextMapPropagator) { 341 t.propagator = propagator 342} 343 344func (t *BridgeTracer) NewHookedContext(ctx context.Context) context.Context { 345 ctx = baggage.ContextWithSetHook(ctx, t.baggageSetHook) 346 ctx = baggage.ContextWithGetHook(ctx, t.baggageGetHook) 347 return ctx 348} 349 350func (t *BridgeTracer) baggageSetHook(ctx context.Context) context.Context { 351 span := ot.SpanFromContext(ctx) 352 if span == nil { 353 t.warningHandler("No active OpenTracing span, can not propagate the baggage items from OpenTelemetry context\n") 354 return ctx 355 } 356 bSpan, ok := span.(*bridgeSpan) 357 if !ok { 358 t.warningHandler("Encountered a foreign OpenTracing span, will not propagate the baggage items from OpenTelemetry context\n") 359 return ctx 360 } 361 // we clear the context only to avoid calling a get hook 362 // during MapFromContext, but otherwise we don't change the 363 // context, so we don't care about the old hooks. 364 clearCtx, _, _ := baggage.ContextWithNoHooks(ctx) 365 m := baggage.MapFromContext(clearCtx) 366 m.Foreach(func(kv attribute.KeyValue) bool { 367 bSpan.setBaggageItemOnly(string(kv.Key), kv.Value.Emit()) 368 return true 369 }) 370 return ctx 371} 372 373func (t *BridgeTracer) baggageGetHook(ctx context.Context, m baggage.Map) baggage.Map { 374 span := ot.SpanFromContext(ctx) 375 if span == nil { 376 t.warningHandler("No active OpenTracing span, can not propagate the baggage items from OpenTracing span context\n") 377 return m 378 } 379 bSpan, ok := span.(*bridgeSpan) 380 if !ok { 381 t.warningHandler("Encountered a foreign OpenTracing span, will not propagate the baggage items from OpenTracing span context\n") 382 return m 383 } 384 items := bSpan.extraBaggageItems 385 if len(items) == 0 { 386 return m 387 } 388 kv := make([]attribute.KeyValue, 0, len(items)) 389 for k, v := range items { 390 kv = append(kv, attribute.String(k, v)) 391 } 392 return m.Apply(baggage.MapUpdate{MultiKV: kv}) 393} 394 395// StartSpan is a part of the implementation of the OpenTracing Tracer 396// interface. 397func (t *BridgeTracer) StartSpan(operationName string, opts ...ot.StartSpanOption) ot.Span { 398 sso := ot.StartSpanOptions{} 399 for _, opt := range opts { 400 opt.Apply(&sso) 401 } 402 parentBridgeSC, links := otSpanReferencesToParentAndLinks(sso.References) 403 attributes, kind, hadTrueErrorTag := otTagsToOTelAttributesKindAndError(sso.Tags) 404 checkCtx := migration.WithDeferredSetup(context.Background()) 405 if parentBridgeSC != nil { 406 checkCtx = trace.ContextWithRemoteSpanContext(checkCtx, parentBridgeSC.otelSpanContext) 407 } 408 checkCtx2, otelSpan := t.setTracer.tracer().Start( 409 checkCtx, 410 operationName, 411 trace.WithAttributes(attributes...), 412 trace.WithTimestamp(sso.StartTime), 413 trace.WithLinks(links...), 414 trace.WithRecord(), 415 trace.WithSpanKind(kind), 416 ) 417 if checkCtx != checkCtx2 { 418 t.warnOnce.Do(func() { 419 t.warningHandler("SDK should have deferred the context setup, see the documentation of go.opentelemetry.io/otel/bridge/opentracing/migration\n") 420 }) 421 } 422 if hadTrueErrorTag { 423 otelSpan.SetStatus(codes.Error, "") 424 } 425 // One does not simply pass a concrete pointer to function 426 // that takes some interface. In case of passing nil concrete 427 // pointer, we get an interface with non-nil type (because the 428 // pointer type is known) and a nil value. Which means 429 // interface is not nil, but calling some interface function 430 // on it will most likely result in nil pointer dereference. 431 var otSpanContext ot.SpanContext 432 if parentBridgeSC != nil { 433 otSpanContext = parentBridgeSC 434 } 435 sctx := newBridgeSpanContext(otelSpan.SpanContext(), otSpanContext) 436 span := newBridgeSpan(otelSpan, sctx, t) 437 438 return span 439} 440 441// ContextWithBridgeSpan sets up the context with the passed 442// OpenTelemetry span as the active OpenTracing span. 443// 444// This function should be used by the OpenTelemetry tracers that want 445// to be aware how to operate in the environment using OpenTracing 446// API. 447func (t *BridgeTracer) ContextWithBridgeSpan(ctx context.Context, span trace.Span) context.Context { 448 var otSpanContext ot.SpanContext 449 if parentSpan := ot.SpanFromContext(ctx); parentSpan != nil { 450 otSpanContext = parentSpan.Context() 451 } 452 bCtx := newBridgeSpanContext(span.SpanContext(), otSpanContext) 453 bSpan := newBridgeSpan(span, bCtx, t) 454 bSpan.skipDeferHook = true 455 return ot.ContextWithSpan(ctx, bSpan) 456} 457 458// ContextWithSpanHook is an implementation of the OpenTracing tracer 459// extension interface. It will call the DeferredContextSetupHook 460// function on the tracer if it implements the 461// DeferredContextSetupTracerExtension interface. 462func (t *BridgeTracer) ContextWithSpanHook(ctx context.Context, span ot.Span) context.Context { 463 bSpan, ok := span.(*bridgeSpan) 464 if !ok { 465 t.warningHandler("Encountered a foreign OpenTracing span, will not run a possible deferred context setup hook\n") 466 return ctx 467 } 468 if bSpan.skipDeferHook { 469 return ctx 470 } 471 if tracerWithExtension, ok := bSpan.tracer.setTracer.tracer().(migration.DeferredContextSetupTracerExtension); ok { 472 ctx = tracerWithExtension.DeferredContextSetupHook(ctx, bSpan.otelSpan) 473 } 474 return ctx 475} 476 477func otTagsToOTelAttributesKindAndError(tags map[string]interface{}) ([]attribute.KeyValue, trace.SpanKind, bool) { 478 kind := trace.SpanKindInternal 479 err := false 480 var pairs []attribute.KeyValue 481 for k, v := range tags { 482 switch k { 483 case string(otext.SpanKind): 484 if s, ok := v.(string); ok { 485 switch strings.ToLower(s) { 486 case "client": 487 kind = trace.SpanKindClient 488 case "server": 489 kind = trace.SpanKindServer 490 case "producer": 491 kind = trace.SpanKindProducer 492 case "consumer": 493 kind = trace.SpanKindConsumer 494 } 495 } 496 case string(otext.Error): 497 if b, ok := v.(bool); ok && b { 498 err = true 499 } 500 default: 501 pairs = append(pairs, otTagToOTelLabel(k, v)) 502 } 503 } 504 return pairs, kind, err 505} 506 507// otTagToOTelLabel converts given key-value into attribute.KeyValue. 508// Note that some conversions are not obvious: 509// - int -> int64 510// - uint -> string 511// - int32 -> int64 512// - uint32 -> int64 513// - uint64 -> string 514// - float32 -> float64 515func otTagToOTelLabel(k string, v interface{}) attribute.KeyValue { 516 key := otTagToOTelLabelKey(k) 517 switch val := v.(type) { 518 case bool: 519 return key.Bool(val) 520 case int64: 521 return key.Int64(val) 522 case uint64: 523 return key.String(fmt.Sprintf("%d", val)) 524 case float64: 525 return key.Float64(val) 526 case int32: 527 return key.Int64(int64(val)) 528 case uint32: 529 return key.Int64(int64(val)) 530 case float32: 531 return key.Float64(float64(val)) 532 case int: 533 return key.Int(val) 534 case uint: 535 return key.String(fmt.Sprintf("%d", val)) 536 case string: 537 return key.String(val) 538 default: 539 return key.String(fmt.Sprint(v)) 540 } 541} 542 543func otTagToOTelLabelKey(k string) attribute.Key { 544 return attribute.Key(k) 545} 546 547func otSpanReferencesToParentAndLinks(references []ot.SpanReference) (*bridgeSpanContext, []trace.Link) { 548 var ( 549 parent *bridgeSpanContext 550 links []trace.Link 551 ) 552 for _, reference := range references { 553 bridgeSC, ok := reference.ReferencedContext.(*bridgeSpanContext) 554 if !ok { 555 // We ignore foreign ot span contexts, 556 // sorry. We have no way of getting any 557 // TraceID and SpanID out of it for form a 558 // OTel SpanContext for OTel Link. And 559 // we can't make it a parent - it also needs a 560 // valid OTel SpanContext. 561 continue 562 } 563 if parent != nil { 564 links = append(links, otSpanReferenceToOTelLink(bridgeSC, reference.Type)) 565 } else { 566 if reference.Type == ot.ChildOfRef { 567 parent = bridgeSC 568 } else { 569 links = append(links, otSpanReferenceToOTelLink(bridgeSC, reference.Type)) 570 } 571 } 572 } 573 return parent, links 574} 575 576func otSpanReferenceToOTelLink(bridgeSC *bridgeSpanContext, refType ot.SpanReferenceType) trace.Link { 577 return trace.Link{ 578 SpanContext: bridgeSC.otelSpanContext, 579 Attributes: otSpanReferenceTypeToOTelLinkAttributes(refType), 580 } 581} 582 583func otSpanReferenceTypeToOTelLinkAttributes(refType ot.SpanReferenceType) []attribute.KeyValue { 584 return []attribute.KeyValue{ 585 attribute.String("ot-span-reference-type", otSpanReferenceTypeToString(refType)), 586 } 587} 588 589func otSpanReferenceTypeToString(refType ot.SpanReferenceType) string { 590 switch refType { 591 case ot.ChildOfRef: 592 // "extra", because first child-of reference is used 593 // as a parent, so this function isn't even called for 594 // it. 595 return "extra-child-of" 596 case ot.FollowsFromRef: 597 return "follows-from-ref" 598 default: 599 return fmt.Sprintf("unknown-%d", int(refType)) 600 } 601} 602 603// fakeSpan is just a holder of span context, nothing more. It's for 604// propagators, so they can get the span context from Go context. 605type fakeSpan struct { 606 trace.Span 607 sc trace.SpanContext 608} 609 610func (s fakeSpan) SpanContext() trace.SpanContext { 611 return s.sc 612} 613 614// Inject is a part of the implementation of the OpenTracing Tracer 615// interface. 616// 617// Currently only the HTTPHeaders format is supported. 618func (t *BridgeTracer) Inject(sm ot.SpanContext, format interface{}, carrier interface{}) error { 619 bridgeSC, ok := sm.(*bridgeSpanContext) 620 if !ok { 621 return ot.ErrInvalidSpanContext 622 } 623 if !bridgeSC.otelSpanContext.IsValid() { 624 return ot.ErrInvalidSpanContext 625 } 626 if builtinFormat, ok := format.(ot.BuiltinFormat); !ok || builtinFormat != ot.HTTPHeaders { 627 return ot.ErrUnsupportedFormat 628 } 629 hhcarrier, ok := carrier.(ot.HTTPHeadersCarrier) 630 if !ok { 631 return ot.ErrInvalidCarrier 632 } 633 header := http.Header(hhcarrier) 634 fs := fakeSpan{ 635 Span: noop.Span, 636 sc: bridgeSC.otelSpanContext, 637 } 638 ctx := trace.ContextWithSpan(context.Background(), fs) 639 ctx = baggage.ContextWithMap(ctx, bridgeSC.baggageItems) 640 t.getPropagator().Inject(ctx, propagation.HeaderCarrier(header)) 641 return nil 642} 643 644// Extract is a part of the implementation of the OpenTracing Tracer 645// interface. 646// 647// Currently only the HTTPHeaders format is supported. 648func (t *BridgeTracer) Extract(format interface{}, carrier interface{}) (ot.SpanContext, error) { 649 if builtinFormat, ok := format.(ot.BuiltinFormat); !ok || builtinFormat != ot.HTTPHeaders { 650 return nil, ot.ErrUnsupportedFormat 651 } 652 hhcarrier, ok := carrier.(ot.HTTPHeadersCarrier) 653 if !ok { 654 return nil, ot.ErrInvalidCarrier 655 } 656 header := http.Header(hhcarrier) 657 ctx := t.getPropagator().Extract(context.Background(), propagation.HeaderCarrier(header)) 658 baggage := baggage.MapFromContext(ctx) 659 otelSC, _, _ := otelparent.GetSpanContextAndLinks(ctx, false) 660 bridgeSC := &bridgeSpanContext{ 661 baggageItems: baggage, 662 otelSpanContext: otelSC, 663 } 664 if !bridgeSC.otelSpanContext.IsValid() { 665 return nil, ot.ErrSpanContextNotFound 666 } 667 return bridgeSC, nil 668} 669 670func (t *BridgeTracer) getPropagator() propagation.TextMapPropagator { 671 if t.propagator != nil { 672 return t.propagator 673 } 674 return otel.GetTextMapPropagator() 675} 676