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