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