package loggabletracer import ( "encoding/binary" "io" "strconv" "strings" "github.com/gogo/protobuf/proto" "github.com/ipfs/go-log/tracer/wire" opentracing "github.com/opentracing/opentracing-go" ) type textMapPropagator struct { tracer *LoggableTracer } type binaryPropagator struct { tracer *LoggableTracer } const ( prefixTracerState = "ot-tracer-" prefixBaggage = "ot-baggage-" tracerStateFieldCount = 3 fieldNameTraceID = prefixTracerState + "traceid" fieldNameSpanID = prefixTracerState + "spanid" fieldNameSampled = prefixTracerState + "sampled" ) func (p *textMapPropagator) Inject( spanContext opentracing.SpanContext, opaqueCarrier interface{}, ) error { sc, ok := spanContext.(SpanContext) if !ok { return opentracing.ErrInvalidSpanContext } carrier, ok := opaqueCarrier.(opentracing.TextMapWriter) if !ok { return opentracing.ErrInvalidCarrier } carrier.Set(fieldNameTraceID, strconv.FormatUint(sc.TraceID, 16)) carrier.Set(fieldNameSpanID, strconv.FormatUint(sc.SpanID, 16)) carrier.Set(fieldNameSampled, strconv.FormatBool(sc.Sampled)) for k, v := range sc.Baggage { carrier.Set(prefixBaggage+k, v) } return nil } func (p *textMapPropagator) Extract( opaqueCarrier interface{}, ) (opentracing.SpanContext, error) { carrier, ok := opaqueCarrier.(opentracing.TextMapReader) if !ok { return nil, opentracing.ErrInvalidCarrier } requiredFieldCount := 0 var traceID, spanID uint64 var sampled bool var err error decodedBaggage := make(map[string]string) err = carrier.ForeachKey(func(k, v string) error { switch strings.ToLower(k) { case fieldNameTraceID: traceID, err = strconv.ParseUint(v, 16, 64) if err != nil { return opentracing.ErrSpanContextCorrupted } case fieldNameSpanID: spanID, err = strconv.ParseUint(v, 16, 64) if err != nil { return opentracing.ErrSpanContextCorrupted } case fieldNameSampled: sampled, err = strconv.ParseBool(v) if err != nil { return opentracing.ErrSpanContextCorrupted } default: lowercaseK := strings.ToLower(k) if strings.HasPrefix(lowercaseK, prefixBaggage) { decodedBaggage[strings.TrimPrefix(lowercaseK, prefixBaggage)] = v } // Balance off the requiredFieldCount++ just below... requiredFieldCount-- } requiredFieldCount++ return nil }) if err != nil { return nil, err } if requiredFieldCount < tracerStateFieldCount { if requiredFieldCount == 0 { return nil, opentracing.ErrSpanContextNotFound } return nil, opentracing.ErrSpanContextCorrupted } return SpanContext{ TraceID: traceID, SpanID: spanID, Sampled: sampled, Baggage: decodedBaggage, }, nil } func (p *binaryPropagator) Inject( spanContext opentracing.SpanContext, opaqueCarrier interface{}, ) error { sc, ok := spanContext.(SpanContext) if !ok { return opentracing.ErrInvalidSpanContext } carrier, ok := opaqueCarrier.(io.Writer) if !ok { return opentracing.ErrInvalidCarrier } state := wire.TracerState{} state.TraceId = sc.TraceID state.SpanId = sc.SpanID state.Sampled = sc.Sampled state.BaggageItems = sc.Baggage b, err := proto.Marshal(&state) if err != nil { return err } // Write the length of the marshalled binary to the writer. length := uint32(len(b)) if err := binary.Write(carrier, binary.BigEndian, &length); err != nil { return err } _, err = carrier.Write(b) return err } func (p *binaryPropagator) Extract( opaqueCarrier interface{}, ) (opentracing.SpanContext, error) { carrier, ok := opaqueCarrier.(io.Reader) if !ok { return nil, opentracing.ErrInvalidCarrier } // Read the length of marshalled binary. io.ReadAll isn't that performant // since it keeps resizing the underlying buffer as it encounters more bytes // to read. By reading the length, we can allocate a fixed sized buf and read // the exact amount of bytes into it. var length uint32 if err := binary.Read(carrier, binary.BigEndian, &length); err != nil { return nil, opentracing.ErrSpanContextCorrupted } buf := make([]byte, length) if n, err := carrier.Read(buf); err != nil { if n > 0 { return nil, opentracing.ErrSpanContextCorrupted } return nil, opentracing.ErrSpanContextNotFound } ctx := wire.TracerState{} if err := proto.Unmarshal(buf, &ctx); err != nil { return nil, opentracing.ErrSpanContextCorrupted } return SpanContext{ TraceID: ctx.TraceId, SpanID: ctx.SpanId, Sampled: ctx.Sampled, Baggage: ctx.BaggageItems, }, nil }