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

Hosting: Support metrics registration via IServiceCollection & deferred configuration #2412

Merged
merged 11 commits into from
Sep 27, 2021
4 changes: 2 additions & 2 deletions docs/metrics/extending-the-sdk/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public static void Main(string[] args)
{
using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource("MyCompany.MyProduct.MyLibrary")
.AddMetricReader(new MyReader())
.AddReader(new MyReader())
CodeBlanch marked this conversation as resolved.
Show resolved Hide resolved
/** /
TODO: revisit once this exception is removed "System.InvalidOperationException: Only one Metricreader is allowed.".
.AddMetricReader(new BaseExportingMetricReader(new MyExporter()))
.AddReader(new BaseExportingMetricReader(new MyExporter()))
/**/
.Build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ OpenTelemetry.Context.RemotingRuntimeContextSlot<T>.Value.get -> object
OpenTelemetry.Context.RemotingRuntimeContextSlot<T>.Value.set -> void
OpenTelemetry.Context.ThreadLocalRuntimeContextSlot<T>.Value.get -> object
OpenTelemetry.Context.ThreadLocalRuntimeContextSlot<T>.Value.set -> void
OpenTelemetry.Metrics.IDeferredMeterProviderBuilder
OpenTelemetry.Metrics.IDeferredMeterProviderBuilder.Configure(System.Action<System.IServiceProvider, OpenTelemetry.Metrics.MeterProviderBuilder> configure) -> OpenTelemetry.Metrics.MeterProviderBuilder
override OpenTelemetry.Context.AsyncLocalRuntimeContextSlot<T>.Get() -> T
override OpenTelemetry.Context.AsyncLocalRuntimeContextSlot<T>.Set(T value) -> void
OpenTelemetry.Context.AsyncLocalRuntimeContextSlot<T>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ OpenTelemetry.Context.IRuntimeContextSlotValueAccessor.Value.get -> object
OpenTelemetry.Context.IRuntimeContextSlotValueAccessor.Value.set -> void
OpenTelemetry.Context.ThreadLocalRuntimeContextSlot<T>.Value.get -> object
OpenTelemetry.Context.ThreadLocalRuntimeContextSlot<T>.Value.set -> void
OpenTelemetry.Metrics.IDeferredMeterProviderBuilder
OpenTelemetry.Metrics.IDeferredMeterProviderBuilder.Configure(System.Action<System.IServiceProvider, OpenTelemetry.Metrics.MeterProviderBuilder> configure) -> OpenTelemetry.Metrics.MeterProviderBuilder
static OpenTelemetry.Context.RuntimeContext.GetValue(string slotName) -> object
static OpenTelemetry.Context.RuntimeContext.GetValue<T>(string slotName) -> T
static OpenTelemetry.Context.RuntimeContext.SetValue(string slotName, object value) -> void
Expand Down
3 changes: 3 additions & 0 deletions src/OpenTelemetry.Api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

* Added `IDeferredMeterProviderBuilder`
([#2412](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2412))

## 1.2.0-alpha4

Released 2021-Sep-23
Expand Down
36 changes: 36 additions & 0 deletions src/OpenTelemetry.Api/Metrics/IDeferredMeterProviderBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// <copyright file="IDeferredMeterProviderBuilder.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;

namespace OpenTelemetry.Metrics
{
/// <summary>
/// Describes a meter provider builder that supports deferred initialization
/// using an <see cref="IServiceProvider"/> to perform dependency injection.
/// </summary>
public interface IDeferredMeterProviderBuilder
{
/// <summary>
/// Register a callback action to configure the <see
/// cref="MeterProviderBuilder"/> once the application <see
/// cref="IServiceProvider"/> is available.
/// </summary>
/// <param name="configure">Configuration callback.</param>
/// <returns>The supplied <see cref="MeterProviderBuilder"/> for chaining.</returns>
MeterProviderBuilder Configure(Action<IServiceProvider, MeterProviderBuilder> configure);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ public static MeterProviderBuilder AddConsoleExporter(this MeterProviderBuilder

if (options.MetricExportIntervalMilliseconds == Timeout.Infinite)
{
return builder.AddMetricReader(new BaseExportingMetricReader(exporter));
return builder.AddReader(new BaseExportingMetricReader(exporter));
}

return builder.AddMetricReader(new PeriodicExportingMetricReader(exporter, options.MetricExportIntervalMilliseconds));
return builder.AddReader(new PeriodicExportingMetricReader(exporter, options.MetricExportIntervalMilliseconds));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static MeterProviderBuilder AddInMemoryExporter(this MeterProviderBuilder
throw new ArgumentNullException(nameof(exportedItems));
}

return builder.AddMetricReader(new BaseExportingMetricReader(new InMemoryExporter<Metric>(exportedItems)));
return builder.AddReader(new BaseExportingMetricReader(new InMemoryExporter<Metric>(exportedItems)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static MeterProviderBuilder AddOtlpExporter(this MeterProviderBuilder bui

var metricExporter = new OtlpMetricsExporter(options);
var metricReader = new PeriodicExportingMetricReader(metricExporter, options.MetricExportIntervalMilliseconds);
return builder.AddMetricReader(metricReader);
return builder.AddReader(metricReader);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static MeterProviderBuilder AddPrometheusExporter(this MeterProviderBuild

var metricsHttpServer = new PrometheusExporterMetricsHttpServer(exporter);
metricsHttpServer.Start();
return builder.AddMetricReader(reader);
return builder.AddReader(reader);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
OpenTelemetry.Exporter.ZipkinExporter
OpenTelemetry.Exporter.ZipkinExporter.ZipkinExporter(OpenTelemetry.Exporter.ZipkinExporterOptions options, System.Net.Http.HttpClient client = null) -> void
OpenTelemetry.Exporter.ZipkinExporterOptions
OpenTelemetry.Exporter.ZipkinExporterOptions.BatchExportProcessorOptions.get -> OpenTelemetry.BatchExportProcessorOptions<System.Diagnostics.Activity>
OpenTelemetry.Exporter.ZipkinExporterOptions.BatchExportProcessorOptions.set -> void
OpenTelemetry.Exporter.ZipkinExporterOptions.Endpoint.get -> System.Uri
OpenTelemetry.Exporter.ZipkinExporterOptions.Endpoint.set -> void
OpenTelemetry.Exporter.ZipkinExporterOptions.ExportProcessorType.get -> OpenTelemetry.ExportProcessorType
OpenTelemetry.Exporter.ZipkinExporterOptions.ExportProcessorType.set -> void
OpenTelemetry.Exporter.ZipkinExporterOptions.MaxPayloadSizeInBytes.get -> int?
OpenTelemetry.Exporter.ZipkinExporterOptions.MaxPayloadSizeInBytes.set -> void
OpenTelemetry.Exporter.ZipkinExporterOptions.UseShortTraceIds.get -> bool
OpenTelemetry.Exporter.ZipkinExporterOptions.UseShortTraceIds.set -> void
OpenTelemetry.Exporter.ZipkinExporterOptions.ZipkinExporterOptions() -> void
OpenTelemetry.Trace.ZipkinExporterHelperExtensions
override OpenTelemetry.Exporter.ZipkinExporter.Export(in OpenTelemetry.Batch<System.Diagnostics.Activity> batch) -> OpenTelemetry.ExportResult
static OpenTelemetry.Trace.ZipkinExporterHelperExtensions.AddZipkinExporter(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action<OpenTelemetry.Exporter.ZipkinExporterOptions> configure = null) -> OpenTelemetry.Trace.TracerProviderBuilder
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions
OpenTelemetry.Metrics.MeterProviderBuilderExtensions
OpenTelemetry.Trace.TracerProviderBuilderExtensions
static Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions.AddOpenTelemetryMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions.AddOpenTelemetryMetrics(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<OpenTelemetry.Metrics.MeterProviderBuilder> configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
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 OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddInstrumentation<T>(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder) -> OpenTelemetry.Metrics.MeterProviderBuilder
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.AddReader<T>(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder) -> OpenTelemetry.Metrics.MeterProviderBuilder
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.Build(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.IServiceProvider serviceProvider) -> OpenTelemetry.Metrics.MeterProvider
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.Configure(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder, System.Action<System.IServiceProvider, OpenTelemetry.Metrics.MeterProviderBuilder> configure) -> OpenTelemetry.Metrics.MeterProviderBuilder
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.GetServices(this OpenTelemetry.Metrics.MeterProviderBuilder meterProviderBuilder) -> 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
Expand Down
6 changes: 6 additions & 0 deletions src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
* Removes upper constraint for Microsoft.Extensions.Hosting.Abstractions
dependency. ([#2179](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2179))

* Added `AddOpenTelemetryMetrics` `IServiceCollection` extensions and added
`AddInstrumentation<T>`, `AddReader<T>`, & `Configure` extensions to support
reyang marked this conversation as resolved.
Show resolved Hide resolved
dependency injection through the OpenTelemetry.Extensions.Hosting
`MeterProviderBuilder`.
([#2412](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2412))

## 1.0.0-rc7

Released 2021-Jul-12
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// <copyright file="MeterProviderBuilderHosting.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 Microsoft.Extensions.DependencyInjection;

namespace OpenTelemetry.Metrics
{
/// <summary>
/// A <see cref="MeterProviderBuilderBase"/> with support for deferred initialization using <see cref="IServiceProvider"/> for dependency injection.
/// </summary>
internal sealed class MeterProviderBuilderHosting : MeterProviderBuilderBase, IDeferredMeterProviderBuilder
{
private readonly List<Action<IServiceProvider, MeterProviderBuilder>> configurationActions = new List<Action<IServiceProvider, MeterProviderBuilder>>();

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

public IServiceCollection Services { get; }

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

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

public MeterProvider Build(IServiceProvider serviceProvider)
CodeBlanch marked this conversation as resolved.
Show resolved Hide resolved
{
int i = 0;
while (i < this.configurationActions.Count)
CodeBlanch marked this conversation as resolved.
Show resolved Hide resolved
{
this.configurationActions[i++](serviceProvider, this);
}

return this.Build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

namespace OpenTelemetry.Extensions.Hosting.Implementation
Expand All @@ -36,12 +37,18 @@ public Task StartAsync(CancellationToken cancellationToken)
{
try
{
// The sole purpose of this HostedService is to ensure
// all instrumentations are created and started.
// This method is invoked when host starts, and
// by requesting the TracerProvider from DI
// it ensures all instrumentations gets started.
this.serviceProvider.GetRequiredService<TracerProvider>();
// The sole purpose of this HostedService is to ensure all
// instrumentations are created and started. This method is
// invoked when host starts, and by requesting the
// TracerProvider & MeterProvider from DI it ensures all
// instrumentations gets started.
var tracerProvider = this.serviceProvider.GetService<TracerProvider>();
var meterProvider = this.serviceProvider.GetService<MeterProvider>();
CodeBlanch marked this conversation as resolved.
Show resolved Hide resolved

if (tracerProvider == null && meterProvider == null)
{
throw new InvalidOperationException("Could not resolve TracerProvider or MeterProvider through ServiceProvider.");
}
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// <copyright file="MeterProviderBuilderExtensions.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.Diagnostics;
using Microsoft.Extensions.DependencyInjection;

namespace OpenTelemetry.Metrics
{
/// <summary>
/// Contains extension methods for the <see cref="MeterProviderBuilder"/> class.
/// </summary>
public static class MeterProviderBuilderExtensions
{
/// <summary>
/// Adds instrumentation to the provider.
/// </summary>
/// <typeparam name="T">Instrumentation type.</typeparam>
/// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param>
/// <returns>The supplied <see cref="MeterProviderBuilder"/> for chaining.</returns>
public static MeterProviderBuilder AddInstrumentation<T>(this MeterProviderBuilder meterProviderBuilder)
where T : class
{
if (meterProviderBuilder is MeterProviderBuilderHosting meterProviderBuilderHosting)
{
meterProviderBuilderHosting.Configure((sp, builder) => builder
.AddInstrumentation(() => sp.GetRequiredService<T>()));
}

return meterProviderBuilder;
}

/// <summary>
/// Adds a reader to the provider.
/// </summary>
/// <typeparam name="T">Reader type.</typeparam>
/// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param>
/// <returns>The supplied <see cref="MeterProviderBuilder"/> for chaining.</returns>
public static MeterProviderBuilder AddReader<T>(this MeterProviderBuilder meterProviderBuilder)
where T : MetricReader
{
if (meterProviderBuilder is MeterProviderBuilderHosting meterProviderBuilderHosting)
{
meterProviderBuilderHosting.Configure((sp, builder) => builder
.AddReader(sp.GetRequiredService<T>()));
}

return meterProviderBuilder;
}

/// <summary>
/// Register a callback action to configure the <see
/// cref="MeterProviderBuilder"/> once the application <see
/// cref="IServiceProvider"/> is available.
/// </summary>
/// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param>
/// <param name="configure">Configuration callback.</param>
/// <returns>The supplied <see cref="MeterProviderBuilder"/> for chaining.</returns>
public static MeterProviderBuilder Configure(this MeterProviderBuilder meterProviderBuilder, Action<IServiceProvider, MeterProviderBuilder> configure)
{
if (meterProviderBuilder is IDeferredMeterProviderBuilder deferredMeterProviderBuilder)
{
deferredMeterProviderBuilder.Configure(configure);
}

return meterProviderBuilder;
}

/// <summary>
/// Gets the application <see cref="IServiceCollection"/> attached to
/// the <see cref="MeterProviderBuilder"/>.
/// </summary>
/// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param>
/// <returns><see cref="IServiceCollection"/> or <see langword="null"/>
/// if services are unavailable.</returns>
public static IServiceCollection GetServices(this MeterProviderBuilder meterProviderBuilder)
{
if (meterProviderBuilder is MeterProviderBuilderHosting meterProviderBuilderHosting)
{
return meterProviderBuilderHosting.Services;
}

return null;
}

/// <summary>
/// Run the configured actions to initialize the <see cref="MeterProvider"/>.
/// </summary>
/// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param>
/// <param name="serviceProvider"><see cref="IServiceProvider"/>.</param>
/// <returns><see cref="MeterProvider"/>.</returns>
public static MeterProvider Build(this MeterProviderBuilder meterProviderBuilder, IServiceProvider serviceProvider)
{
if (meterProviderBuilder is MeterProviderBuilderHosting meterProviderBuilderHosting)
{
return meterProviderBuilderHosting.Build(serviceProvider);
}

if (meterProviderBuilder is MeterProviderBuilderBase meterProviderBuilderBase)
{
return meterProviderBuilderBase.Build();
}

return null;
}
}
}
Loading