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.RegisterOptionsFactory(configuration => new ExportActivityProcessorOptions(configuration));
services.TryAddSingleton<TracerProviderBuilderState>();
return services;
}

View File

@ -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<TracerProvider>(
sp => throw new NotSupportedException("External TracerProvider created through Sdk.CreateTracerProviderBuilder cannot be accessed using service provider."));
this.services = services;
this.ownsServices = true;

View File

@ -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,

View File

@ -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<TracerProviderBuilderState>();
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);

View File

@ -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<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(
TracerProviderBuilder builder,
Func<TracerProviderSdk> buildFunc,