context.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. // Copyright (c) 2016 Uber Technologies, Inc.
  2. // Permission is hereby granted, free of charge, to any person obtaining a copy
  3. // of this software and associated documentation files (the "Software"), to deal
  4. // in the Software without restriction, including without limitation the rights
  5. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  6. // copies of the Software, and to permit persons to whom the Software is
  7. // furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in
  10. // all copies or substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. // THE SOFTWARE.
  19. package jaeger
  20. import (
  21. "errors"
  22. "fmt"
  23. "strconv"
  24. "strings"
  25. )
  26. const (
  27. flagSampled = byte(1)
  28. flagDebug = byte(2)
  29. )
  30. var (
  31. errEmptyTracerStateString = errors.New("Cannot convert empty string to tracer state")
  32. errMalformedTracerStateString = errors.New("String does not match tracer state format")
  33. emptyContext = SpanContext{}
  34. )
  35. // TraceID represents unique 128bit identifier of a trace
  36. type TraceID struct {
  37. High, Low uint64
  38. }
  39. // SpanID represents unique 64bit identifier of a span
  40. type SpanID uint64
  41. // SpanContext represents propagated span identity and state
  42. type SpanContext struct {
  43. // traceID represents globally unique ID of the trace.
  44. // Usually generated as a random number.
  45. traceID TraceID
  46. // spanID represents span ID that must be unique within its trace,
  47. // but does not have to be globally unique.
  48. spanID SpanID
  49. // parentID refers to the ID of the parent span.
  50. // Should be 0 if the current span is a root span.
  51. parentID SpanID
  52. // flags is a bitmap containing such bits as 'sampled' and 'debug'.
  53. flags byte
  54. // Distributed Context baggage. The is a snapshot in time.
  55. baggage map[string]string
  56. // debugID can be set to some correlation ID when the context is being
  57. // extracted from a TextMap carrier.
  58. //
  59. // See JaegerDebugHeader in constants.go
  60. debugID string
  61. }
  62. // ForeachBaggageItem implements ForeachBaggageItem() of opentracing.SpanContext
  63. func (c SpanContext) ForeachBaggageItem(handler func(k, v string) bool) {
  64. for k, v := range c.baggage {
  65. if !handler(k, v) {
  66. break
  67. }
  68. }
  69. }
  70. // IsSampled returns whether this trace was chosen for permanent storage
  71. // by the sampling mechanism of the tracer.
  72. func (c SpanContext) IsSampled() bool {
  73. return (c.flags & flagSampled) == flagSampled
  74. }
  75. // IsDebug indicates whether sampling was explicitly requested by the service.
  76. func (c SpanContext) IsDebug() bool {
  77. return (c.flags & flagDebug) == flagDebug
  78. }
  79. // IsValid indicates whether this context actually represents a valid trace.
  80. func (c SpanContext) IsValid() bool {
  81. return c.traceID.IsValid() && c.spanID != 0
  82. }
  83. func (c SpanContext) String() string {
  84. if c.traceID.High == 0 {
  85. return fmt.Sprintf("%x:%x:%x:%x", c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.flags)
  86. }
  87. return fmt.Sprintf("%x%016x:%x:%x:%x", c.traceID.High, c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.flags)
  88. }
  89. // ContextFromString reconstructs the Context encoded in a string
  90. func ContextFromString(value string) (SpanContext, error) {
  91. var context SpanContext
  92. if value == "" {
  93. return emptyContext, errEmptyTracerStateString
  94. }
  95. parts := strings.Split(value, ":")
  96. if len(parts) != 4 {
  97. return emptyContext, errMalformedTracerStateString
  98. }
  99. var err error
  100. if context.traceID, err = TraceIDFromString(parts[0]); err != nil {
  101. return emptyContext, err
  102. }
  103. if context.spanID, err = SpanIDFromString(parts[1]); err != nil {
  104. return emptyContext, err
  105. }
  106. if context.parentID, err = SpanIDFromString(parts[2]); err != nil {
  107. return emptyContext, err
  108. }
  109. flags, err := strconv.ParseUint(parts[3], 10, 8)
  110. if err != nil {
  111. return emptyContext, err
  112. }
  113. context.flags = byte(flags)
  114. return context, nil
  115. }
  116. // TraceID returns the trace ID of this span context
  117. func (c SpanContext) TraceID() TraceID {
  118. return c.traceID
  119. }
  120. // SpanID returns the span ID of this span context
  121. func (c SpanContext) SpanID() SpanID {
  122. return c.spanID
  123. }
  124. // ParentID returns the parent span ID of this span context
  125. func (c SpanContext) ParentID() SpanID {
  126. return c.parentID
  127. }
  128. // NewSpanContext creates a new instance of SpanContext
  129. func NewSpanContext(traceID TraceID, spanID, parentID SpanID, sampled bool, baggage map[string]string) SpanContext {
  130. flags := byte(0)
  131. if sampled {
  132. flags = flagSampled
  133. }
  134. return SpanContext{
  135. traceID: traceID,
  136. spanID: spanID,
  137. parentID: parentID,
  138. flags: flags,
  139. baggage: baggage}
  140. }
  141. // CopyFrom copies data from ctx into this context, including span identity and baggage.
  142. // TODO This is only used by interop.go. Remove once TChannel Go supports OpenTracing.
  143. func (c *SpanContext) CopyFrom(ctx *SpanContext) {
  144. c.traceID = ctx.traceID
  145. c.spanID = ctx.spanID
  146. c.parentID = ctx.parentID
  147. c.flags = ctx.flags
  148. if l := len(ctx.baggage); l > 0 {
  149. c.baggage = make(map[string]string, l)
  150. for k, v := range ctx.baggage {
  151. c.baggage[k] = v
  152. }
  153. } else {
  154. c.baggage = nil
  155. }
  156. }
  157. // WithBaggageItem creates a new context with an extra baggage item.
  158. func (c SpanContext) WithBaggageItem(key, value string) SpanContext {
  159. var newBaggage map[string]string
  160. if c.baggage == nil {
  161. newBaggage = map[string]string{key: value}
  162. } else {
  163. newBaggage = make(map[string]string, len(c.baggage)+1)
  164. for k, v := range c.baggage {
  165. newBaggage[k] = v
  166. }
  167. newBaggage[key] = value
  168. }
  169. // Use positional parameters so the compiler will help catch new fields.
  170. return SpanContext{c.traceID, c.spanID, c.parentID, c.flags, newBaggage, ""}
  171. }
  172. // isDebugIDContainerOnly returns true when the instance of the context is only
  173. // used to return the debug/correlation ID from extract() method. This happens
  174. // in the situation when "jaeger-debug-id" header is passed in the carrier to
  175. // the extract() method, but the request otherwise has no span context in it.
  176. // Previously this would've returned opentracing.ErrSpanContextNotFound from the
  177. // extract method, but now it returns a dummy context with only debugID filled in.
  178. //
  179. // See JaegerDebugHeader in constants.go
  180. // See textMapPropagator#Extract
  181. func (c *SpanContext) isDebugIDContainerOnly() bool {
  182. return !c.traceID.IsValid() && c.debugID != ""
  183. }
  184. // ------- TraceID -------
  185. func (t TraceID) String() string {
  186. if t.High == 0 {
  187. return fmt.Sprintf("%x", t.Low)
  188. }
  189. return fmt.Sprintf("%x%016x", t.High, t.Low)
  190. }
  191. // TraceIDFromString creates a TraceID from a hexadecimal string
  192. func TraceIDFromString(s string) (TraceID, error) {
  193. var hi, lo uint64
  194. var err error
  195. if len(s) > 32 {
  196. return TraceID{}, fmt.Errorf("TraceID cannot be longer than 32 hex characters: %s", s)
  197. } else if len(s) > 16 {
  198. hiLen := len(s) - 16
  199. if hi, err = strconv.ParseUint(s[0:hiLen], 16, 64); err != nil {
  200. return TraceID{}, err
  201. }
  202. if lo, err = strconv.ParseUint(s[hiLen:], 16, 64); err != nil {
  203. return TraceID{}, err
  204. }
  205. } else {
  206. if lo, err = strconv.ParseUint(s, 16, 64); err != nil {
  207. return TraceID{}, err
  208. }
  209. }
  210. return TraceID{High: hi, Low: lo}, nil
  211. }
  212. // IsValid checks if the trace ID is valid, i.e. not zero.
  213. func (t TraceID) IsValid() bool {
  214. return t.High != 0 || t.Low != 0
  215. }
  216. // ------- SpanID -------
  217. func (s SpanID) String() string {
  218. return fmt.Sprintf("%x", uint64(s))
  219. }
  220. // SpanIDFromString creates a SpanID from a hexadecimal string
  221. func SpanIDFromString(s string) (SpanID, error) {
  222. if len(s) > 16 {
  223. return SpanID(0), fmt.Errorf("SpanID cannot be longer than 16 hex characters: %s", s)
  224. }
  225. id, err := strconv.ParseUint(s, 16, 64)
  226. if err != nil {
  227. return SpanID(0), err
  228. }
  229. return SpanID(id), nil
  230. }