Skip to content

Commit

Permalink
[di] Expose a detached TracerProviderBuilder extension on IServiceCol…
Browse files Browse the repository at this point in the history
…lection which may modify services. (#4508)
  • Loading branch information
CodeBlanch authored May 25, 2023
1 parent eb12f9b commit 107dd26
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 76 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
static OpenTelemetry.Trace.OpenTelemetryDependencyInjectionTracingServiceCollectionExtensions.ConfigureOpenTelemetryTracerProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action<OpenTelemetry.Trace.TracerProviderBuilder!>! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
static OpenTelemetry.Trace.OpenTelemetryDependencyInjectionTracingServiceCollectionExtensions.ConfigureOpenTelemetryTracerProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action<OpenTelemetry.Trace.TracerProviderBuilder!>! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
6 changes: 6 additions & 0 deletions src/OpenTelemetry.Api.ProviderBuilderExtensions/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
`TracerProvider`.
([#4468](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4468))

* Added an `IServiceCollection.ConfigureOpenTelemetryTracerProvider` overload
which may be used to configure `TracerProviderBuilder`s while the
`IServiceCollection` is modifiable (before the `IServiceProvider` has been
created).
([#4508](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4508))

## 1.5.0-alpha.2

Released 2023-Mar-31
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,44 +26,76 @@ public static class OpenTelemetryDependencyInjectionTracingServiceCollectionExte
{
/// <summary>
/// Registers an action used to configure the OpenTelemetry <see
/// cref="TracerProviderBuilder"/> used to create the <see
/// cref="TracerProvider"/> for the <see cref="IServiceCollection"/> being
/// configured.
/// cref="TracerProviderBuilder"/>.
/// </summary>
/// <remarks>
/// Notes:
/// <list type="bullet">
/// <item>This is safe to be called multiple times and by library authors.
/// Each registered configuration action will be applied
/// sequentially.</item>
/// <item>A <see cref="TracerProvider"/> will not be created automatically
/// using this method. To begin collecting metrics use the
/// <item>A <see cref="TracerProvider"/> will NOT be created automatically
/// using this method. To begin collecting traces use the
/// <c>IServiceCollection.AddOpenTelemetry</c> extension in the
/// <c>OpenTelemetry.Extensions.Hosting</c> package.</item>
/// </list>
/// </remarks>
/// <param name="services">The <see cref="IServiceCollection" /> to add
/// services to.</param>
/// <param name="services"><see cref="IServiceCollection" />.</param>
/// <param name="configure">Callback action to configure the <see
/// cref="TracerProviderBuilder"/>.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls
/// can be chained.</returns>
public static IServiceCollection ConfigureOpenTelemetryTracerProvider(
this IServiceCollection services,
Action<IServiceProvider, TracerProviderBuilder> configure)
Action<TracerProviderBuilder> configure)
{
RegisterBuildAction(services, configure);
Guard.ThrowIfNull(services);
Guard.ThrowIfNull(configure);

configure(new TracerProviderServiceCollectionBuilder(services));

return services;
}

private static void RegisterBuildAction(IServiceCollection services, Action<IServiceProvider, TracerProviderBuilder> configure)
/// <summary>
/// Registers an action used to configure the OpenTelemetry <see
/// cref="TracerProviderBuilder"/> once the <see cref="IServiceProvider"/>
/// is available.
/// </summary>
/// <remarks>
/// Notes:
/// <list type="bullet">
/// <item>This is safe to be called multiple times and by library authors.
/// Each registered configuration action will be applied
/// sequentially.</item>
/// <item>A <see cref="TracerProvider"/> will NOT be created automatically
/// using this method. To begin collecting traces use the
/// <c>IServiceCollection.AddOpenTelemetry</c> extension in the
/// <c>OpenTelemetry.Extensions.Hosting</c> package.</item>
/// <item>The supplied configuration delegate is called once the <see
/// cref="IServiceProvider"/> is available. Services may NOT be added to a
/// <see cref="TracerProviderBuilder"/> once the <see
/// cref="IServiceProvider"/> has been created. Many helper extensions
/// register services and may throw if invoked inside the configuration
/// delegate.</item>
/// </list>
/// </remarks>
/// <param name="services"><see cref="IServiceCollection" />.</param>
/// <param name="configure">Callback action to configure the <see
/// cref="TracerProviderBuilder"/>.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls
/// can be chained.</returns>
public static IServiceCollection ConfigureOpenTelemetryTracerProvider(
this IServiceCollection services,
Action<IServiceProvider, TracerProviderBuilder> configure)
{
Guard.ThrowIfNull(services);
Guard.ThrowIfNull(configure);

services.AddSingleton<IConfigureTracerProviderBuilder>(
new ConfigureTracerProviderBuilderCallbackWrapper(configure));

return services;
}

private sealed class ConfigureTracerProviderBuilderCallbackWrapper : IConfigureTracerProviderBuilder
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// <copyright file="TracerProviderServiceCollectionBuilder.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry.Internal;

namespace OpenTelemetry.Trace;

internal sealed class TracerProviderServiceCollectionBuilder : TracerProviderBuilder, ITracerProviderBuilder
{
public TracerProviderServiceCollectionBuilder(IServiceCollection services)
{
services.ConfigureOpenTelemetryTracerProvider((sp, builder) => this.Services = null);

this.Services = services;
}

public IServiceCollection? Services { get; set; }

public TracerProvider? Provider => null;

/// <inheritdoc />
public override TracerProviderBuilder AddInstrumentation<TInstrumentation>(Func<TInstrumentation> instrumentationFactory)
{
Guard.ThrowIfNull(instrumentationFactory);

this.ConfigureBuilderInternal((sp, builder) =>
{
builder.AddInstrumentation(instrumentationFactory);
});

return this;
}

/// <inheritdoc />
public override TracerProviderBuilder AddSource(params string[] names)
{
Guard.ThrowIfNull(names);

this.ConfigureBuilderInternal((sp, builder) =>
{
builder.AddSource(names);
});

return this;
}

/// <inheritdoc />
public override TracerProviderBuilder AddLegacySource(string operationName)
{
Guard.ThrowIfNullOrWhitespace(operationName);

this.ConfigureBuilderInternal((sp, builder) =>
{
builder.AddLegacySource(operationName);
});

return this;
}

/// <inheritdoc />
public TracerProviderBuilder ConfigureServices(Action<IServiceCollection> configure)
=> this.ConfigureServicesInternal(configure);

/// <inheritdoc cref="IDeferredTracerProviderBuilder.Configure" />
public TracerProviderBuilder ConfigureBuilder(Action<IServiceProvider, TracerProviderBuilder> configure)
=> this.ConfigureBuilderInternal(configure);

/// <inheritdoc />
TracerProviderBuilder IDeferredTracerProviderBuilder.Configure(Action<IServiceProvider, TracerProviderBuilder> configure)
=> this.ConfigureBuilderInternal(configure);

private TracerProviderBuilder ConfigureBuilderInternal(Action<IServiceProvider, TracerProviderBuilder> configure)
{
var services = this.Services
?? throw new NotSupportedException("Builder cannot be configured during TracerProvider construction.");

services.ConfigureOpenTelemetryTracerProvider(configure);

return this;
}

private TracerProviderBuilder ConfigureServicesInternal(Action<IServiceCollection> configure)
{
Guard.ThrowIfNull(configure);

var services = this.Services
?? throw new NotSupportedException("Services cannot be configured during TracerProvider construction.");

configure(services);

return this;
}
}
71 changes: 11 additions & 60 deletions src/OpenTelemetry/Trace/Builder/TracerProviderBuilderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace OpenTelemetry.Trace;
public class TracerProviderBuilderBase : TracerProviderBuilder, ITracerProviderBuilder
{
private readonly bool allowBuild;
private IServiceCollection? services;
private readonly TracerProviderServiceCollectionBuilder innerBuilder;

/// <summary>
/// Initializes a new instance of the <see cref="TracerProviderBuilderBase"/> class.
Expand All @@ -43,9 +43,7 @@ public TracerProviderBuilderBase()
.TryAddSingleton<TracerProvider>(
sp => throw new NotSupportedException("Self-contained TracerProvider cannot be accessed using the application IServiceProvider call Build instead."));

services.ConfigureOpenTelemetryTracerProvider((sp, builder) => this.services = null);

this.services = services;
this.innerBuilder = new TracerProviderServiceCollectionBuilder(services);

this.allowBuild = true;
}
Expand All @@ -58,9 +56,7 @@ internal TracerProviderBuilderBase(IServiceCollection services)
.AddOpenTelemetryTracerProviderBuilderServices()
.TryAddSingleton<TracerProvider>(sp => new TracerProviderSdk(sp, ownsServiceProvider: false));

services.ConfigureOpenTelemetryTracerProvider((sp, builder) => this.services = null);

this.services = services;
this.innerBuilder = new TracerProviderServiceCollectionBuilder(services);

this.allowBuild = false;
}
Expand All @@ -71,49 +67,34 @@ internal TracerProviderBuilderBase(IServiceCollection services)
/// <inheritdoc />
public override TracerProviderBuilder AddInstrumentation<TInstrumentation>(Func<TInstrumentation> instrumentationFactory)
{
Guard.ThrowIfNull(instrumentationFactory);

this.ConfigureBuilderInternal((sp, builder) =>
{
builder.AddInstrumentation(instrumentationFactory);
});
this.innerBuilder.AddInstrumentation(instrumentationFactory);

return this;
}

/// <inheritdoc />
public override TracerProviderBuilder AddSource(params string[] names)
{
Guard.ThrowIfNull(names);

this.ConfigureBuilderInternal((sp, builder) =>
{
builder.AddSource(names);
});
this.innerBuilder.AddSource(names);

return this;
}

/// <inheritdoc />
public override TracerProviderBuilder AddLegacySource(string operationName)
{
Guard.ThrowIfNullOrWhitespace(operationName);

this.ConfigureBuilderInternal((sp, builder) =>
{
builder.AddLegacySource(operationName);
});
this.innerBuilder.AddLegacySource(operationName);

return this;
}

/// <inheritdoc />
TracerProviderBuilder ITracerProviderBuilder.ConfigureServices(Action<IServiceCollection> configure)
=> this.ConfigureServicesInternal(configure);
=> this.innerBuilder.ConfigureServices(configure);

/// <inheritdoc />
TracerProviderBuilder IDeferredTracerProviderBuilder.Configure(Action<IServiceProvider, TracerProviderBuilder> configure)
=> this.ConfigureBuilderInternal(configure);
=> this.innerBuilder.ConfigureBuilder(configure);

internal TracerProvider InvokeBuild()
=> this.Build();
Expand All @@ -134,7 +115,7 @@ protected TracerProviderBuilder AddInstrumentation(
Guard.ThrowIfNullOrWhitespace(instrumentationVersion);
Guard.ThrowIfNull(instrumentationFactory);

return this.ConfigureBuilderInternal((sp, builder) =>
return this.innerBuilder.ConfigureBuilder((sp, builder) =>
{
if (builder is TracerProviderBuilderSdk tracerProviderBuilderState)
{
Expand All @@ -157,14 +138,14 @@ protected TracerProvider Build()
throw new NotSupportedException("A TracerProviderBuilder bound to external service cannot be built directly. Access the TracerProvider using the application IServiceProvider instead.");
}

var services = this.services;
var services = this.innerBuilder.Services;

if (services == null)
{
throw new NotSupportedException("TracerProviderBuilder build method cannot be called multiple times.");
}

this.services = null;
this.innerBuilder.Services = null;

#if DEBUG
bool validateScopes = true;
Expand All @@ -175,34 +156,4 @@ protected TracerProvider Build()

return new TracerProviderSdk(serviceProvider, ownsServiceProvider: true);
}

private TracerProviderBuilder ConfigureBuilderInternal(Action<IServiceProvider, TracerProviderBuilder> configure)
{
var services = this.services;

if (services == null)
{
throw new NotSupportedException("Builder cannot be configured during TracerProvider construction.");
}

services.ConfigureOpenTelemetryTracerProvider(configure);

return this;
}

private TracerProviderBuilder ConfigureServicesInternal(Action<IServiceCollection> configure)
{
Guard.ThrowIfNull(configure);

var services = this.services;

if (services == null)
{
throw new NotSupportedException("Services cannot be configured during TracerProvider construction.");
}

configure(services);

return this;
}
}
Loading

0 comments on commit 107dd26

Please sign in to comment.