Skip to content

Commit 107dd26

Browse files
authored
[di] Expose a detached TracerProviderBuilder extension on IServiceCollection which may modify services. (#4508)
1 parent eb12f9b commit 107dd26

File tree

8 files changed

+208
-76
lines changed

8 files changed

+208
-76
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
static OpenTelemetry.Trace.OpenTelemetryDependencyInjectionTracingServiceCollectionExtensions.ConfigureOpenTelemetryTracerProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action<OpenTelemetry.Trace.TracerProviderBuilder!>! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
static OpenTelemetry.Trace.OpenTelemetryDependencyInjectionTracingServiceCollectionExtensions.ConfigureOpenTelemetryTracerProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action<OpenTelemetry.Trace.TracerProviderBuilder!>! configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!

src/OpenTelemetry.Api.ProviderBuilderExtensions/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
`TracerProvider`.
99
([#4468](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4468))
1010

11+
* Added an `IServiceCollection.ConfigureOpenTelemetryTracerProvider` overload
12+
which may be used to configure `TracerProviderBuilder`s while the
13+
`IServiceCollection` is modifiable (before the `IServiceProvider` has been
14+
created).
15+
([#4508](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4508))
16+
1117
## 1.5.0-alpha.2
1218

1319
Released 2023-Mar-31

src/OpenTelemetry.Api.ProviderBuilderExtensions/Trace/OpenTelemetryDependencyInjectionTracingServiceCollectionExtensions.cs

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,44 +26,76 @@ public static class OpenTelemetryDependencyInjectionTracingServiceCollectionExte
2626
{
2727
/// <summary>
2828
/// Registers an action used to configure the OpenTelemetry <see
29-
/// cref="TracerProviderBuilder"/> used to create the <see
30-
/// cref="TracerProvider"/> for the <see cref="IServiceCollection"/> being
31-
/// configured.
29+
/// cref="TracerProviderBuilder"/>.
3230
/// </summary>
3331
/// <remarks>
3432
/// Notes:
3533
/// <list type="bullet">
3634
/// <item>This is safe to be called multiple times and by library authors.
3735
/// Each registered configuration action will be applied
3836
/// sequentially.</item>
39-
/// <item>A <see cref="TracerProvider"/> will not be created automatically
40-
/// using this method. To begin collecting metrics use the
37+
/// <item>A <see cref="TracerProvider"/> will NOT be created automatically
38+
/// using this method. To begin collecting traces use the
4139
/// <c>IServiceCollection.AddOpenTelemetry</c> extension in the
4240
/// <c>OpenTelemetry.Extensions.Hosting</c> package.</item>
4341
/// </list>
4442
/// </remarks>
45-
/// <param name="services">The <see cref="IServiceCollection" /> to add
46-
/// services to.</param>
43+
/// <param name="services"><see cref="IServiceCollection" />.</param>
4744
/// <param name="configure">Callback action to configure the <see
4845
/// cref="TracerProviderBuilder"/>.</param>
4946
/// <returns>The <see cref="IServiceCollection"/> so that additional calls
5047
/// can be chained.</returns>
5148
public static IServiceCollection ConfigureOpenTelemetryTracerProvider(
5249
this IServiceCollection services,
53-
Action<IServiceProvider, TracerProviderBuilder> configure)
50+
Action<TracerProviderBuilder> configure)
5451
{
55-
RegisterBuildAction(services, configure);
52+
Guard.ThrowIfNull(services);
53+
Guard.ThrowIfNull(configure);
54+
55+
configure(new TracerProviderServiceCollectionBuilder(services));
5656

5757
return services;
5858
}
5959

60-
private static void RegisterBuildAction(IServiceCollection services, Action<IServiceProvider, TracerProviderBuilder> configure)
60+
/// <summary>
61+
/// Registers an action used to configure the OpenTelemetry <see
62+
/// cref="TracerProviderBuilder"/> once the <see cref="IServiceProvider"/>
63+
/// is available.
64+
/// </summary>
65+
/// <remarks>
66+
/// Notes:
67+
/// <list type="bullet">
68+
/// <item>This is safe to be called multiple times and by library authors.
69+
/// Each registered configuration action will be applied
70+
/// sequentially.</item>
71+
/// <item>A <see cref="TracerProvider"/> will NOT be created automatically
72+
/// using this method. To begin collecting traces use the
73+
/// <c>IServiceCollection.AddOpenTelemetry</c> extension in the
74+
/// <c>OpenTelemetry.Extensions.Hosting</c> package.</item>
75+
/// <item>The supplied configuration delegate is called once the <see
76+
/// cref="IServiceProvider"/> is available. Services may NOT be added to a
77+
/// <see cref="TracerProviderBuilder"/> once the <see
78+
/// cref="IServiceProvider"/> has been created. Many helper extensions
79+
/// register services and may throw if invoked inside the configuration
80+
/// delegate.</item>
81+
/// </list>
82+
/// </remarks>
83+
/// <param name="services"><see cref="IServiceCollection" />.</param>
84+
/// <param name="configure">Callback action to configure the <see
85+
/// cref="TracerProviderBuilder"/>.</param>
86+
/// <returns>The <see cref="IServiceCollection"/> so that additional calls
87+
/// can be chained.</returns>
88+
public static IServiceCollection ConfigureOpenTelemetryTracerProvider(
89+
this IServiceCollection services,
90+
Action<IServiceProvider, TracerProviderBuilder> configure)
6191
{
6292
Guard.ThrowIfNull(services);
6393
Guard.ThrowIfNull(configure);
6494

6595
services.AddSingleton<IConfigureTracerProviderBuilder>(
6696
new ConfigureTracerProviderBuilderCallbackWrapper(configure));
97+
98+
return services;
6799
}
68100

69101
private sealed class ConfigureTracerProviderBuilderCallbackWrapper : IConfigureTracerProviderBuilder
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// <copyright file="TracerProviderServiceCollectionBuilder.cs" company="OpenTelemetry Authors">
2+
// Copyright The OpenTelemetry Authors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
// </copyright>
16+
17+
using Microsoft.Extensions.DependencyInjection;
18+
using OpenTelemetry.Internal;
19+
20+
namespace OpenTelemetry.Trace;
21+
22+
internal sealed class TracerProviderServiceCollectionBuilder : TracerProviderBuilder, ITracerProviderBuilder
23+
{
24+
public TracerProviderServiceCollectionBuilder(IServiceCollection services)
25+
{
26+
services.ConfigureOpenTelemetryTracerProvider((sp, builder) => this.Services = null);
27+
28+
this.Services = services;
29+
}
30+
31+
public IServiceCollection? Services { get; set; }
32+
33+
public TracerProvider? Provider => null;
34+
35+
/// <inheritdoc />
36+
public override TracerProviderBuilder AddInstrumentation<TInstrumentation>(Func<TInstrumentation> instrumentationFactory)
37+
{
38+
Guard.ThrowIfNull(instrumentationFactory);
39+
40+
this.ConfigureBuilderInternal((sp, builder) =>
41+
{
42+
builder.AddInstrumentation(instrumentationFactory);
43+
});
44+
45+
return this;
46+
}
47+
48+
/// <inheritdoc />
49+
public override TracerProviderBuilder AddSource(params string[] names)
50+
{
51+
Guard.ThrowIfNull(names);
52+
53+
this.ConfigureBuilderInternal((sp, builder) =>
54+
{
55+
builder.AddSource(names);
56+
});
57+
58+
return this;
59+
}
60+
61+
/// <inheritdoc />
62+
public override TracerProviderBuilder AddLegacySource(string operationName)
63+
{
64+
Guard.ThrowIfNullOrWhitespace(operationName);
65+
66+
this.ConfigureBuilderInternal((sp, builder) =>
67+
{
68+
builder.AddLegacySource(operationName);
69+
});
70+
71+
return this;
72+
}
73+
74+
/// <inheritdoc />
75+
public TracerProviderBuilder ConfigureServices(Action<IServiceCollection> configure)
76+
=> this.ConfigureServicesInternal(configure);
77+
78+
/// <inheritdoc cref="IDeferredTracerProviderBuilder.Configure" />
79+
public TracerProviderBuilder ConfigureBuilder(Action<IServiceProvider, TracerProviderBuilder> configure)
80+
=> this.ConfigureBuilderInternal(configure);
81+
82+
/// <inheritdoc />
83+
TracerProviderBuilder IDeferredTracerProviderBuilder.Configure(Action<IServiceProvider, TracerProviderBuilder> configure)
84+
=> this.ConfigureBuilderInternal(configure);
85+
86+
private TracerProviderBuilder ConfigureBuilderInternal(Action<IServiceProvider, TracerProviderBuilder> configure)
87+
{
88+
var services = this.Services
89+
?? throw new NotSupportedException("Builder cannot be configured during TracerProvider construction.");
90+
91+
services.ConfigureOpenTelemetryTracerProvider(configure);
92+
93+
return this;
94+
}
95+
96+
private TracerProviderBuilder ConfigureServicesInternal(Action<IServiceCollection> configure)
97+
{
98+
Guard.ThrowIfNull(configure);
99+
100+
var services = this.Services
101+
?? throw new NotSupportedException("Services cannot be configured during TracerProvider construction.");
102+
103+
configure(services);
104+
105+
return this;
106+
}
107+
}

src/OpenTelemetry/Trace/Builder/TracerProviderBuilderBase.cs

Lines changed: 11 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ namespace OpenTelemetry.Trace;
2828
public class TracerProviderBuilderBase : TracerProviderBuilder, ITracerProviderBuilder
2929
{
3030
private readonly bool allowBuild;
31-
private IServiceCollection? services;
31+
private readonly TracerProviderServiceCollectionBuilder innerBuilder;
3232

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

46-
services.ConfigureOpenTelemetryTracerProvider((sp, builder) => this.services = null);
47-
48-
this.services = services;
46+
this.innerBuilder = new TracerProviderServiceCollectionBuilder(services);
4947

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

61-
services.ConfigureOpenTelemetryTracerProvider((sp, builder) => this.services = null);
62-
63-
this.services = services;
59+
this.innerBuilder = new TracerProviderServiceCollectionBuilder(services);
6460

6561
this.allowBuild = false;
6662
}
@@ -71,49 +67,34 @@ internal TracerProviderBuilderBase(IServiceCollection services)
7167
/// <inheritdoc />
7268
public override TracerProviderBuilder AddInstrumentation<TInstrumentation>(Func<TInstrumentation> instrumentationFactory)
7369
{
74-
Guard.ThrowIfNull(instrumentationFactory);
75-
76-
this.ConfigureBuilderInternal((sp, builder) =>
77-
{
78-
builder.AddInstrumentation(instrumentationFactory);
79-
});
70+
this.innerBuilder.AddInstrumentation(instrumentationFactory);
8071

8172
return this;
8273
}
8374

8475
/// <inheritdoc />
8576
public override TracerProviderBuilder AddSource(params string[] names)
8677
{
87-
Guard.ThrowIfNull(names);
88-
89-
this.ConfigureBuilderInternal((sp, builder) =>
90-
{
91-
builder.AddSource(names);
92-
});
78+
this.innerBuilder.AddSource(names);
9379

9480
return this;
9581
}
9682

9783
/// <inheritdoc />
9884
public override TracerProviderBuilder AddLegacySource(string operationName)
9985
{
100-
Guard.ThrowIfNullOrWhitespace(operationName);
101-
102-
this.ConfigureBuilderInternal((sp, builder) =>
103-
{
104-
builder.AddLegacySource(operationName);
105-
});
86+
this.innerBuilder.AddLegacySource(operationName);
10687

10788
return this;
10889
}
10990

11091
/// <inheritdoc />
11192
TracerProviderBuilder ITracerProviderBuilder.ConfigureServices(Action<IServiceCollection> configure)
112-
=> this.ConfigureServicesInternal(configure);
93+
=> this.innerBuilder.ConfigureServices(configure);
11394

11495
/// <inheritdoc />
11596
TracerProviderBuilder IDeferredTracerProviderBuilder.Configure(Action<IServiceProvider, TracerProviderBuilder> configure)
116-
=> this.ConfigureBuilderInternal(configure);
97+
=> this.innerBuilder.ConfigureBuilder(configure);
11798

11899
internal TracerProvider InvokeBuild()
119100
=> this.Build();
@@ -134,7 +115,7 @@ protected TracerProviderBuilder AddInstrumentation(
134115
Guard.ThrowIfNullOrWhitespace(instrumentationVersion);
135116
Guard.ThrowIfNull(instrumentationFactory);
136117

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

160-
var services = this.services;
141+
var services = this.innerBuilder.Services;
161142

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

167-
this.services = null;
148+
this.innerBuilder.Services = null;
168149

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

176157
return new TracerProviderSdk(serviceProvider, ownsServiceProvider: true);
177158
}
178-
179-
private TracerProviderBuilder ConfigureBuilderInternal(Action<IServiceProvider, TracerProviderBuilder> configure)
180-
{
181-
var services = this.services;
182-
183-
if (services == null)
184-
{
185-
throw new NotSupportedException("Builder cannot be configured during TracerProvider construction.");
186-
}
187-
188-
services.ConfigureOpenTelemetryTracerProvider(configure);
189-
190-
return this;
191-
}
192-
193-
private TracerProviderBuilder ConfigureServicesInternal(Action<IServiceCollection> configure)
194-
{
195-
Guard.ThrowIfNull(configure);
196-
197-
var services = this.services;
198-
199-
if (services == null)
200-
{
201-
throw new NotSupportedException("Services cannot be configured during TracerProvider construction.");
202-
}
203-
204-
configure(services);
205-
206-
return this;
207-
}
208159
}

0 commit comments

Comments
 (0)