Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dependency injection support #1889

Merged
merged 19 commits into from
Mar 26, 2021
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions examples/AspNetCore/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using OpenTelemetry;
using OpenTelemetry.Exporter;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

Expand Down Expand Up @@ -63,20 +63,17 @@ public void ConfigureServices(IServiceCollection services)
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(this.Configuration.GetValue<string>("Jaeger:ServiceName")))
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddJaegerExporter(jaegerOptions =>
{
jaegerOptions.AgentHost = this.Configuration.GetValue<string>("Jaeger:Host");
jaegerOptions.AgentPort = this.Configuration.GetValue<int>("Jaeger:Port");
}));
.AddJaegerExporter());

services.Configure<JaegerExporterOptions>(this.Configuration.GetSection("Jaeger"));
CodeBlanch marked this conversation as resolved.
Show resolved Hide resolved
break;
case "zipkin":
services.AddOpenTelemetryTracing((builder) => builder
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddZipkinExporter(zipkinOptions =>
{
zipkinOptions.Endpoint = new Uri(this.Configuration.GetValue<string>("Zipkin:Endpoint"));
}));
.AddZipkinExporter());

services.Configure<ZipkinExporterOptions>(this.Configuration.GetSection("Zipkin"));
break;
case "otlp":
// Adding the OtlpExporter creates a GrpcChannel.
Expand Down
4 changes: 2 additions & 2 deletions examples/AspNetCore/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"UseExporter": "console",
"Jaeger": {
"ServiceName": "jaeger-test",
"Host": "localhost",
"Port": 6831
"AgentHost": "localhost",
"AgentPort": 6831
},
"Zipkin": {
"ServiceName": "zipkin-test",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OpenTelemetry.Trace.TracerProviderBuilder.TracerProviderBuilder() -> void
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OpenTelemetry.Trace.TracerProviderBuilder.TracerProviderBuilder() -> void
7 changes: 5 additions & 2 deletions src/OpenTelemetry.Api/Trace/TracerProviderBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@ namespace OpenTelemetry.Trace
/// </summary>
public abstract class TracerProviderBuilder
{
internal TracerProviderBuilder()
/// <summary>
/// Initializes a new instance of the <see cref="TracerProviderBuilder"/> class.
/// </summary>
protected TracerProviderBuilder()
{
}

/// <summary>
/// Adds an instrumentation to the provider.
/// Adds instrumentation to the provider.
/// </summary>
/// <typeparam name="TInstrumentation">Type of instrumentation class.</typeparam>
/// <param name="instrumentationFactory">Function that builds instrumentation.</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,35 @@ public static TracerProviderBuilder AddJaegerExporter(this TracerProviderBuilder
throw new ArgumentNullException(nameof(builder));
}

var exporterOptions = new JaegerExporterOptions();
configure?.Invoke(exporterOptions);
var jaegerExporter = new JaegerExporter(exporterOptions);
if (builder is IDeferredTracerProviderBuilder deferredTracerProviderBuilder)
{
return deferredTracerProviderBuilder.Configure((sp, builder) =>
{
AddJaegerExporter(builder, sp.GetOptions<JaegerExporterOptions>(), configure);
});
}

return AddJaegerExporter(builder, new JaegerExporterOptions(), configure);
}

private static TracerProviderBuilder AddJaegerExporter(TracerProviderBuilder builder, JaegerExporterOptions options, Action<JaegerExporterOptions> configure = null)
{
configure?.Invoke(options);

var jaegerExporter = new JaegerExporter(options);

if (exporterOptions.ExportProcessorType == ExportProcessorType.Simple)
if (options.ExportProcessorType == ExportProcessorType.Simple)
{
return builder.AddProcessor(new SimpleActivityExportProcessor(jaegerExporter));
}
else
{
return builder.AddProcessor(new BatchActivityExportProcessor(
jaegerExporter,
exporterOptions.BatchExportProcessorOptions.MaxQueueSize,
exporterOptions.BatchExportProcessorOptions.ScheduledDelayMilliseconds,
exporterOptions.BatchExportProcessorOptions.ExporterTimeoutMilliseconds,
exporterOptions.BatchExportProcessorOptions.MaxExportBatchSize));
options.BatchExportProcessorOptions.MaxQueueSize,
options.BatchExportProcessorOptions.ScheduledDelayMilliseconds,
options.BatchExportProcessorOptions.ExporterTimeoutMilliseconds,
options.BatchExportProcessorOptions.MaxExportBatchSize));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Includes\PooledList.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PeerServiceResolver.cs" Link="Includes\PeerServiceResolver.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\ResourceSemanticConventions.cs" Link="Includes\ResourceSemanticConventions.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\ServiceProviderExtensions.cs" Link="Includes\ServiceProviderExtensions.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Includes\PooledList.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PeerServiceResolver.cs" Link="Includes\PeerServiceResolver.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\ResourceSemanticConventions.cs" Link="Includes\ResourceSemanticConventions.cs" />
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\ServiceProviderExtensions.cs" Link="Includes\ServiceProviderExtensions.cs" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,35 @@ public static TracerProviderBuilder AddZipkinExporter(this TracerProviderBuilder
throw new ArgumentNullException(nameof(builder));
}

var exporterOptions = new ZipkinExporterOptions();
configure?.Invoke(exporterOptions);
var zipkinExporter = new ZipkinExporter(exporterOptions);
if (builder is IDeferredTracerProviderBuilder deferredTracerProviderBuilder)
{
return deferredTracerProviderBuilder.Configure((sp, builder) =>
{
AddZipkinExporter(builder, sp.GetOptions<ZipkinExporterOptions>(), configure);
});
}

return AddZipkinExporter(builder, new ZipkinExporterOptions(), configure);
}

