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