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 }