1// Copyright The 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 // import "go.opentelemetry.io/otel/sdk/trace"
16
17import (
18	"context"
19	"fmt"
20	"sync"
21	"sync/atomic"
22
23	"go.opentelemetry.io/otel"
24	"go.opentelemetry.io/otel/trace"
25
26	"go.opentelemetry.io/otel/sdk/instrumentation"
27	"go.opentelemetry.io/otel/sdk/resource"
28)
29
30const (
31	defaultTracerName = "go.opentelemetry.io/otel/sdk/tracer"
32)
33
34// TODO (MrAlias): unify this API option design:
35// https://github.com/open-telemetry/opentelemetry-go/issues/536
36
37// TracerProviderConfig
38type TracerProviderConfig struct {
39	processors []SpanProcessor
40
41	// sampler is the default sampler used when creating new spans.
42	sampler Sampler
43
44	// idGenerator is used to generate all Span and Trace IDs when needed.
45	idGenerator IDGenerator
46
47	// spanLimits defines the attribute, event, and link limits for spans.
48	spanLimits SpanLimits
49
50	// resource contains attributes representing an entity that produces telemetry.
51	resource *resource.Resource
52}
53
54type TracerProviderOption func(*TracerProviderConfig)
55
56type TracerProvider struct {
57	mu             sync.Mutex
58	namedTracer    map[instrumentation.Library]*tracer
59	spanProcessors atomic.Value
60	sampler        Sampler
61	idGenerator    IDGenerator
62	spanLimits     SpanLimits
63	resource       *resource.Resource
64}
65
66var _ trace.TracerProvider = &TracerProvider{}
67
68// NewTracerProvider returns a new and configured TracerProvider.
69//
70// By default the returned TracerProvider is configured with:
71//  - a ParentBased(AlwaysSample) Sampler
72//  - a random number IDGenerator
73//  - the resource.Default() Resource
74//  - the default SpanLimits.
75//
76// The passed opts are used to override these default values and configure the
77// returned TracerProvider appropriately.
78func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider {
79	o := &TracerProviderConfig{}
80
81	for _, opt := range opts {
82		opt(o)
83	}
84
85	ensureValidTracerProviderConfig(o)
86
87	tp := &TracerProvider{
88		namedTracer: make(map[instrumentation.Library]*tracer),
89		sampler:     o.sampler,
90		idGenerator: o.idGenerator,
91		spanLimits:  o.spanLimits,
92		resource:    o.resource,
93	}
94
95	for _, sp := range o.processors {
96		tp.RegisterSpanProcessor(sp)
97	}
98
99	return tp
100}
101
102// Tracer returns a Tracer with the given name and options. If a Tracer for
103// the given name and options does not exist it is created, otherwise the
104// existing Tracer is returned.
105//
106// If name is empty, DefaultTracerName is used instead.
107//
108// This method is safe to be called concurrently.
109func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer {
110	c := trace.NewTracerConfig(opts...)
111
112	p.mu.Lock()
113	defer p.mu.Unlock()
114	if name == "" {
115		name = defaultTracerName
116	}
117	il := instrumentation.Library{
118		Name:    name,
119		Version: c.InstrumentationVersion,
120	}
121	t, ok := p.namedTracer[il]
122	if !ok {
123		t = &tracer{
124			provider:               p,
125			instrumentationLibrary: il,
126		}
127		p.namedTracer[il] = t
128	}
129	return t
130}
131
132// RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors
133func (p *TracerProvider) RegisterSpanProcessor(s SpanProcessor) {
134	p.mu.Lock()
135	defer p.mu.Unlock()
136	new := spanProcessorStates{}
137	if old, ok := p.spanProcessors.Load().(spanProcessorStates); ok {
138		new = append(new, old...)
139	}
140	newSpanSync := &spanProcessorState{
141		sp:    s,
142		state: &sync.Once{},
143	}
144	new = append(new, newSpanSync)
145	p.spanProcessors.Store(new)
146}
147
148// UnregisterSpanProcessor removes the given SpanProcessor from the list of SpanProcessors
149func (p *TracerProvider) UnregisterSpanProcessor(s SpanProcessor) {
150	p.mu.Lock()
151	defer p.mu.Unlock()
152	spss := spanProcessorStates{}
153	old, ok := p.spanProcessors.Load().(spanProcessorStates)
154	if !ok || len(old) == 0 {
155		return
156	}
157	spss = append(spss, old...)
158
159	// stop the span processor if it is started and remove it from the list
160	var stopOnce *spanProcessorState
161	var idx int
162	for i, sps := range spss {
163		if sps.sp == s {
164			stopOnce = sps
165			idx = i
166		}
167	}
168	if stopOnce != nil {
169		stopOnce.state.Do(func() {
170			if err := s.Shutdown(context.Background()); err != nil {
171				otel.Handle(err)
172			}
173		})
174	}
175	if len(spss) > 1 {
176		copy(spss[idx:], spss[idx+1:])
177	}
178	spss[len(spss)-1] = nil
179	spss = spss[:len(spss)-1]
180
181	p.spanProcessors.Store(spss)
182}
183
184// ForceFlush immediately exports all spans that have not yet been exported for
185// all the registered span processors.
186func (p *TracerProvider) ForceFlush(ctx context.Context) error {
187	spss, ok := p.spanProcessors.Load().(spanProcessorStates)
188	if !ok {
189		return fmt.Errorf("failed to load span processors")
190	}
191	if len(spss) == 0 {
192		return nil
193	}
194
195	for _, sps := range spss {
196		select {
197		case <-ctx.Done():
198			return ctx.Err()
199		default:
200		}
201
202		if err := sps.sp.ForceFlush(ctx); err != nil {
203			return err
204		}
205	}
206	return nil
207}
208
209// Shutdown shuts down the span processors in the order they were registered.
210func (p *TracerProvider) Shutdown(ctx context.Context) error {
211	spss, ok := p.spanProcessors.Load().(spanProcessorStates)
212	if !ok {
213		return fmt.Errorf("failed to load span processors")
214	}
215	if len(spss) == 0 {
216		return nil
217	}
218
219	for _, sps := range spss {
220		select {
221		case <-ctx.Done():
222			return ctx.Err()
223		default:
224		}
225
226		var err error
227		sps.state.Do(func() {
228			err = sps.sp.Shutdown(ctx)
229		})
230		if err != nil {
231			return err
232		}
233	}
234	return nil
235}
236
237// WithSyncer registers the exporter with the TracerProvider using a
238// SimpleSpanProcessor.
239func WithSyncer(e SpanExporter) TracerProviderOption {
240	return WithSpanProcessor(NewSimpleSpanProcessor(e))
241}
242
243// WithBatcher registers the exporter with the TracerProvider using a
244// BatchSpanProcessor configured with the passed opts.
245func WithBatcher(e SpanExporter, opts ...BatchSpanProcessorOption) TracerProviderOption {
246	return WithSpanProcessor(NewBatchSpanProcessor(e, opts...))
247}
248
249// WithSpanProcessor registers the SpanProcessor with a TracerProvider.
250func WithSpanProcessor(sp SpanProcessor) TracerProviderOption {
251	return func(opts *TracerProviderConfig) {
252		opts.processors = append(opts.processors, sp)
253	}
254}
255
256// WithResource returns a TracerProviderOption that will configure the
257// Resource r as a TracerProvider's Resource. The configured Resource is
258// referenced by all the Tracers the TracerProvider creates. It represents the
259// entity producing telemetry.
260//
261// If this option is not used, the TracerProvider will use the
262// resource.Default() Resource by default.
263func WithResource(r *resource.Resource) TracerProviderOption {
264	return func(opts *TracerProviderConfig) {
265		opts.resource = resource.Merge(resource.Environment(), r)
266	}
267}
268
269// WithIDGenerator returns a TracerProviderOption that will configure the
270// IDGenerator g as a TracerProvider's IDGenerator. The configured IDGenerator
271// is used by the Tracers the TracerProvider creates to generate new Span and
272// Trace IDs.
273//
274// If this option is not used, the TracerProvider will use a random number
275// IDGenerator by default.
276func WithIDGenerator(g IDGenerator) TracerProviderOption {
277	return func(opts *TracerProviderConfig) {
278		if g != nil {
279			opts.idGenerator = g
280		}
281	}
282}
283
284// WithSampler returns a TracerProviderOption that will configure the Sampler
285// s as a TracerProvider's Sampler. The configured Sampler is used by the
286// Tracers the TracerProvider creates to make their sampling decisions for the
287// Spans they create.
288//
289// If this option is not used, the TracerProvider will use a
290// ParentBased(AlwaysSample) Sampler by default.
291func WithSampler(s Sampler) TracerProviderOption {
292	return func(opts *TracerProviderConfig) {
293		if s != nil {
294			opts.sampler = s
295		}
296	}
297}
298
299// WithSpanLimits returns a TracerProviderOption that will configure the
300// SpanLimits sl as a TracerProvider's SpanLimits. The configured SpanLimits
301// are used used by the Tracers the TracerProvider and the Spans they create
302// to limit tracing resources used.
303//
304// If this option is not used, the TracerProvider will use the default
305// SpanLimits.
306func WithSpanLimits(sl SpanLimits) TracerProviderOption {
307	return func(opts *TracerProviderConfig) {
308		opts.spanLimits = sl
309	}
310}
311
312// ensureValidTracerProviderConfig ensures that given TracerProviderConfig is valid.
313func ensureValidTracerProviderConfig(cfg *TracerProviderConfig) {
314	if cfg.sampler == nil {
315		cfg.sampler = ParentBased(AlwaysSample())
316	}
317	if cfg.idGenerator == nil {
318		cfg.idGenerator = defaultIDGenerator()
319	}
320	cfg.spanLimits.ensureDefault()
321	if cfg.resource == nil {
322		cfg.resource = resource.Default()
323	}
324}
325