1// Copyright 2019, OpenTelemetry 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 15package trace 16 17import ( 18 "context" 19 "sync" 20 "time" 21 22 "google.golang.org/grpc/codes" 23 24 "go.opentelemetry.io/otel/api/core" 25 apitrace "go.opentelemetry.io/otel/api/trace" 26 export "go.opentelemetry.io/otel/sdk/export/trace" 27 "go.opentelemetry.io/otel/sdk/internal" 28) 29 30// span implements apitrace.Span interface. 31type span struct { 32 // data contains information recorded about the span. 33 // 34 // It will be non-nil if we are exporting the span or recording events for it. 35 // Otherwise, data is nil, and the span is simply a carrier for the 36 // SpanContext, so that the trace ID is propagated. 37 data *export.SpanData 38 mu sync.Mutex // protects the contents of *data (but not the pointer value.) 39 spanContext core.SpanContext 40 41 // attributes are capped at configured limit. When the capacity is reached an oldest entry 42 // is removed to create room for a new entry. 43 attributes *attributesMap 44 45 // messageEvents are stored in FIFO queue capped by configured limit. 46 messageEvents *evictedQueue 47 48 // links are stored in FIFO queue capped by configured limit. 49 links *evictedQueue 50 51 // spanStore is the spanStore this span belongs to, if any, otherwise it is nil. 52 //*spanStore 53 endOnce sync.Once 54 55 executionTracerTaskEnd func() // ends the execution tracer span 56 tracer *tracer // tracer used to create span. 57} 58 59var _ apitrace.Span = &span{} 60 61func (s *span) SpanContext() core.SpanContext { 62 if s == nil { 63 return core.EmptySpanContext() 64 } 65 return s.spanContext 66} 67 68func (s *span) IsRecording() bool { 69 if s == nil { 70 return false 71 } 72 return s.data != nil 73} 74 75func (s *span) SetStatus(status codes.Code) { 76 if s == nil { 77 return 78 } 79 if !s.IsRecording() { 80 return 81 } 82 s.mu.Lock() 83 s.data.Status = status 84 s.mu.Unlock() 85} 86 87func (s *span) SetAttributes(attributes ...core.KeyValue) { 88 if !s.IsRecording() { 89 return 90 } 91 s.copyToCappedAttributes(attributes...) 92} 93 94func (s *span) End(options ...apitrace.EndOption) { 95 if s == nil { 96 return 97 } 98 99 if s.executionTracerTaskEnd != nil { 100 s.executionTracerTaskEnd() 101 } 102 if !s.IsRecording() { 103 return 104 } 105 opts := apitrace.EndConfig{} 106 for _, opt := range options { 107 opt(&opts) 108 } 109 s.endOnce.Do(func() { 110 sps, _ := s.tracer.provider.spanProcessors.Load().(spanProcessorMap) 111 mustExportOrProcess := len(sps) > 0 112 if mustExportOrProcess { 113 sd := s.makeSpanData() 114 if opts.EndTime.IsZero() { 115 sd.EndTime = internal.MonotonicEndTime(sd.StartTime) 116 } else { 117 sd.EndTime = opts.EndTime 118 } 119 for sp := range sps { 120 sp.OnEnd(sd) 121 } 122 } 123 }) 124} 125 126func (s *span) Tracer() apitrace.Tracer { 127 return s.tracer 128} 129 130func (s *span) AddEvent(ctx context.Context, name string, attrs ...core.KeyValue) { 131 if !s.IsRecording() { 132 return 133 } 134 s.addEventWithTimestamp(time.Now(), name, attrs...) 135} 136 137func (s *span) AddEventWithTimestamp(ctx context.Context, timestamp time.Time, name string, attrs ...core.KeyValue) { 138 if !s.IsRecording() { 139 return 140 } 141 s.addEventWithTimestamp(timestamp, name, attrs...) 142} 143 144func (s *span) addEventWithTimestamp(timestamp time.Time, name string, attrs ...core.KeyValue) { 145 s.mu.Lock() 146 defer s.mu.Unlock() 147 s.messageEvents.add(export.Event{ 148 Name: name, 149 Attributes: attrs, 150 Time: timestamp, 151 }) 152} 153 154func (s *span) SetName(name string) { 155 s.mu.Lock() 156 defer s.mu.Unlock() 157 158 if s.data == nil { 159 // TODO: now what? 160 return 161 } 162 spanName := s.tracer.spanNameWithPrefix(name) 163 s.data.Name = spanName 164 // SAMPLING 165 noParent := !s.data.ParentSpanID.IsValid() 166 var ctx core.SpanContext 167 if noParent { 168 ctx = core.EmptySpanContext() 169 } else { 170 // FIXME: Where do we get the parent context from? 171 // From SpanStore? 172 ctx = s.data.SpanContext 173 } 174 data := samplingData{ 175 noParent: noParent, 176 remoteParent: s.data.HasRemoteParent, 177 parent: ctx, 178 name: spanName, 179 cfg: s.tracer.provider.config.Load().(*Config), 180 span: s, 181 } 182 makeSamplingDecision(data) 183} 184 185func (s *span) addLink(link apitrace.Link) { 186 if !s.IsRecording() { 187 return 188 } 189 s.mu.Lock() 190 defer s.mu.Unlock() 191 s.links.add(link) 192} 193 194// makeSpanData produces a SpanData representing the current state of the span. 195// It requires that s.data is non-nil. 196func (s *span) makeSpanData() *export.SpanData { 197 var sd export.SpanData 198 s.mu.Lock() 199 defer s.mu.Unlock() 200 sd = *s.data 201 202 s.attributes.toSpanData(&sd) 203 204 if len(s.messageEvents.queue) > 0 { 205 sd.MessageEvents = s.interfaceArrayToMessageEventArray() 206 sd.DroppedMessageEventCount = s.messageEvents.droppedCount 207 } 208 if len(s.links.queue) > 0 { 209 sd.Links = s.interfaceArrayToLinksArray() 210 sd.DroppedLinkCount = s.links.droppedCount 211 } 212 return &sd 213} 214 215func (s *span) interfaceArrayToLinksArray() []apitrace.Link { 216 linkArr := make([]apitrace.Link, 0) 217 for _, value := range s.links.queue { 218 linkArr = append(linkArr, value.(apitrace.Link)) 219 } 220 return linkArr 221} 222 223func (s *span) interfaceArrayToMessageEventArray() []export.Event { 224 messageEventArr := make([]export.Event, 0) 225 for _, value := range s.messageEvents.queue { 226 messageEventArr = append(messageEventArr, value.(export.Event)) 227 } 228 return messageEventArr 229} 230 231func (s *span) copyToCappedAttributes(attributes ...core.KeyValue) { 232 s.mu.Lock() 233 defer s.mu.Unlock() 234 for _, a := range attributes { 235 s.attributes.add(a) 236 } 237} 238 239func (s *span) addChild() { 240 if !s.IsRecording() { 241 return 242 } 243 s.mu.Lock() 244 s.data.ChildSpanCount++ 245 s.mu.Unlock() 246} 247 248func startSpanInternal(tr *tracer, name string, parent core.SpanContext, remoteParent bool, o apitrace.StartConfig) *span { 249 var noParent bool 250 span := &span{} 251 span.spanContext = parent 252 253 cfg := tr.provider.config.Load().(*Config) 254 255 if parent == core.EmptySpanContext() { 256 span.spanContext.TraceID = cfg.IDGenerator.NewTraceID() 257 noParent = true 258 } 259 span.spanContext.SpanID = cfg.IDGenerator.NewSpanID() 260 data := samplingData{ 261 noParent: noParent, 262 remoteParent: remoteParent, 263 parent: parent, 264 name: name, 265 cfg: cfg, 266 span: span, 267 } 268 makeSamplingDecision(data) 269 270 // TODO: [rghetia] restore when spanstore is added. 271 // if !internal.LocalSpanStoreEnabled && !span.spanContext.IsSampled() && !o.Record { 272 if !span.spanContext.IsSampled() && !o.Record { 273 return span 274 } 275 276 startTime := o.StartTime 277 if startTime.IsZero() { 278 startTime = time.Now() 279 } 280 span.data = &export.SpanData{ 281 SpanContext: span.spanContext, 282 StartTime: startTime, 283 SpanKind: apitrace.ValidateSpanKind(o.SpanKind), 284 Name: name, 285 HasRemoteParent: remoteParent, 286 } 287 span.attributes = newAttributesMap(cfg.MaxAttributesPerSpan) 288 span.messageEvents = newEvictedQueue(cfg.MaxEventsPerSpan) 289 span.links = newEvictedQueue(cfg.MaxLinksPerSpan) 290 291 if !noParent { 292 span.data.ParentSpanID = parent.SpanID 293 } 294 // TODO: [rghetia] restore when spanstore is added. 295 //if internal.LocalSpanStoreEnabled { 296 // ss := spanStoreForNameCreateIfNew(name) 297 // if ss != nil { 298 // span.spanStore = ss 299 // ss.add(span) 300 // } 301 //} 302 303 return span 304} 305 306type samplingData struct { 307 noParent bool 308 remoteParent bool 309 parent core.SpanContext 310 name string 311 cfg *Config 312 span *span 313} 314 315func makeSamplingDecision(data samplingData) { 316 if data.noParent || data.remoteParent { 317 // If this span is the child of a local span and no 318 // Sampler is set in the options, keep the parent's 319 // TraceFlags. 320 // 321 // Otherwise, consult the Sampler in the options if it 322 // is non-nil, otherwise the default sampler. 323 sampler := data.cfg.DefaultSampler 324 //if o.Sampler != nil { 325 // sampler = o.Sampler 326 //} 327 spanContext := &data.span.spanContext 328 sampled := sampler(SamplingParameters{ 329 ParentContext: data.parent, 330 TraceID: spanContext.TraceID, 331 SpanID: spanContext.SpanID, 332 Name: data.name, 333 HasRemoteParent: data.remoteParent}).Sample 334 if sampled { 335 spanContext.TraceFlags |= core.TraceFlagsSampled 336 } else { 337 spanContext.TraceFlags &^= core.TraceFlagsSampled 338 } 339 } 340} 341