spancontext.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Copyright 2022 The OpenZipkin 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. package b3
  15. import (
  16. "strconv"
  17. "strings"
  18. "github.com/openzipkin/zipkin-go/model"
  19. )
  20. // ParseHeaders takes values found from B3 Headers and tries to reconstruct a
  21. // SpanContext.
  22. func ParseHeaders(
  23. hdrTraceID, hdrSpanID, hdrParentSpanID, hdrSampled, hdrFlags string,
  24. ) (*model.SpanContext, error) {
  25. var (
  26. err error
  27. spanID uint64
  28. requiredCount int
  29. sc = &model.SpanContext{}
  30. )
  31. // correct values for an existing sampled header are "0" and "1".
  32. // For legacy support and being lenient to other tracing implementations we
  33. // allow "true" and "false" as inputs for interop purposes.
  34. switch strings.ToLower(hdrSampled) {
  35. case "0", "false":
  36. sampled := false
  37. sc.Sampled = &sampled
  38. case "1", "true":
  39. sampled := true
  40. sc.Sampled = &sampled
  41. case "":
  42. // sc.Sampled = nil
  43. default:
  44. return nil, ErrInvalidSampledHeader
  45. }
  46. // The only accepted value for Flags is "1". This will set Debug to true. All
  47. // other values and omission of header will be ignored.
  48. if hdrFlags == "1" {
  49. sc.Debug = true
  50. sc.Sampled = nil
  51. }
  52. if hdrTraceID != "" {
  53. requiredCount++
  54. if sc.TraceID, err = model.TraceIDFromHex(hdrTraceID); err != nil {
  55. return nil, ErrInvalidTraceIDHeader
  56. }
  57. }
  58. if hdrSpanID != "" {
  59. requiredCount++
  60. if spanID, err = strconv.ParseUint(hdrSpanID, 16, 64); err != nil {
  61. return nil, ErrInvalidSpanIDHeader
  62. }
  63. sc.ID = model.ID(spanID)
  64. }
  65. if requiredCount != 0 && requiredCount != 2 {
  66. return nil, ErrInvalidScope
  67. }
  68. if hdrParentSpanID != "" {
  69. if requiredCount == 0 {
  70. return nil, ErrInvalidScopeParent
  71. }
  72. if spanID, err = strconv.ParseUint(hdrParentSpanID, 16, 64); err != nil {
  73. return nil, ErrInvalidParentSpanIDHeader
  74. }
  75. parentSpanID := model.ID(spanID)
  76. sc.ParentID = &parentSpanID
  77. }
  78. return sc, nil
  79. }
  80. // ParseSingleHeader takes values found from B3 Single Header and tries to reconstruct a
  81. // SpanContext.
  82. func ParseSingleHeader(contextHeader string) (*model.SpanContext, error) {
  83. if contextHeader == "" {
  84. return nil, ErrEmptyContext
  85. }
  86. var (
  87. sc = model.SpanContext{}
  88. sampling string
  89. )
  90. headerLen := len(contextHeader)
  91. if headerLen == 1 {
  92. sampling = contextHeader
  93. } else if headerLen == 16 || headerLen == 32 {
  94. return nil, ErrInvalidScope
  95. } else if headerLen >= 16+16+1 {
  96. var high, low uint64
  97. pos := 0
  98. if string(contextHeader[16]) != "-" {
  99. // traceID must be 128 bits
  100. var err error
  101. high, err = strconv.ParseUint(contextHeader[0:16], 16, 64)
  102. if err != nil {
  103. return nil, ErrInvalidTraceIDValue
  104. }
  105. pos = 16
  106. }
  107. low, err := strconv.ParseUint(contextHeader[pos:pos+16], 16, 64)
  108. if err != nil {
  109. return nil, ErrInvalidTraceIDValue
  110. }
  111. sc.TraceID = model.TraceID{High: high, Low: low}
  112. rawID, err := strconv.ParseUint(contextHeader[pos+16+1:pos+16+1+16], 16, 64)
  113. if err != nil {
  114. return nil, ErrInvalidSpanIDValue
  115. }
  116. sc.ID = model.ID(rawID)
  117. if headerLen > pos+16+1+16 {
  118. if headerLen == pos+16+1+16+1 {
  119. return nil, ErrInvalidSampledByte
  120. }
  121. if headerLen == pos+16+1+16+1+1 {
  122. sampling = string(contextHeader[pos+16+1+16+1])
  123. } else if headerLen == pos+16+1+16+1+16 {
  124. return nil, ErrInvalidScopeParentSingle
  125. } else if headerLen == pos+16+1+16+1+1+1+16 {
  126. sampling = string(contextHeader[pos+16+1+16+1])
  127. var rawParentID uint64
  128. rawParentID, err = strconv.ParseUint(contextHeader[pos+16+1+16+1+1+1:], 16, 64)
  129. if err != nil {
  130. return nil, ErrInvalidParentSpanIDValue
  131. }
  132. parentID := model.ID(rawParentID)
  133. sc.ParentID = &parentID
  134. } else {
  135. return nil, ErrInvalidParentSpanIDValue
  136. }
  137. }
  138. } else {
  139. return nil, ErrInvalidTraceIDValue
  140. }
  141. switch sampling {
  142. case "d":
  143. sc.Debug = true
  144. case "1":
  145. trueVal := true
  146. sc.Sampled = &trueVal
  147. case "0":
  148. falseVal := false
  149. sc.Sampled = &falseVal
  150. case "":
  151. default:
  152. return nil, ErrInvalidSampledByte
  153. }
  154. return &sc, nil
  155. }
  156. // BuildSingleHeader takes the values from the SpanContext and builds the B3 header
  157. func BuildSingleHeader(sc model.SpanContext) string {
  158. var header []string
  159. if !sc.TraceID.Empty() && sc.ID > 0 {
  160. header = append(header, sc.TraceID.String(), sc.ID.String())
  161. }
  162. if sc.Debug {
  163. header = append(header, "d")
  164. } else if sc.Sampled != nil {
  165. if *sc.Sampled {
  166. header = append(header, "1")
  167. } else {
  168. header = append(header, "0")
  169. }
  170. }
  171. if sc.ParentID != nil {
  172. header = append(header, sc.ParentID.String())
  173. }
  174. return strings.Join(header, "-")
  175. }