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 }