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	"sync"
20	"sync/atomic"
21
22	"go.opentelemetry.io/otel"
23	"go.opentelemetry.io/otel/trace"
24
25	export "go.opentelemetry.io/otel/sdk/export/trace"
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	config     Config
41}
42
43type TracerProviderOption func(*TracerProviderConfig)
44
45type TracerProvider struct {
46	mu             sync.Mutex
47	namedTracer    map[instrumentation.Library]*tracer
48	spanProcessors atomic.Value
49	config         atomic.Value // access atomically
50}
51
52var _ trace.TracerProvider = &TracerProvider{}
53
54// NewTracerProvider creates an instance of trace provider. Optional
55// parameter configures the provider with common options applicable
56// to all tracer instances that will be created by this provider.
57func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider {
58	o := &TracerProviderConfig{}
59
60	for _, opt := range opts {
61		opt(o)
62	}
63
64	tp := &TracerProvider{
65		namedTracer: make(map[instrumentation.Library]*tracer),
66	}
67	tp.config.Store(&Config{
68		DefaultSampler: ParentBased(AlwaysSample()),
69		IDGenerator:    defaultIDGenerator(),
70		SpanLimits: SpanLimits{
71			AttributeCountLimit:         DefaultAttributeCountLimit,
72			EventCountLimit:             DefaultEventCountLimit,
73			LinkCountLimit:              DefaultLinkCountLimit,
74			AttributePerEventCountLimit: DefaultAttributePerEventCountLimit,
75			AttributePerLinkCountLimit:  DefaultAttributePerLinkCountLimit,
76		},
77	})
78
79	for _, sp := range o.processors {
80		tp.RegisterSpanProcessor(sp)
81	}
82
83	tp.ApplyConfig(o.config)
84
85	return tp
86}
87
88// Tracer with the given name. If a tracer for the given name does not exist,
89// it is created first. If the name is empty, DefaultTracerName is used.
90func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer {
91	c := trace.NewTracerConfig(opts...)
92
93	p.mu.Lock()
94	defer p.mu.Unlock()
95	if name == "" {
96		name = defaultTracerName
97	}
98	il := instrumentation.Library{
99		Name:    name,
100		Version: c.InstrumentationVersion,
101	}
102	t, ok := p.namedTracer[il]
103	if !ok {
104		t = &tracer{
105			provider:               p,
106			instrumentationLibrary: il,
107		}
108		p.namedTracer[il] = t
109	}
110	return t
111}
112
113// RegisterSpanProcessor adds the given SpanProcessor to the list of SpanProcessors
114func (p *TracerProvider) RegisterSpanProcessor(s SpanProcessor) {
115	p.mu.Lock()
116	defer p.mu.Unlock()
117	new := spanProcessorStates{}
118	if old, ok := p.spanProcessors.Load().(spanProcessorStates); ok {
119		new = append(new, old...)
120	}
121	newSpanSync := &spanProcessorState{
122		sp:    s,
123		state: &sync.Once{},
124	}
125	new = append(new, newSpanSync)
126	p.spanProcessors.Store(new)
127}
128
129// UnregisterSpanProcessor removes the given SpanProcessor from the list of SpanProcessors
130func (p *TracerProvider) UnregisterSpanProcessor(s SpanProcessor) {
131	p.mu.Lock()
132	defer p.mu.Unlock()
133	spss := spanProcessorStates{}
134	old, ok := p.spanProcessors.Load().(spanProcessorStates)
135	if !ok || len(old) == 0 {
136		return
137	}
138	spss = append(spss, old...)
139
140	// stop the span processor if it is started and remove it from the list
141	var stopOnce *spanProcessorState
142	var idx int
143	for i, sps := range spss {
144		if sps.sp == s {
145			stopOnce = sps
146			idx = i
147		}
148	}
149	if stopOnce != nil {
150		stopOnce.state.Do(func() {
151			if err := s.Shutdown(context.Background()); err != nil {
152				otel.Handle(err)
153			}
154		})
155	}
156	if len(spss) > 1 {
157		copy(spss[idx:], spss[idx+1:])
158	}
159	spss[len(spss)-1] = nil
160	spss = spss[:len(spss)-1]
161
162	p.spanProcessors.Store(spss)
163}
164
165// ApplyConfig changes the configuration of the provider.
166// If a field in the configuration is empty or nil then its original value is preserved.
167func (p *TracerProvider) ApplyConfig(cfg Config) {
168	p.mu.Lock()
169	defer p.mu.Unlock()
170	c := *p.config.Load().(*Config)
171	if cfg.DefaultSampler != nil {
172		c.DefaultSampler = cfg.DefaultSampler
173	}
174	if cfg.IDGenerator != nil {
175		c.IDGenerator = cfg.IDGenerator
176	}
177	if cfg.SpanLimits.EventCountLimit > 0 {
178		c.SpanLimits.EventCountLimit = cfg.SpanLimits.EventCountLimit
179	}
180	if cfg.SpanLimits.AttributeCountLimit > 0 {
181		c.SpanLimits.AttributeCountLimit = cfg.SpanLimits.AttributeCountLimit
182	}
183	if cfg.SpanLimits.LinkCountLimit > 0 {
184		c.SpanLimits.LinkCountLimit = cfg.SpanLimits.LinkCountLimit
185	}
186	if cfg.SpanLimits.AttributePerEventCountLimit > 0 {
187		c.SpanLimits.AttributePerEventCountLimit = cfg.SpanLimits.AttributePerEventCountLimit
188	}
189	if cfg.SpanLimits.AttributePerLinkCountLimit > 0 {
190		c.SpanLimits.AttributePerLinkCountLimit = cfg.SpanLimits.AttributePerLinkCountLimit
191	}
192	c.Resource = cfg.Resource
193	if c.Resource == nil {
194		c.Resource = resource.Default()
195	}
196	p.config.Store(&c)
197}
198
199// Shutdown shuts down the span processors in the order they were registered
200func (p *TracerProvider) Shutdown(ctx context.Context) error {
201	spss, ok := p.spanProcessors.Load().(spanProcessorStates)
202	if !ok || len(spss) == 0 {
203		return nil
204	}
205
206	for _, sps := range spss {
207		sps.state.Do(func() {
208			if err := sps.sp.Shutdown(ctx); err != nil {
209				otel.Handle(err)
210			}
211		})
212	}
213	return nil
214}
215
216// WithSyncer registers the exporter with the TracerProvider using a
217// SimpleSpanProcessor.
218func WithSyncer(e export.SpanExporter) TracerProviderOption {
219	return WithSpanProcessor(NewSimpleSpanProcessor(e))
220}
221
222// WithBatcher registers the exporter with the TracerProvider using a
223// BatchSpanProcessor configured with the passed opts.
224func WithBatcher(e export.SpanExporter, opts ...BatchSpanProcessorOption) TracerProviderOption {
225	return WithSpanProcessor(NewBatchSpanProcessor(e, opts...))
226}
227
228// WithSpanProcessor registers the SpanProcessor with a TracerProvider.
229func WithSpanProcessor(sp SpanProcessor) TracerProviderOption {
230	return func(opts *TracerProviderConfig) {
231		opts.processors = append(opts.processors, sp)
232	}
233}
234
235// WithConfig option sets the configuration to provider.
236func WithConfig(config Config) TracerProviderOption {
237	return func(opts *TracerProviderConfig) {
238		opts.config = config
239	}
240}
241
242// WithResource option attaches a resource to the provider.
243// The resource is added to the span when it is started.
244func WithResource(r *resource.Resource) TracerProviderOption {
245	return func(opts *TracerProviderConfig) {
246		opts.config.Resource = r
247	}
248}
249
250// WithIDGenerator option registers an IDGenerator with the TracerProvider.
251func WithIDGenerator(g IDGenerator) TracerProviderOption {
252	return func(opts *TracerProviderConfig) {
253		opts.config.IDGenerator = g
254	}
255}
256