Joey Armstrong | a6af152 | 2023-01-17 16:06:16 -0500 | [diff] [blame] | 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 | |
| 15 | package internal |
| 16 | |
| 17 | /* |
| 18 | This file contains the forwarding implementation of the TracerProvider used as |
| 19 | the default global instance. Prior to initialization of an SDK, Tracers |
| 20 | returned by the global TracerProvider will provide no-op functionality. This |
| 21 | means that all Span created prior to initialization are no-op Spans. |
| 22 | |
| 23 | Once an SDK has been initialized, all provided no-op Tracers are swapped for |
| 24 | Tracers provided by the SDK defined TracerProvider. However, any Span started |
| 25 | prior to this initialization does not change its behavior. Meaning, the Span |
| 26 | remains a no-op Span. |
| 27 | |
| 28 | The implementation to track and swap Tracers locks all new Tracer creation |
| 29 | until the swap is complete. This assumes that this operation is not |
| 30 | performance-critical. If that assumption is incorrect, be sure to configure an |
| 31 | SDK prior to any Tracer creation. |
| 32 | */ |
| 33 | |
| 34 | import ( |
| 35 | "context" |
| 36 | "sync" |
| 37 | |
| 38 | "go.opentelemetry.io/otel/api/trace" |
| 39 | "go.opentelemetry.io/otel/internal/trace/noop" |
| 40 | ) |
| 41 | |
| 42 | // tracerProvider is a placeholder for a configured SDK TracerProvider. |
| 43 | // |
| 44 | // All TracerProvider functionality is forwarded to a delegate once |
| 45 | // configured. |
| 46 | type tracerProvider struct { |
| 47 | mtx sync.Mutex |
| 48 | tracers []*tracer |
| 49 | |
| 50 | delegate trace.TracerProvider |
| 51 | } |
| 52 | |
| 53 | // Compile-time guarantee that tracerProvider implements the TracerProvider |
| 54 | // interface. |
| 55 | var _ trace.TracerProvider = &tracerProvider{} |
| 56 | |
| 57 | // setDelegate configures p to delegate all TracerProvider functionality to |
| 58 | // provider. |
| 59 | // |
| 60 | // All Tracers provided prior to this function call are switched out to be |
| 61 | // Tracers provided by provider. |
| 62 | // |
| 63 | // Delegation only happens on the first call to this method. All subsequent |
| 64 | // calls result in no delegation changes. |
| 65 | func (p *tracerProvider) setDelegate(provider trace.TracerProvider) { |
| 66 | if p.delegate != nil { |
| 67 | return |
| 68 | } |
| 69 | |
| 70 | p.mtx.Lock() |
| 71 | defer p.mtx.Unlock() |
| 72 | |
| 73 | p.delegate = provider |
| 74 | for _, t := range p.tracers { |
| 75 | t.setDelegate(provider) |
| 76 | } |
| 77 | |
| 78 | p.tracers = nil |
| 79 | } |
| 80 | |
| 81 | // Tracer implements TracerProvider. |
| 82 | func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer { |
| 83 | p.mtx.Lock() |
| 84 | defer p.mtx.Unlock() |
| 85 | |
| 86 | if p.delegate != nil { |
| 87 | return p.delegate.Tracer(name) |
| 88 | } |
| 89 | |
| 90 | t := &tracer{name: name, opts: opts} |
| 91 | p.tracers = append(p.tracers, t) |
| 92 | return t |
| 93 | } |
| 94 | |
| 95 | // tracer is a placeholder for a trace.Tracer. |
| 96 | // |
| 97 | // All Tracer functionality is forwarded to a delegate once configured. |
| 98 | // Otherwise, all functionality is forwarded to a NoopTracer. |
| 99 | type tracer struct { |
| 100 | once sync.Once |
| 101 | name string |
| 102 | opts []trace.TracerOption |
| 103 | |
| 104 | delegate trace.Tracer |
| 105 | } |
| 106 | |
| 107 | // Compile-time guarantee that tracer implements the trace.Tracer interface. |
| 108 | var _ trace.Tracer = &tracer{} |
| 109 | |
| 110 | // setDelegate configures t to delegate all Tracer functionality to Tracers |
| 111 | // created by provider. |
| 112 | // |
| 113 | // All subsequent calls to the Tracer methods will be passed to the delegate. |
| 114 | // |
| 115 | // Delegation only happens on the first call to this method. All subsequent |
| 116 | // calls result in no delegation changes. |
| 117 | func (t *tracer) setDelegate(provider trace.TracerProvider) { |
| 118 | t.once.Do(func() { t.delegate = provider.Tracer(t.name, t.opts...) }) |
| 119 | } |
| 120 | |
| 121 | // Start implements trace.Tracer by forwarding the call to t.delegate if |
| 122 | // set, otherwise it forwards the call to a NoopTracer. |
| 123 | func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanOption) (context.Context, trace.Span) { |
| 124 | if t.delegate != nil { |
| 125 | return t.delegate.Start(ctx, name, opts...) |
| 126 | } |
| 127 | return noop.Tracer.Start(ctx, name, opts...) |
| 128 | } |