go.undefinedlabs.com/scopeagent@v0.4.2/tracer/propagation_ot.go (about)

     1  package tracer
     2  
     3  import (
     4  	"encoding/binary"
     5  	"io"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/gogo/protobuf/proto"
    10  	"github.com/google/uuid"
    11  	opentracing "github.com/opentracing/opentracing-go"
    12  	"go.undefinedlabs.com/scopeagent/tracer/wire"
    13  )
    14  
    15  type textMapPropagator struct {
    16  	tracer *tracerImpl
    17  }
    18  type binaryPropagator struct {
    19  	tracer *tracerImpl
    20  }
    21  
    22  const (
    23  	prefixTracerState = "ot-tracer-"
    24  	prefixBaggage     = "ot-baggage-"
    25  
    26  	tracerStateFieldCount = 3
    27  	fieldNameTraceID      = prefixTracerState + "traceid"
    28  	fieldNameSpanID       = prefixTracerState + "spanid"
    29  	fieldNameSampled      = prefixTracerState + "sampled"
    30  )
    31  
    32  func (p *textMapPropagator) Inject(
    33  	spanContext opentracing.SpanContext,
    34  	opaqueCarrier interface{},
    35  ) error {
    36  	sc, ok := spanContext.(SpanContext)
    37  	if !ok {
    38  		return opentracing.ErrInvalidSpanContext
    39  	}
    40  	carrier, ok := opaqueCarrier.(opentracing.TextMapWriter)
    41  	if !ok {
    42  		return opentracing.ErrInvalidCarrier
    43  	}
    44  	carrier.Set(fieldNameTraceID, UUIDToString(sc.TraceID))
    45  	carrier.Set(fieldNameSpanID, strconv.FormatUint(sc.SpanID, 16))
    46  	carrier.Set(fieldNameSampled, strconv.FormatBool(sc.Sampled))
    47  
    48  	for k, v := range sc.Baggage {
    49  		carrier.Set(prefixBaggage+k, v)
    50  	}
    51  	return nil
    52  }
    53  
    54  func (p *textMapPropagator) Extract(
    55  	opaqueCarrier interface{},
    56  ) (opentracing.SpanContext, error) {
    57  	carrier, ok := opaqueCarrier.(opentracing.TextMapReader)
    58  	if !ok {
    59  		return nil, opentracing.ErrInvalidCarrier
    60  	}
    61  	requiredFieldCount := 0
    62  	var traceID uuid.UUID
    63  	var spanID uint64
    64  	var sampled bool
    65  	var err error
    66  	decodedBaggage := make(map[string]string)
    67  	err = carrier.ForeachKey(func(k, v string) error {
    68  		switch strings.ToLower(k) {
    69  		case fieldNameTraceID:
    70  			traceID, err = StringToUUID(v)
    71  			if err != nil {
    72  				return opentracing.ErrSpanContextCorrupted
    73  			}
    74  		case fieldNameSpanID:
    75  			spanID, err = strconv.ParseUint(v, 16, 64)
    76  			if err != nil {
    77  				return opentracing.ErrSpanContextCorrupted
    78  			}
    79  		case fieldNameSampled:
    80  			sampled, err = strconv.ParseBool(v)
    81  			if err != nil {
    82  				return opentracing.ErrSpanContextCorrupted
    83  			}
    84  		default:
    85  			lowercaseK := strings.ToLower(k)
    86  			if strings.HasPrefix(lowercaseK, prefixBaggage) {
    87  				decodedBaggage[strings.TrimPrefix(lowercaseK, prefixBaggage)] = v
    88  			}
    89  			// Balance off the requiredFieldCount++ just below...
    90  			requiredFieldCount--
    91  		}
    92  		requiredFieldCount++
    93  		return nil
    94  	})
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	if requiredFieldCount < tracerStateFieldCount {
    99  		if requiredFieldCount == 0 {
   100  			return nil, opentracing.ErrSpanContextNotFound
   101  		}
   102  		return nil, opentracing.ErrSpanContextCorrupted
   103  	}
   104  
   105  	return SpanContext{
   106  		TraceID: traceID,
   107  		SpanID:  spanID,
   108  		Sampled: sampled,
   109  		Baggage: decodedBaggage,
   110  	}, nil
   111  }
   112  
   113  func (p *binaryPropagator) Inject(
   114  	spanContext opentracing.SpanContext,
   115  	opaqueCarrier interface{},
   116  ) error {
   117  	sc, ok := spanContext.(SpanContext)
   118  	if !ok {
   119  		return opentracing.ErrInvalidSpanContext
   120  	}
   121  	carrier, ok := opaqueCarrier.(io.Writer)
   122  	if !ok {
   123  		return opentracing.ErrInvalidCarrier
   124  	}
   125  
   126  	state := wire.TracerState{}
   127  	state.TraceIdHi = binary.LittleEndian.Uint64(sc.TraceID[:8])
   128  	state.TraceIdLo = binary.LittleEndian.Uint64(sc.TraceID[8:])
   129  	state.SpanId = sc.SpanID
   130  	state.Sampled = sc.Sampled
   131  	state.BaggageItems = sc.Baggage
   132  
   133  	b, err := proto.Marshal(&state)
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	// Write the length of the marshalled binary to the writer.
   139  	length := uint32(len(b))
   140  	if err := binary.Write(carrier, binary.BigEndian, &length); err != nil {
   141  		return err
   142  	}
   143  
   144  	_, err = carrier.Write(b)
   145  	return err
   146  }
   147  
   148  func (p *binaryPropagator) Extract(
   149  	opaqueCarrier interface{},
   150  ) (opentracing.SpanContext, error) {
   151  	carrier, ok := opaqueCarrier.(io.Reader)
   152  	if !ok {
   153  		return nil, opentracing.ErrInvalidCarrier
   154  	}
   155  
   156  	// Read the length of marshalled binary. io.ReadAll isn't that performant
   157  	// since it keeps resizing the underlying buffer as it encounters more bytes
   158  	// to read. By reading the length, we can allocate a fixed sized buf and read
   159  	// the exact amount of bytes into it.
   160  	var length uint32
   161  	if err := binary.Read(carrier, binary.BigEndian, &length); err != nil {
   162  		return nil, opentracing.ErrSpanContextCorrupted
   163  	}
   164  	buf := make([]byte, length)
   165  	if n, err := carrier.Read(buf); err != nil {
   166  		if n > 0 {
   167  			return nil, opentracing.ErrSpanContextCorrupted
   168  		}
   169  		return nil, opentracing.ErrSpanContextNotFound
   170  	}
   171  
   172  	ctx := wire.TracerState{}
   173  	if err := proto.Unmarshal(buf, &ctx); err != nil {
   174  		return nil, opentracing.ErrSpanContextCorrupted
   175  	}
   176  
   177  	traceId := uuid.UUID{}
   178  	binary.LittleEndian.PutUint64(traceId[:8], ctx.TraceIdHi)
   179  	binary.LittleEndian.PutUint64(traceId[8:], ctx.TraceIdLo)
   180  
   181  	return SpanContext{
   182  		TraceID: traceId,
   183  		SpanID:  ctx.SpanId,
   184  		Sampled: ctx.Sampled,
   185  		Baggage: ctx.BaggageItems,
   186  	}, nil
   187  }