cloudflared-mirror/vendor/go.opentelemetry.io/contrib/propagators/Jaeger/jaeger_propagator.go

162 lines
4.7 KiB
Go

// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package jaeger
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
)
const (
jaegerHeader = "uber-trace-id"
separator = ":"
traceID64bitsWidth = 64 / 4
traceID128bitsWidth = 128 / 4
spanIDWidth = 64 / 4
traceIDPadding = "0000000000000000"
flagsDebug = 0x02
flagsSampled = 0x01
flagsNotSampled = 0x00
deprecatedParentSpanID = "0"
)
var (
empty = trace.SpanContext{}
errMalformedTraceContextVal = errors.New("header value of uber-trace-id should contain four different part separated by : ")
errInvalidTraceIDLength = errors.New("invalid trace id length, must be either 16 or 32")
errMalformedTraceID = errors.New("cannot decode trace id from header, should be a string of hex, lowercase trace id can't be all zero")
errInvalidSpanIDLength = errors.New("invalid span id length, must be 16")
errMalformedSpanID = errors.New("cannot decode span id from header, should be a string of hex, lowercase span id can't be all zero")
errMalformedFlag = errors.New("cannot decode flag")
)
// Jaeger propagator serializes SpanContext to/from Jaeger Headers
//
// Jaeger format:
//
// uber-trace-id: {trace-id}:{span-id}:{parent-span-id}:{flags}
type Jaeger struct{}
var _ propagation.TextMapPropagator = &Jaeger{}
// Inject injects a context to the carrier following jaeger format.
// The parent span ID is set to an dummy parent span id as the most implementations do.
func (jaeger Jaeger) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {
sc := trace.SpanFromContext(ctx).SpanContext()
headers := []string{}
if !sc.TraceID().IsValid() || !sc.SpanID().IsValid() {
return
}
headers = append(headers, sc.TraceID().String(), sc.SpanID().String(), deprecatedParentSpanID)
if debugFromContext(ctx) {
headers = append(headers, fmt.Sprintf("%x", flagsDebug|flagsSampled))
} else if sc.IsSampled() {
headers = append(headers, fmt.Sprintf("%x", flagsSampled))
} else {
headers = append(headers, fmt.Sprintf("%x", flagsNotSampled))
}
carrier.Set(jaegerHeader, strings.Join(headers, separator))
}
// Extract extracts a context from the carrier if it contains Jaeger headers.
func (jaeger Jaeger) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context {
// extract tracing information
if h := carrier.Get(jaegerHeader); h != "" {
ctx, sc, err := extract(ctx, h)
if err == nil && sc.IsValid() {
return trace.ContextWithRemoteSpanContext(ctx, sc)
}
}
return ctx
}
func extract(ctx context.Context, headerVal string) (context.Context, trace.SpanContext, error) {
var (
scc = trace.SpanContextConfig{}
err error
)
parts := strings.Split(headerVal, separator)
if len(parts) != 4 {
return ctx, empty, errMalformedTraceContextVal
}
// extract trace ID
if parts[0] != "" {
id := parts[0]
if len(id) != traceID128bitsWidth && len(id) != traceID64bitsWidth {
return ctx, empty, errInvalidTraceIDLength
}
// padding when length is 16
if len(id) == traceID64bitsWidth {
id = traceIDPadding + id
}
scc.TraceID, err = trace.TraceIDFromHex(id)
if err != nil {
return ctx, empty, errMalformedTraceID
}
}
// extract span ID
if parts[1] != "" {
id := parts[1]
if len(id) != spanIDWidth {
return ctx, empty, errInvalidSpanIDLength
}
scc.SpanID, err = trace.SpanIDFromHex(id)
if err != nil {
return ctx, empty, errMalformedSpanID
}
}
// skip third part as it is deprecated
// extract flag
if parts[3] != "" {
flagStr := parts[3]
flag, err := strconv.ParseInt(flagStr, 16, 64)
if err != nil {
return ctx, empty, errMalformedFlag
}
if flag&flagsSampled == flagsSampled {
// if sample bit is set, we check if debug bit is also set
if flag&flagsDebug == flagsDebug {
scc.TraceFlags |= trace.FlagsSampled
ctx = withDebug(ctx, true)
} else {
scc.TraceFlags |= trace.FlagsSampled
}
}
// ignore other bit, including firehose since we don't have corresponding flag in trace context.
}
return ctx, trace.NewSpanContext(scc), nil
}
func (jaeger Jaeger) Fields() []string {
return []string{jaegerHeader}
}