span_context.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. // Copyright (c) 2017 Uber Technologies, Inc.
  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 jaeger
  15. import (
  16. "errors"
  17. "fmt"
  18. "strconv"
  19. "strings"
  20. "sync"
  21. "go.uber.org/atomic"
  22. )
  23. const (
  24. flagSampled = 1
  25. flagDebug = 2
  26. flagFirehose = 8
  27. )
  28. var (
  29. errEmptyTracerStateString = errors.New("Cannot convert empty string to tracer state")
  30. errMalformedTracerStateString = errors.New("String does not match tracer state format")
  31. emptyContext = SpanContext{}
  32. )
  33. // TraceID represents unique 128bit identifier of a trace
  34. type TraceID struct {
  35. High, Low uint64
  36. }
  37. // SpanID represents unique 64bit identifier of a span
  38. type SpanID uint64
  39. // SpanContext represents propagated span identity and state
  40. type SpanContext struct {
  41. // traceID represents globally unique ID of the trace.
  42. // Usually generated as a random number.
  43. traceID TraceID
  44. // spanID represents span ID that must be unique within its trace,
  45. // but does not have to be globally unique.
  46. spanID SpanID
  47. // parentID refers to the ID of the parent span.
  48. // Should be 0 if the current span is a root span.
  49. parentID SpanID
  50. // Distributed Context baggage. The is a snapshot in time.
  51. baggage map[string]string
  52. // debugID can be set to some correlation ID when the context is being
  53. // extracted from a TextMap carrier.
  54. //
  55. // See JaegerDebugHeader in constants.go
  56. debugID string
  57. // samplingState is shared across all spans
  58. samplingState *samplingState
  59. // remote indicates that span context represents a remote parent
  60. remote bool
  61. }
  62. type samplingState struct {
  63. // Span context's state flags that are propagated across processes. Only lower 8 bits are used.
  64. // We use an int32 instead of byte to be able to use CAS operations.
  65. stateFlags atomic.Int32
  66. // When state is not final, sampling will be retried on other span write operations,
  67. // like SetOperationName / SetTag, and the spans will remain writable.
  68. final atomic.Bool
  69. // localRootSpan stores the SpanID of the first span created in this process for a given trace.
  70. localRootSpan SpanID
  71. // extendedState allows samplers to keep intermediate state.
  72. // The keys and values in this map are completely opaque: interface{} -> interface{}.
  73. extendedState sync.Map
  74. }
  75. func (s *samplingState) isLocalRootSpan(id SpanID) bool {
  76. return id == s.localRootSpan
  77. }
  78. func (s *samplingState) setFlag(newFlag int32) {
  79. swapped := false
  80. for !swapped {
  81. old := s.stateFlags.Load()
  82. swapped = s.stateFlags.CAS(old, old|newFlag)
  83. }
  84. }
  85. func (s *samplingState) unsetFlag(newFlag int32) {
  86. swapped := false
  87. for !swapped {
  88. old := s.stateFlags.Load()
  89. swapped = s.stateFlags.CAS(old, old&^newFlag)
  90. }
  91. }
  92. func (s *samplingState) setSampled() {
  93. s.setFlag(flagSampled)
  94. }
  95. func (s *samplingState) unsetSampled() {
  96. s.unsetFlag(flagSampled)
  97. }
  98. func (s *samplingState) setDebugAndSampled() {
  99. s.setFlag(flagDebug | flagSampled)
  100. }
  101. func (s *samplingState) setFirehose() {
  102. s.setFlag(flagFirehose)
  103. }
  104. func (s *samplingState) setFlags(flags byte) {
  105. s.stateFlags.Store(int32(flags))
  106. }
  107. func (s *samplingState) setFinal() {
  108. s.final.Store(true)
  109. }
  110. func (s *samplingState) flags() byte {
  111. return byte(s.stateFlags.Load())
  112. }
  113. func (s *samplingState) isSampled() bool {
  114. return s.stateFlags.Load()&flagSampled == flagSampled
  115. }
  116. func (s *samplingState) isDebug() bool {
  117. return s.stateFlags.Load()&flagDebug == flagDebug
  118. }
  119. func (s *samplingState) isFirehose() bool {
  120. return s.stateFlags.Load()&flagFirehose == flagFirehose
  121. }
  122. func (s *samplingState) isFinal() bool {
  123. return s.final.Load()
  124. }
  125. func (s *samplingState) extendedStateForKey(key interface{}, initValue func() interface{}) interface{} {
  126. if value, ok := s.extendedState.Load(key); ok {
  127. return value
  128. }
  129. value := initValue()
  130. value, _ = s.extendedState.LoadOrStore(key, value)
  131. return value
  132. }
  133. // ForeachBaggageItem implements ForeachBaggageItem() of opentracing.SpanContext
  134. func (c SpanContext) ForeachBaggageItem(handler func(k, v string) bool) {
  135. for k, v := range c.baggage {
  136. if !handler(k, v) {
  137. break
  138. }
  139. }
  140. }
  141. // IsSampled returns whether this trace was chosen for permanent storage
  142. // by the sampling mechanism of the tracer.
  143. func (c SpanContext) IsSampled() bool {
  144. return c.samplingState.isSampled()
  145. }
  146. // IsDebug indicates whether sampling was explicitly requested by the service.
  147. func (c SpanContext) IsDebug() bool {
  148. return c.samplingState.isDebug()
  149. }
  150. // IsSamplingFinalized indicates whether the sampling decision has been finalized.
  151. func (c SpanContext) IsSamplingFinalized() bool {
  152. return c.samplingState.isFinal()
  153. }
  154. // IsFirehose indicates whether the firehose flag was set
  155. func (c SpanContext) IsFirehose() bool {
  156. return c.samplingState.isFirehose()
  157. }
  158. // ExtendedSamplingState returns the custom state object for a given key. If the value for this key does not exist,
  159. // it is initialized via initValue function. This state can be used by samplers (e.g. x.PrioritySampler).
  160. func (c SpanContext) ExtendedSamplingState(key interface{}, initValue func() interface{}) interface{} {
  161. return c.samplingState.extendedStateForKey(key, initValue)
  162. }
  163. // IsValid indicates whether this context actually represents a valid trace.
  164. func (c SpanContext) IsValid() bool {
  165. return c.traceID.IsValid() && c.spanID != 0
  166. }
  167. // SetFirehose enables firehose mode for this trace.
  168. func (c SpanContext) SetFirehose() {
  169. c.samplingState.setFirehose()
  170. }
  171. func (c SpanContext) String() string {
  172. var flags int32
  173. if c.samplingState != nil {
  174. flags = c.samplingState.stateFlags.Load()
  175. }
  176. if c.traceID.High == 0 {
  177. return fmt.Sprintf("%016x:%016x:%016x:%x", c.traceID.Low, uint64(c.spanID), uint64(c.parentID), flags)
  178. }
  179. return fmt.Sprintf("%016x%016x:%016x:%016x:%x", c.traceID.High, c.traceID.Low, uint64(c.spanID), uint64(c.parentID), flags)
  180. }
  181. // ContextFromString reconstructs the Context encoded in a string
  182. func ContextFromString(value string) (SpanContext, error) {
  183. var context SpanContext
  184. if value == "" {
  185. return emptyContext, errEmptyTracerStateString
  186. }
  187. parts := strings.Split(value, ":")
  188. if len(parts) != 4 {
  189. return emptyContext, errMalformedTracerStateString
  190. }
  191. var err error
  192. if context.traceID, err = TraceIDFromString(parts[0]); err != nil {
  193. return emptyContext, err
  194. }
  195. if context.spanID, err = SpanIDFromString(parts[1]); err != nil {
  196. return emptyContext, err
  197. }
  198. if context.parentID, err = SpanIDFromString(parts[2]); err != nil {
  199. return emptyContext, err
  200. }
  201. flags, err := strconv.ParseUint(parts[3], 10, 8)
  202. if err != nil {
  203. return emptyContext, err
  204. }
  205. context.samplingState = &samplingState{}
  206. context.samplingState.setFlags(byte(flags))
  207. return context, nil
  208. }
  209. // TraceID returns the trace ID of this span context
  210. func (c SpanContext) TraceID() TraceID {
  211. return c.traceID
  212. }
  213. // SpanID returns the span ID of this span context
  214. func (c SpanContext) SpanID() SpanID {
  215. return c.spanID
  216. }
  217. // ParentID returns the parent span ID of this span context
  218. func (c SpanContext) ParentID() SpanID {
  219. return c.parentID
  220. }
  221. // Flags returns the bitmap containing such bits as 'sampled' and 'debug'.
  222. func (c SpanContext) Flags() byte {
  223. return c.samplingState.flags()
  224. }
  225. // Span can be written to if it is sampled or the sampling decision has not been finalized.
  226. func (c SpanContext) isWriteable() bool {
  227. state := c.samplingState
  228. return !state.isFinal() || state.isSampled()
  229. }
  230. func (c SpanContext) isSamplingFinalized() bool {
  231. return c.samplingState.isFinal()
  232. }
  233. // NewSpanContext creates a new instance of SpanContext
  234. func NewSpanContext(traceID TraceID, spanID, parentID SpanID, sampled bool, baggage map[string]string) SpanContext {
  235. samplingState := &samplingState{}
  236. if sampled {
  237. samplingState.setSampled()
  238. }
  239. return SpanContext{
  240. traceID: traceID,
  241. spanID: spanID,
  242. parentID: parentID,
  243. samplingState: samplingState,
  244. baggage: baggage}
  245. }
  246. // CopyFrom copies data from ctx into this context, including span identity and baggage.
  247. // TODO This is only used by interop.go. Remove once TChannel Go supports OpenTracing.
  248. func (c *SpanContext) CopyFrom(ctx *SpanContext) {
  249. c.traceID = ctx.traceID
  250. c.spanID = ctx.spanID
  251. c.parentID = ctx.parentID
  252. c.samplingState = ctx.samplingState
  253. if l := len(ctx.baggage); l > 0 {
  254. c.baggage = make(map[string]string, l)
  255. for k, v := range ctx.baggage {
  256. c.baggage[k] = v
  257. }
  258. } else {
  259. c.baggage = nil
  260. }
  261. }
  262. // WithBaggageItem creates a new context with an extra baggage item.
  263. // Delete a baggage item if provided blank value.
  264. //
  265. // The SpanContext is designed to be immutable and passed by value. As such,
  266. // it cannot contain any locks, and should only hold immutable data, including baggage.
  267. // Another reason for why baggage is immutable is when the span context is passed
  268. // as a parent when starting a new span. The new span's baggage cannot affect the parent
  269. // span's baggage, so the child span either needs to take a copy of the parent baggage
  270. // (which is expensive and unnecessary since baggage rarely changes in the life span of
  271. // a trace), or it needs to do a copy-on-write, which is the approach taken here.
  272. func (c SpanContext) WithBaggageItem(key, value string) SpanContext {
  273. var newBaggage map[string]string
  274. // unset baggage item
  275. if value == "" {
  276. if _, ok := c.baggage[key]; !ok {
  277. return c
  278. }
  279. newBaggage = make(map[string]string, len(c.baggage))
  280. for k, v := range c.baggage {
  281. newBaggage[k] = v
  282. }
  283. delete(newBaggage, key)
  284. return SpanContext{c.traceID, c.spanID, c.parentID, newBaggage, "", c.samplingState, c.remote}
  285. }
  286. if c.baggage == nil {
  287. newBaggage = map[string]string{key: value}
  288. } else {
  289. newBaggage = make(map[string]string, len(c.baggage)+1)
  290. for k, v := range c.baggage {
  291. newBaggage[k] = v
  292. }
  293. newBaggage[key] = value
  294. }
  295. // Use positional parameters so the compiler will help catch new fields.
  296. return SpanContext{c.traceID, c.spanID, c.parentID, newBaggage, "", c.samplingState, c.remote}
  297. }
  298. // isDebugIDContainerOnly returns true when the instance of the context is only
  299. // used to return the debug/correlation ID from extract() method. This happens
  300. // in the situation when "jaeger-debug-id" header is passed in the carrier to
  301. // the extract() method, but the request otherwise has no span context in it.
  302. // Previously this would've returned opentracing.ErrSpanContextNotFound from the
  303. // extract method, but now it returns a dummy context with only debugID filled in.
  304. //
  305. // See JaegerDebugHeader in constants.go
  306. // See TextMapPropagator#Extract
  307. func (c *SpanContext) isDebugIDContainerOnly() bool {
  308. return !c.traceID.IsValid() && c.debugID != ""
  309. }
  310. // ------- TraceID -------
  311. func (t TraceID) String() string {
  312. if t.High == 0 {
  313. return fmt.Sprintf("%016x", t.Low)
  314. }
  315. return fmt.Sprintf("%016x%016x", t.High, t.Low)
  316. }
  317. // TraceIDFromString creates a TraceID from a hexadecimal string
  318. func TraceIDFromString(s string) (TraceID, error) {
  319. var hi, lo uint64
  320. var err error
  321. if len(s) > 32 {
  322. return TraceID{}, fmt.Errorf("TraceID cannot be longer than 32 hex characters: %s", s)
  323. } else if len(s) > 16 {
  324. hiLen := len(s) - 16
  325. if hi, err = strconv.ParseUint(s[0:hiLen], 16, 64); err != nil {
  326. return TraceID{}, err
  327. }
  328. if lo, err = strconv.ParseUint(s[hiLen:], 16, 64); err != nil {
  329. return TraceID{}, err
  330. }
  331. } else {
  332. if lo, err = strconv.ParseUint(s, 16, 64); err != nil {
  333. return TraceID{}, err
  334. }
  335. }
  336. return TraceID{High: hi, Low: lo}, nil
  337. }
  338. // IsValid checks if the trace ID is valid, i.e. not zero.
  339. func (t TraceID) IsValid() bool {
  340. return t.High != 0 || t.Low != 0
  341. }
  342. // ------- SpanID -------
  343. func (s SpanID) String() string {
  344. return fmt.Sprintf("%016x", uint64(s))
  345. }
  346. // SpanIDFromString creates a SpanID from a hexadecimal string
  347. func SpanIDFromString(s string) (SpanID, error) {
  348. if len(s) > 16 {
  349. return SpanID(0), fmt.Errorf("SpanID cannot be longer than 16 hex characters: %s", s)
  350. }
  351. id, err := strconv.ParseUint(s, 16, 64)
  352. if err != nil {
  353. return SpanID(0), err
  354. }
  355. return SpanID(id), nil
  356. }