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