diff --git a/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs b/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs index f10e072f8..7fd082d96 100644 --- a/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs +++ b/src/OpenTelemetry/Internal/Builder/ProviderBuilderServiceCollectionExtensions.cs @@ -41,6 +41,7 @@ internal static class ProviderBuilderServiceCollectionExtensions services.AddOpenTelemetryProviderBuilderServices(); services.RegisterOptionsFactory(configuration => new ExportActivityProcessorOptions(configuration)); + services.TryAddSingleton(); return services; } diff --git a/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderBase.cs b/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderBase.cs index 6692c72ed..71ee6f9ec 100644 --- a/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderBase.cs +++ b/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderBase.cs @@ -45,8 +45,9 @@ namespace OpenTelemetry.Trace this.State = state; } - // This ctor is for AddOpenTelemetryTracing scenario where the builder - // is bound to an external service collection. + // This ctor is for ConfigureOpenTelemetryTracing + + // AddOpenTelemetryTracing scenarios where the builder is bound to an + // external service collection. internal TracerProviderBuilderBase(IServiceCollection services) { Guard.ThrowIfNull(services); @@ -65,6 +66,8 @@ namespace OpenTelemetry.Trace var services = new ServiceCollection(); services.AddOpenTelemetryTracerProviderBuilderServices(); + services.AddSingleton( + sp => throw new NotSupportedException("External TracerProvider created through Sdk.CreateTracerProviderBuilder cannot be accessed using service provider.")); this.services = services; this.ownsServices = true; diff --git a/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderState.cs b/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderState.cs index 0f6be9270..22080b82f 100644 --- a/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderState.cs +++ b/src/OpenTelemetry/Trace/Builder/TracerProviderBuilderState.cs @@ -38,6 +38,7 @@ namespace OpenTelemetry.Trace internal Sampler? Sampler; internal bool SetErrorStatusOnException; + private bool hasEnteredBuildPhase; private TracerProviderBuilderSdk? builder; public TracerProviderBuilderState(IServiceProvider serviceProvider) @@ -49,6 +50,16 @@ namespace OpenTelemetry.Trace public TracerProviderBuilderSdk Builder => this.builder ??= new TracerProviderBuilderSdk(this); + public void CheckForCircularBuild() + { + if (this.hasEnteredBuildPhase) + { + throw new NotSupportedException("TracerProvider cannot be accessed while build is executing."); + } + + this.hasEnteredBuildPhase = true; + } + public void AddInstrumentation( string instrumentationName, string instrumentationVersion, diff --git a/src/OpenTelemetry/Trace/TracerProviderSdk.cs b/src/OpenTelemetry/Trace/TracerProviderSdk.cs index 73439b5b6..dd84d81f4 100644 --- a/src/OpenTelemetry/Trace/TracerProviderSdk.cs +++ b/src/OpenTelemetry/Trace/TracerProviderSdk.cs @@ -22,6 +22,7 @@ using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Text; +using Microsoft.Extensions.DependencyInjection; using OpenTelemetry.Internal; using OpenTelemetry.Resources; @@ -47,6 +48,9 @@ namespace OpenTelemetry.Trace { Debug.Assert(serviceProvider != null, "serviceProvider was null"); + var state = serviceProvider!.GetRequiredService(); + state.CheckForCircularBuild(); + this.ServiceProvider = serviceProvider!; if (ownsServiceProvider) @@ -57,8 +61,6 @@ namespace OpenTelemetry.Trace OpenTelemetrySdkEventSource.Log.TracerProviderSdkEvent("Building TracerProvider."); - var state = new TracerProviderBuilderState(serviceProvider!); - TracerProviderBuilderServiceCollectionHelper.InvokeRegisteredConfigureStateCallbacks( serviceProvider!, state); diff --git a/test/OpenTelemetry.Tests/Trace/TracerProviderBuilderExtensionsTest.cs b/test/OpenTelemetry.Tests/Trace/TracerProviderBuilderExtensionsTest.cs index 6060bc793..6c94a0da7 100644 --- a/test/OpenTelemetry.Tests/Trace/TracerProviderBuilderExtensionsTest.cs +++ b/test/OpenTelemetry.Tests/Trace/TracerProviderBuilderExtensionsTest.cs @@ -455,6 +455,56 @@ namespace OpenTelemetry.Trace.Tests Assert.True(configureBuilderCalled); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void TracerProviderNestedResolutionUsingBuilderTest(bool callNestedConfigure) + { + bool innerTestExecuted = false; + + using var provider = Sdk.CreateTracerProviderBuilder() + .ConfigureServices(services => + { + if (callNestedConfigure) + { + services.ConfigureOpenTelemetryTracing(); + } + }) + .ConfigureBuilder((sp, builder) => + { + innerTestExecuted = true; + Assert.Throws(() => sp.GetService()); + }) + .Build(); + + Assert.True(innerTestExecuted); + + Assert.Throws(() => provider.GetServiceProvider()?.GetService()); + } + + [Fact] + public void TracerProviderNestedResolutionUsingConfigureTest() + { + bool innerTestExecuted = false; + + var serviceCollection = new ServiceCollection(); + + serviceCollection.ConfigureOpenTelemetryTracing(builder => + { + builder.ConfigureBuilder((sp, builder) => + { + innerTestExecuted = true; + Assert.Throws(() => sp.GetService()); + }); + }); + + using var serviceProvider = serviceCollection.BuildServiceProvider(); + + var resolvedProvider = serviceProvider.GetRequiredService(); + + Assert.True(innerTestExecuted); + } + private static void RunBuilderServiceLifecycleTest( TracerProviderBuilder builder, Func buildFunc,