diff --git a/CHANGELOG.md b/CHANGELOG.md index da42684fc25..0b1e158a401 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,9 +40,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Jaeger Exporter: Ensure mapping between OTEL and Jaeger span data complies with the specification. (#1626) - The `otel-collector` example now correctly flushes metric events prior to shutting down the exporter. (#1678) - Synchronization issues in global trace delegate implementation. (#1686) - -### Fixed - Do not set span status message in `SpanStatusFromHTTPStatusCode` if it can be inferred from `http.status_code`. (#1681) +- Reduced excess memory usage by global `TracerProvider`. (#1687) ## [0.18.0] - 2020-03-03 diff --git a/internal/global/trace.go b/internal/global/trace.go index a23df914f93..d7f71dc06c6 100644 --- a/internal/global/trace.go +++ b/internal/global/trace.go @@ -46,7 +46,7 @@ import ( // configured. type tracerProvider struct { mtx sync.Mutex - tracers []*tracer + tracers map[il]*tracer delegate trace.TracerProvider } @@ -66,6 +66,11 @@ func (p *tracerProvider) setDelegate(provider trace.TracerProvider) { defer p.mtx.Unlock() p.delegate = provider + + if len(p.tracers) == 0 { + return + } + for _, t := range p.tracers { t.setDelegate(provider) } @@ -82,11 +87,31 @@ func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T return p.delegate.Tracer(name, opts...) } + // At this moment it is guaranteed that no sdk is installed, save the tracer in the tracers map. + + key := il{ + name: name, + version: trace.NewTracerConfig(opts...).InstrumentationVersion, + } + + if p.tracers == nil { + p.tracers = make(map[il]*tracer) + } + + if val, ok := p.tracers[key]; ok { + return val + } + t := &tracer{name: name, opts: opts} - p.tracers = append(p.tracers, t) + p.tracers[key] = t return t } +type il struct { + name string + version string +} + // tracer is a placeholder for a trace.Tracer. // // All Tracer functionality is forwarded to a delegate once configured. diff --git a/internal/global/trace_test.go b/internal/global/trace_test.go index 10652611943..a8fdfb44d9d 100644 --- a/internal/global/trace_test.go +++ b/internal/global/trace_test.go @@ -195,3 +195,21 @@ func TestTracerDelegatesConcurrentSafe(t *testing.T) { assert.LessOrEqual(t, int32(10), atomic.LoadInt32(&called), "expected configured TraceProvider to be called") } + +func TestTraceProviderDelegatesSameInstance(t *testing.T) { + global.ResetForTest() + + // Retrieve the placeholder TracerProvider. + gtp := otel.GetTracerProvider() + tracer := gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz")) + assert.Same(t, tracer, gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz"))) + assert.Same(t, tracer, gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz"))) + + otel.SetTracerProvider(fnTracerProvider{ + tracer: func(name string, opts ...trace.TracerOption) trace.Tracer { + return trace.NewNoopTracerProvider().Tracer("") + }, + }) + + assert.NotSame(t, tracer, gtp.Tracer("abc", trace.WithInstrumentationVersion("xyz"))) +}