gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

trace_context.go (4551B)


      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 
     15 package propagation // import "go.opentelemetry.io/otel/propagation"
     16 
     17 import (
     18 	"context"
     19 	"encoding/hex"
     20 	"fmt"
     21 	"regexp"
     22 
     23 	"go.opentelemetry.io/otel/trace"
     24 )
     25 
     26 const (
     27 	supportedVersion  = 0
     28 	maxVersion        = 254
     29 	traceparentHeader = "traceparent"
     30 	tracestateHeader  = "tracestate"
     31 )
     32 
     33 // TraceContext is a propagator that supports the W3C Trace Context format
     34 // (https://www.w3.org/TR/trace-context/)
     35 //
     36 // This propagator will propagate the traceparent and tracestate headers to
     37 // guarantee traces are not broken. It is up to the users of this propagator
     38 // to choose if they want to participate in a trace by modifying the
     39 // traceparent header and relevant parts of the tracestate header containing
     40 // their proprietary information.
     41 type TraceContext struct{}
     42 
     43 var _ TextMapPropagator = TraceContext{}
     44 var traceCtxRegExp = regexp.MustCompile("^(?P<version>[0-9a-f]{2})-(?P<traceID>[a-f0-9]{32})-(?P<spanID>[a-f0-9]{16})-(?P<traceFlags>[a-f0-9]{2})(?:-.*)?$")
     45 
     46 // Inject set tracecontext from the Context into the carrier.
     47 func (tc TraceContext) Inject(ctx context.Context, carrier TextMapCarrier) {
     48 	sc := trace.SpanContextFromContext(ctx)
     49 	if !sc.IsValid() {
     50 		return
     51 	}
     52 
     53 	if ts := sc.TraceState().String(); ts != "" {
     54 		carrier.Set(tracestateHeader, ts)
     55 	}
     56 
     57 	// Clear all flags other than the trace-context supported sampling bit.
     58 	flags := sc.TraceFlags() & trace.FlagsSampled
     59 
     60 	h := fmt.Sprintf("%.2x-%s-%s-%s",
     61 		supportedVersion,
     62 		sc.TraceID(),
     63 		sc.SpanID(),
     64 		flags)
     65 	carrier.Set(traceparentHeader, h)
     66 }
     67 
     68 // Extract reads tracecontext from the carrier into a returned Context.
     69 //
     70 // The returned Context will be a copy of ctx and contain the extracted
     71 // tracecontext as the remote SpanContext. If the extracted tracecontext is
     72 // invalid, the passed ctx will be returned directly instead.
     73 func (tc TraceContext) Extract(ctx context.Context, carrier TextMapCarrier) context.Context {
     74 	sc := tc.extract(carrier)
     75 	if !sc.IsValid() {
     76 		return ctx
     77 	}
     78 	return trace.ContextWithRemoteSpanContext(ctx, sc)
     79 }
     80 
     81 func (tc TraceContext) extract(carrier TextMapCarrier) trace.SpanContext {
     82 	h := carrier.Get(traceparentHeader)
     83 	if h == "" {
     84 		return trace.SpanContext{}
     85 	}
     86 
     87 	matches := traceCtxRegExp.FindStringSubmatch(h)
     88 
     89 	if len(matches) == 0 {
     90 		return trace.SpanContext{}
     91 	}
     92 
     93 	if len(matches) < 5 { // four subgroups plus the overall match
     94 		return trace.SpanContext{}
     95 	}
     96 
     97 	if len(matches[1]) != 2 {
     98 		return trace.SpanContext{}
     99 	}
    100 	ver, err := hex.DecodeString(matches[1])
    101 	if err != nil {
    102 		return trace.SpanContext{}
    103 	}
    104 	version := int(ver[0])
    105 	if version > maxVersion {
    106 		return trace.SpanContext{}
    107 	}
    108 
    109 	if version == 0 && len(matches) != 5 { // four subgroups plus the overall match
    110 		return trace.SpanContext{}
    111 	}
    112 
    113 	if len(matches[2]) != 32 {
    114 		return trace.SpanContext{}
    115 	}
    116 
    117 	var scc trace.SpanContextConfig
    118 
    119 	scc.TraceID, err = trace.TraceIDFromHex(matches[2][:32])
    120 	if err != nil {
    121 		return trace.SpanContext{}
    122 	}
    123 
    124 	if len(matches[3]) != 16 {
    125 		return trace.SpanContext{}
    126 	}
    127 	scc.SpanID, err = trace.SpanIDFromHex(matches[3])
    128 	if err != nil {
    129 		return trace.SpanContext{}
    130 	}
    131 
    132 	if len(matches[4]) != 2 {
    133 		return trace.SpanContext{}
    134 	}
    135 	opts, err := hex.DecodeString(matches[4])
    136 	if err != nil || len(opts) < 1 || (version == 0 && opts[0] > 2) {
    137 		return trace.SpanContext{}
    138 	}
    139 	// Clear all flags other than the trace-context supported sampling bit.
    140 	scc.TraceFlags = trace.TraceFlags(opts[0]) & trace.FlagsSampled
    141 
    142 	// Ignore the error returned here. Failure to parse tracestate MUST NOT
    143 	// affect the parsing of traceparent according to the W3C tracecontext
    144 	// specification.
    145 	scc.TraceState, _ = trace.ParseTraceState(carrier.Get(tracestateHeader))
    146 	scc.Remote = true
    147 
    148 	sc := trace.NewSpanContext(scc)
    149 	if !sc.IsValid() {
    150 		return trace.SpanContext{}
    151 	}
    152 
    153 	return sc
    154 }
    155 
    156 // Fields returns the keys who's values are set with Inject.
    157 func (tc TraceContext) Fields() []string {
    158 	return []string{traceparentHeader, tracestateHeader}
    159 }