private static TracerProviderBuilder AddZipkinExporter(TracerProviderBuilder builder, ZipkinExporterOptions options, Action<ZipkinExporterOptions> configure = null)
{
configure?.Invoke(options);

var zipkinExporter = new ZipkinExporter(options);

if (exporterOptions.ExportProcessorType == ExportProcessorType.Simple)
if (options.ExportProcessorType == ExportProcessorType.Simple)
{
return builder.AddProcessor(new SimpleActivityExportProcessor(zipkinExporter));
}
else
{
return builder.AddProcessor(new BatchActivityExportProcessor(
zipkinExporter,
exporterOptions.BatchExportProcessorOptions.MaxQueueSize,
exporterOptions.BatchExportProcessorOptions.ScheduledDelayMilliseconds,
exporterOptions.BatchExportProcessorOptions.ExporterTimeoutMilliseconds,
exporterOptions.BatchExportProcessorOptions.MaxExportBatchSize));
options.BatchExportProcessorOptions.MaxQueueSize,
options.BatchExportProcessorOptions.ScheduledDelayMilliseconds,
options.BatchExportProcessorOptions.ExporterTimeoutMilliseconds,
options.BatchExportProcessorOptions.MaxExportBatchSize));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions
OpenTelemetry.Trace.TracerProviderBuilderExtensions
static Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions.AddOpenTelemetryTracing(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions.AddOpenTelemetryTracing(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<OpenTelemetry.Trace.TracerProviderBuilder> configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions.AddOpenTelemetryTracing(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<System.IServiceProvider, OpenTelemetry.Trace.TracerProviderBuilder> configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddInstrumentation<T>(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder) -> OpenTelemetry.Trace.TracerProviderBuilder
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddProcessor<T>(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder) -> OpenTelemetry.Trace.TracerProviderBuilder
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.Build(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.IServiceProvider serviceProvider) -> OpenTelemetry.Trace.TracerProvider
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.Configure(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.Action<System.IServiceProvider, OpenTelemetry.Trace.TracerProviderBuilder> configure) -> OpenTelemetry.Trace.TracerProviderBuilder
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.SetSampler<T>(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder) -> OpenTelemetry.Trace.TracerProviderBuilder
5 changes: 5 additions & 0 deletions src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## Unreleased

* Added `AddInstrumentation<T>`, `AddProcessor<T>`, `SetSampler<T>`, and
`Configure` extensions to support dependency injection through the
OpenTelemetry.Extensions.Hosting `TracerProviderBuilder`.
([#1889](https:/open-telemetry/opentelemetry-dotnet/pull/1889))

## 1.0.0-rc2

Released 2021-Jan-29
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// <copyright file="TracerProviderBuilderHosting.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 System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection;

namespace OpenTelemetry.Trace
{
/// <summary>
/// A <see cref="TracerProviderBuilderBase"/> with support for deferred initialization using <see cref="IServiceProvider"/> for dependency injection.
/// </summary>
internal class TracerProviderBuilderHosting : TracerProviderBuilderBase, IDeferredTracerProviderBuilder
{
private readonly List<InstrumentationFactory> instrumentationFactories = new List<InstrumentationFactory>();
private readonly List<Type> processorTypes = new List<Type>();
private readonly List<Action<IServiceProvider, TracerProviderBuilder>> configurationActions = new List<Action<IServiceProvider, TracerProviderBuilder>>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like having generic configuration actions like this.

private Type samplerType;

public TracerProviderBuilderHosting(IServiceCollection services)
{
this.Services = services ?? throw new ArgumentNullException(nameof(services));
}

public IServiceCollection Services { get; }

public TracerProviderBuilder AddInstrumentation<TInstrumentation>(
Func<IServiceProvider, TInstrumentation> instrumentationFactory)
where TInstrumentation : class
{
if (instrumentationFactory == null)
{
throw new ArgumentNullException(nameof(instrumentationFactory));
}

this.instrumentationFactories.Add(
new InstrumentationFactory(
typeof(TInstrumentation).Name,
"semver:" + typeof(TInstrumentation).Assembly.GetName().Version,
typeof(TInstrumentation)));

return this;
}

public TracerProviderBuilder AddProcessor<T>()
where T : BaseProcessor<Activity>
{
this.processorTypes.Add(typeof(T));
return this;
}

public TracerProviderBuilder SetSampler<T>()
where T : Sampler
{
this.samplerType = typeof(T);
return this;
}

public TracerProviderBuilder Configure(Action<IServiceProvider, TracerProviderBuilder> configure)
{
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}

this.configurationActions.Add(configure);
return this;
}

public TracerProvider Build(IServiceProvider serviceProvider)
{
foreach (InstrumentationFactory instrumentationFactory in this.instrumentationFactories)
{
this.AddInstrumentation(
instrumentationFactory.Name,
instrumentationFactory.Version,
() => serviceProvider.GetRequiredService(instrumentationFactory.Type));
}

foreach (Type processorType in this.processorTypes)
{
this.AddProcessor((BaseProcessor<Activity>)serviceProvider.GetRequiredService(processorType));
}

if (this.samplerType != null)
{
this.SetSampler((Sampler)serviceProvider.GetRequiredService(this.samplerType));
}

foreach (Action<IServiceProvider, TracerProviderBuilder> configureAction in this.configurationActions)
{
configureAction(serviceProvider, this);
}

return this.Build();
}

private readonly struct InstrumentationFactory
{
public readonly string Name;
public readonly string Version;
public readonly Type Type;

internal InstrumentationFactory(string name, string version, Type type)
{
this.Name = name;
this.Version = version;
this.Type = type;
}
}
}
}
Loading