| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- // Copyright 2022 The OpenZipkin 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 b3
- import (
- "strconv"
- "strings"
- "github.com/openzipkin/zipkin-go/model"
- )
- // ParseHeaders takes values found from B3 Headers and tries to reconstruct a
- // SpanContext.
- func ParseHeaders(
- hdrTraceID, hdrSpanID, hdrParentSpanID, hdrSampled, hdrFlags string,
- ) (*model.SpanContext, error) {
- var (
- err error
- spanID uint64
- requiredCount int
- sc = &model.SpanContext{}
- )
- // correct values for an existing sampled header are "0" and "1".
- // For legacy support and being lenient to other tracing implementations we
- // allow "true" and "false" as inputs for interop purposes.
- switch strings.ToLower(hdrSampled) {
- case "0", "false":
- sampled := false
- sc.Sampled = &sampled
- case "1", "true":
- sampled := true
- sc.Sampled = &sampled
- case "":
- // sc.Sampled = nil
- default:
- return nil, ErrInvalidSampledHeader
- }
- // The only accepted value for Flags is "1". This will set Debug to true. All
- // other values and omission of header will be ignored.
- if hdrFlags == "1" {
- sc.Debug = true
- sc.Sampled = nil
- }
- if hdrTraceID != "" {
- requiredCount++
- if sc.TraceID, err = model.TraceIDFromHex(hdrTraceID); err != nil {
- return nil, ErrInvalidTraceIDHeader
- }
- }
- if hdrSpanID != "" {
- requiredCount++
- if spanID, err = strconv.ParseUint(hdrSpanID, 16, 64); err != nil {
- return nil, ErrInvalidSpanIDHeader
- }
- sc.ID = model.ID(spanID)
- }
- if requiredCount != 0 && requiredCount != 2 {
- return nil, ErrInvalidScope
- }
- if hdrParentSpanID != "" {
- if requiredCount == 0 {
- return nil, ErrInvalidScopeParent
- }
- if spanID, err = strconv.ParseUint(hdrParentSpanID, 16, 64); err != nil {
- return nil, ErrInvalidParentSpanIDHeader
- }
- parentSpanID := model.ID(spanID)
- sc.ParentID = &parentSpanID
- }
- return sc, nil
- }
- // ParseSingleHeader takes values found from B3 Single Header and tries to reconstruct a
- // SpanContext.
- func ParseSingleHeader(contextHeader string) (*model.SpanContext, error) {
- if contextHeader == "" {
- return nil, ErrEmptyContext
- }
- var (
- sc = model.SpanContext{}
- sampling string
- )
- headerLen := len(contextHeader)
- if headerLen == 1 {
- sampling = contextHeader
- } else if headerLen == 16 || headerLen == 32 {
- return nil, ErrInvalidScope
- } else if headerLen >= 16+16+1 {
- var high, low uint64
- pos := 0
- if string(contextHeader[16]) != "-" {
- // traceID must be 128 bits
- var err error
- high, err = strconv.ParseUint(contextHeader[0:16], 16, 64)
- if err != nil {
- return nil, ErrInvalidTraceIDValue
- }
- pos = 16
- }
- low, err := strconv.ParseUint(contextHeader[pos:pos+16], 16, 64)
- if err != nil {
- return nil, ErrInvalidTraceIDValue
- }
- sc.TraceID = model.TraceID{High: high, Low: low}
- rawID, err := strconv.ParseUint(contextHeader[pos+16+1:pos+16+1+16], 16, 64)
- if err != nil {
- return nil, ErrInvalidSpanIDValue
- }
- sc.ID = model.ID(rawID)
- if headerLen > pos+16+1+16 {
- if headerLen == pos+16+1+16+1 {
- return nil, ErrInvalidSampledByte
- }
- if headerLen == pos+16+1+16+1+1 {
- sampling = string(contextHeader[pos+16+1+16+1])
- } else if headerLen == pos+16+1+16+1+16 {
- return nil, ErrInvalidScopeParentSingle
- } else if headerLen == pos+16+1+16+1+1+1+16 {
- sampling = string(contextHeader[pos+16+1+16+1])
- var rawParentID uint64
- rawParentID, err = strconv.ParseUint(contextHeader[pos+16+1+16+1+1+1:], 16, 64)
- if err != nil {
- return nil, ErrInvalidParentSpanIDValue
- }
- parentID := model.ID(rawParentID)
- sc.ParentID = &parentID
- } else {
- return nil, ErrInvalidParentSpanIDValue
- }
- }
- } else {
- return nil, ErrInvalidTraceIDValue
- }
- switch sampling {
- case "d":
- sc.Debug = true
- case "1":
- trueVal := true
- sc.Sampled = &trueVal
- case "0":
- falseVal := false
- sc.Sampled = &falseVal
- case "":
- default:
- return nil, ErrInvalidSampledByte
- }
- return &sc, nil
- }
- // BuildSingleHeader takes the values from the SpanContext and builds the B3 header
- func BuildSingleHeader(sc model.SpanContext) string {
- var header []string
- if !sc.TraceID.Empty() && sc.ID > 0 {
- header = append(header, sc.TraceID.String(), sc.ID.String())
- }
- if sc.Debug {
- header = append(header, "d")
- } else if sc.Sampled != nil {
- if *sc.Sampled {
- header = append(header, "1")
- } else {
- header = append(header, "0")
- }
- }
- if sc.ParentID != nil {
- header = append(header, sc.ParentID.String())
- }
- return strings.Join(header, "-")
- }
|