Fix circular reference issue building up tracer provider. (#3803)

This commit is contained in:
Mikel Blanchard 2022-10-21 13:10:46 -07:00 committed by GitHub
parent 32650555d4
commit 9b8702e3dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 71 additions and 4 deletions

View File

@ -41,6 +41,7 @@ internal static class ProviderBuilderServiceCollectionExtensions
services.AddOpenTelemetryProviderBuilderServices(); services.AddOpenTelemetryProviderBuilderServices();
services.RegisterOptionsFactory(configuration => new ExportActivityProcessorOptions(configuration)); services.RegisterOptionsFactory(configuration => new ExportActivityProcessorOptions(configuration));
services.TryAddSingleton<TracerProviderBuilderState>();
return services; return services;
} }

View File

@ -45,8 +45,9 @@ namespace OpenTelemetry.Trace
this.State = state; this.State = state;
} }
// This ctor is for AddOpenTelemetryTracing scenario where the builder // This ctor is for ConfigureOpenTelemetryTracing +
// is bound to an external service collection. // AddOpenTelemetryTracing scenarios where the builder is bound to an
// external service collection.
internal TracerProviderBuilderBase(IServiceCollection services) internal TracerProviderBuilderBase(IServiceCollection services)
{ {
Guard.ThrowIfNull(services); Guard.ThrowIfNull(services);
@ -65,6 +66,8 @@ namespace OpenTelemetry.Trace
var services = new ServiceCollection(); var services = new ServiceCollection();
services.AddOpenTelemetryTracerProviderBuilderServices(); services.AddOpenTelemetryTracerProviderBuilderServices();
services.AddSingleton<TracerProvider>(
sp => throw new NotSupportedException("External TracerProvider created through Sdk.CreateTracerProviderBuilder cannot be accessed using service provider."));
this.services = services; this.services = services;
this.ownsServices = true; this.ownsServices = true;

View File

@ -38,6 +38,7 @@ namespace OpenTelemetry.Trace
internal Sampler? Sampler; internal Sampler? Sampler;
internal bool SetErrorStatusOnException; internal bool SetErrorStatusOnException;
private bool hasEnteredBuildPhase;
private TracerProviderBuilderSdk? builder; private TracerProviderBuilderSdk? builder;
public TracerProviderBuilderState(IServiceProvider serviceProvider) public TracerProviderBuilderState(IServiceProvider serviceProvider)
@ -49,6 +50,16 @@ namespace OpenTelemetry.Trace
public TracerProviderBuilderSdk Builder => this.builder ??= new TracerProviderBuilderSdk(this); 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( public void AddInstrumentation(
string instrumentationName, string instrumentationName,
string instrumentationVersion, string instrumentationVersion,

View File

@ -22,6 +22,7 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry.Internal; using OpenTelemetry.Internal;
using OpenTelemetry.Resources; using OpenTelemetry.Resources;
@ -47,6 +48,9 @@ namespace OpenTelemetry.Trace
{ {
Debug.Assert(serviceProvider != null, "serviceProvider was null"); Debug.Assert(serviceProvider != null, "serviceProvider was null");
var state = serviceProvider!.GetRequiredService<TracerProviderBuilderState>();
state.CheckForCircularBuild();
this.ServiceProvider = serviceProvider!; this.ServiceProvider = serviceProvider!;
if (ownsServiceProvider) if (ownsServiceProvider)
@ -57,8 +61,6 @@ namespace OpenTelemetry.Trace
OpenTelemetrySdkEventSource.Log.TracerProviderSdkEvent("Building TracerProvider."); OpenTelemetrySdkEventSource.Log.TracerProviderSdkEvent("Building TracerProvider.");
var state = new TracerProviderBuilderState(serviceProvider!);
TracerProviderBuilderServiceCollectionHelper.InvokeRegisteredConfigureStateCallbacks( TracerProviderBuilderServiceCollectionHelper.InvokeRegisteredConfigureStateCallbacks(
serviceProvider!, serviceProvider!,
state); state);

View File

@ -455,6 +455,56 @@ namespace OpenTelemetry.Trace.Tests
Assert.True(configureBuilderCalled); 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<NotSupportedException>(() => sp.GetService<TracerProvider>());
})
.Build();
Assert.True(innerTestExecuted);
Assert.Throws<NotSupportedException>(() => provider.GetServiceProvider()?.GetService<TracerProvider>());
}
[Fact]
public void TracerProviderNestedResolutionUsingConfigureTest()
{
bool innerTestExecuted = false;
var serviceCollection = new ServiceCollection();
serviceCollection.ConfigureOpenTelemetryTracing(builder =>
{
builder.ConfigureBuilder((sp, builder) =>
{
innerTestExecuted = true;
Assert.Throws<NotSupportedException>(() => sp.GetService<TracerProvider>());
});
});
using var serviceProvider = serviceCollection.BuildServiceProvider();
var resolvedProvider = serviceProvider.GetRequiredService<TracerProvider>();
Assert.True(innerTestExecuted);
}
private static void RunBuilderServiceLifecycleTest( private static void RunBuilderServiceLifecycleTest(
TracerProviderBuilder builder, TracerProviderBuilder builder,
Func<TracerProviderSdk> buildFunc, Func<TracerProviderSdk> buildFunc,