span.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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. "strings"
  22. "sync"
  23. "time"
  24. "github.com/opentracing/opentracing-go"
  25. "github.com/opentracing/opentracing-go/ext"
  26. "github.com/opentracing/opentracing-go/log"
  27. )
  28. // Span implements opentracing.Span
  29. type Span struct {
  30. sync.RWMutex
  31. tracer *Tracer
  32. context SpanContext
  33. // The name of the "operation" this span is an instance of.
  34. // Known as a "span name" in some implementations.
  35. operationName string
  36. // firstInProcess, if true, indicates that this span is the root of the (sub)tree
  37. // of spans in the current process. In other words it's true for the root spans,
  38. // and the ingress spans when the process joins another trace.
  39. firstInProcess bool
  40. // startTime is the timestamp indicating when the span began, with microseconds precision.
  41. startTime time.Time
  42. // duration returns duration of the span with microseconds precision.
  43. // Zero value means duration is unknown.
  44. duration time.Duration
  45. // tags attached to this span
  46. tags []Tag
  47. // The span's "micro-log"
  48. logs []opentracing.LogRecord
  49. // references for this span
  50. references []Reference
  51. observer ContribSpanObserver
  52. }
  53. // Tag is a simple key value wrapper.
  54. // TODO deprecate in the next major release, use opentracing.Tag instead.
  55. type Tag struct {
  56. key string
  57. value interface{}
  58. }
  59. // SetOperationName sets or changes the operation name.
  60. func (s *Span) SetOperationName(operationName string) opentracing.Span {
  61. s.Lock()
  62. defer s.Unlock()
  63. if s.context.IsSampled() {
  64. s.operationName = operationName
  65. }
  66. s.observer.OnSetOperationName(operationName)
  67. return s
  68. }
  69. // SetTag implements SetTag() of opentracing.Span
  70. func (s *Span) SetTag(key string, value interface{}) opentracing.Span {
  71. s.observer.OnSetTag(key, value)
  72. if key == string(ext.SamplingPriority) && setSamplingPriority(s, value) {
  73. return s
  74. }
  75. s.Lock()
  76. defer s.Unlock()
  77. if s.context.IsSampled() {
  78. s.setTagNoLocking(key, value)
  79. }
  80. return s
  81. }
  82. func (s *Span) setTagNoLocking(key string, value interface{}) {
  83. s.tags = append(s.tags, Tag{key: key, value: value})
  84. }
  85. // LogFields implements opentracing.Span API
  86. func (s *Span) LogFields(fields ...log.Field) {
  87. s.Lock()
  88. defer s.Unlock()
  89. if !s.context.IsSampled() {
  90. return
  91. }
  92. s.logFieldsNoLocking(fields...)
  93. }
  94. // this function should only be called while holding a Write lock
  95. func (s *Span) logFieldsNoLocking(fields ...log.Field) {
  96. lr := opentracing.LogRecord{
  97. Fields: fields,
  98. Timestamp: time.Now(),
  99. }
  100. s.appendLog(lr)
  101. }
  102. // LogKV implements opentracing.Span API
  103. func (s *Span) LogKV(alternatingKeyValues ...interface{}) {
  104. s.RLock()
  105. sampled := s.context.IsSampled()
  106. s.RUnlock()
  107. if !sampled {
  108. return
  109. }
  110. fields, err := log.InterleavedKVToFields(alternatingKeyValues...)
  111. if err != nil {
  112. s.LogFields(log.Error(err), log.String("function", "LogKV"))
  113. return
  114. }
  115. s.LogFields(fields...)
  116. }
  117. // LogEvent implements opentracing.Span API
  118. func (s *Span) LogEvent(event string) {
  119. s.Log(opentracing.LogData{Event: event})
  120. }
  121. // LogEventWithPayload implements opentracing.Span API
  122. func (s *Span) LogEventWithPayload(event string, payload interface{}) {
  123. s.Log(opentracing.LogData{Event: event, Payload: payload})
  124. }
  125. // Log implements opentracing.Span API
  126. func (s *Span) Log(ld opentracing.LogData) {
  127. s.Lock()
  128. defer s.Unlock()
  129. if s.context.IsSampled() {
  130. if ld.Timestamp.IsZero() {
  131. ld.Timestamp = s.tracer.timeNow()
  132. }
  133. s.appendLog(ld.ToLogRecord())
  134. }
  135. }
  136. // this function should only be called while holding a Write lock
  137. func (s *Span) appendLog(lr opentracing.LogRecord) {
  138. // TODO add logic to limit number of logs per span (issue #46)
  139. s.logs = append(s.logs, lr)
  140. }
  141. // SetBaggageItem implements SetBaggageItem() of opentracing.SpanContext
  142. func (s *Span) SetBaggageItem(key, value string) opentracing.Span {
  143. key = normalizeBaggageKey(key)
  144. s.Lock()
  145. defer s.Unlock()
  146. s.tracer.setBaggage(s, key, value)
  147. return s
  148. }
  149. // BaggageItem implements BaggageItem() of opentracing.SpanContext
  150. func (s *Span) BaggageItem(key string) string {
  151. key = normalizeBaggageKey(key)
  152. s.RLock()
  153. defer s.RUnlock()
  154. return s.context.baggage[key]
  155. }
  156. // Finish implements opentracing.Span API
  157. func (s *Span) Finish() {
  158. s.FinishWithOptions(opentracing.FinishOptions{})
  159. }
  160. // FinishWithOptions implements opentracing.Span API
  161. func (s *Span) FinishWithOptions(options opentracing.FinishOptions) {
  162. if options.FinishTime.IsZero() {
  163. options.FinishTime = s.tracer.timeNow()
  164. }
  165. s.observer.OnFinish(options)
  166. s.Lock()
  167. if s.context.IsSampled() {
  168. s.duration = options.FinishTime.Sub(s.startTime)
  169. // Note: bulk logs are not subject to maxLogsPerSpan limit
  170. if options.LogRecords != nil {
  171. s.logs = append(s.logs, options.LogRecords...)
  172. }
  173. for _, ld := range options.BulkLogData {
  174. s.logs = append(s.logs, ld.ToLogRecord())
  175. }
  176. }
  177. s.Unlock()
  178. // call reportSpan even for non-sampled traces, to return span to the pool
  179. s.tracer.reportSpan(s)
  180. }
  181. // Context implements opentracing.Span API
  182. func (s *Span) Context() opentracing.SpanContext {
  183. return s.context
  184. }
  185. // Tracer implements opentracing.Span API
  186. func (s *Span) Tracer() opentracing.Tracer {
  187. return s.tracer
  188. }
  189. func (s *Span) String() string {
  190. s.RLock()
  191. defer s.RUnlock()
  192. return s.context.String()
  193. }
  194. // OperationName allows retrieving current operation name.
  195. func (s *Span) OperationName() string {
  196. s.RLock()
  197. defer s.RUnlock()
  198. return s.operationName
  199. }
  200. func setSamplingPriority(s *Span, value interface{}) bool {
  201. s.Lock()
  202. defer s.Unlock()
  203. if val, ok := value.(uint16); ok {
  204. if val > 0 {
  205. s.context.flags = s.context.flags | flagDebug | flagSampled
  206. } else {
  207. s.context.flags = s.context.flags & (^flagSampled)
  208. }
  209. return true
  210. }
  211. return false
  212. }
  213. // Converts end-user baggage key into internal representation.
  214. // Used for both read and write access to baggage items.
  215. func normalizeBaggageKey(key string) string {
  216. // TODO(yurishkuro) normalizeBaggageKey: cache the results in some bounded LRU cache
  217. return strings.Replace(strings.ToLower(key), "_", "-", -1)
  218. }