1// Copyright 2018, OpenCensus 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 b3 contains a propagation.HTTPFormat implementation 16// for B3 propagation. See https://github.com/openzipkin/b3-propagation 17// for more details. 18package b3 // import "go.opencensus.io/plugin/ochttp/propagation/b3" 19 20import ( 21 "encoding/hex" 22 "net/http" 23 24 "go.opencensus.io/trace" 25 "go.opencensus.io/trace/propagation" 26) 27 28// B3 headers that OpenCensus understands. 29const ( 30 TraceIDHeader = "X-B3-TraceId" 31 SpanIDHeader = "X-B3-SpanId" 32 SampledHeader = "X-B3-Sampled" 33) 34 35// HTTPFormat implements propagation.HTTPFormat to propagate 36// traces in HTTP headers in B3 propagation format. 37// HTTPFormat skips the X-B3-ParentId and X-B3-Flags headers 38// because there are additional fields not represented in the 39// OpenCensus span context. Spans created from the incoming 40// header will be the direct children of the client-side span. 41// Similarly, receiver of the outgoing spans should use client-side 42// span created by OpenCensus as the parent. 43type HTTPFormat struct{} 44 45var _ propagation.HTTPFormat = (*HTTPFormat)(nil) 46 47// SpanContextFromRequest extracts a B3 span context from incoming requests. 48func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) { 49 tid, ok := ParseTraceID(req.Header.Get(TraceIDHeader)) 50 if !ok { 51 return trace.SpanContext{}, false 52 } 53 sid, ok := ParseSpanID(req.Header.Get(SpanIDHeader)) 54 if !ok { 55 return trace.SpanContext{}, false 56 } 57 sampled, _ := ParseSampled(req.Header.Get(SampledHeader)) 58 return trace.SpanContext{ 59 TraceID: tid, 60 SpanID: sid, 61 TraceOptions: sampled, 62 }, true 63} 64 65// ParseTraceID parses the value of the X-B3-TraceId header. 66func ParseTraceID(tid string) (trace.TraceID, bool) { 67 if tid == "" { 68 return trace.TraceID{}, false 69 } 70 b, err := hex.DecodeString(tid) 71 if err != nil || len(b) > 16 { 72 return trace.TraceID{}, false 73 } 74 var traceID trace.TraceID 75 if len(b) <= 8 { 76 // The lower 64-bits. 77 start := 8 + (8 - len(b)) 78 copy(traceID[start:], b) 79 } else { 80 start := 16 - len(b) 81 copy(traceID[start:], b) 82 } 83 84 return traceID, true 85} 86 87// ParseSpanID parses the value of the X-B3-SpanId or X-B3-ParentSpanId headers. 88func ParseSpanID(sid string) (spanID trace.SpanID, ok bool) { 89 if sid == "" { 90 return trace.SpanID{}, false 91 } 92 b, err := hex.DecodeString(sid) 93 if err != nil || len(b) > 8 { 94 return trace.SpanID{}, false 95 } 96 start := 8 - len(b) 97 copy(spanID[start:], b) 98 return spanID, true 99} 100 101// ParseSampled parses the value of the X-B3-Sampled header. 102func ParseSampled(sampled string) (trace.TraceOptions, bool) { 103 switch sampled { 104 case "true", "1": 105 return trace.TraceOptions(1), true 106 default: 107 return trace.TraceOptions(0), false 108 } 109} 110 111// SpanContextToRequest modifies the given request to include B3 headers. 112func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) { 113 req.Header.Set(TraceIDHeader, hex.EncodeToString(sc.TraceID[:])) 114 req.Header.Set(SpanIDHeader, hex.EncodeToString(sc.SpanID[:])) 115 116 var sampled string 117 if sc.IsSampled() { 118 sampled = "1" 119 } else { 120 sampled = "0" 121 } 122 req.Header.Set(SampledHeader, sampled) 123} 124