Skip to content

Commit

Permalink
Hosting: Support metrics registration via IServiceCollection & deferr…
Browse files Browse the repository at this point in the history
…ed configuration (#2412)

* Add metrics support to hosting lib.

* Bug fixes and some public api additions.

* Added missing zipkin publicapi files.

* Unit tests.

* CHANGELOG updates

* Fixes

* Code review.

* Update src/OpenTelemetry.Extensions.Hosting/CHANGELOG.md

Co-authored-by: Reiley Yang <[email protected]>

* Code review.

* Warning fixup.

Co-authored-by: Reiley Yang <[email protected]>
Co-authored-by: Cijo Thomas <[email protected]>
  • Loading branch information
3 people authored Sep 27, 2021
1 parent c60dc36 commit 38ee521
Show file tree
Hide file tree
Showing 32 changed files with 737 additions and 130 deletions.
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())
/** /
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
Empty file.
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` extensions on `IServiceCollection` to register
OpenTelemetry `MeterProvider` with application services. Added
`AddInstrumentation<T>`, `AddReader<T>`, and `Configure` extensions on
`MeterProviderBuilder` to support dependency injection scenarios.
([#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,65 @@
// <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)
{
if (serviceProvider == null)
{
throw new ArgumentNullException(nameof(serviceProvider));
}

// Note: Not using a foreach loop because additional actions can be
// added during each call.
for (int i = 0; i < this.configurationActions.Count; i++)
{
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,15 @@ 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, exporters, etc., are created and started.
var meterProvider = this.serviceProvider.GetService<MeterProvider>();
var tracerProvider = this.serviceProvider.GetService<TracerProvider>();

if (meterProvider == null && tracerProvider == null)
{
throw new InvalidOperationException("Could not resolve either MeterProvider or TracerProvider through application ServiceProvider, OpenTelemetry SDK has not been initialized.");
}
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,16 @@ public TracerProviderBuilder Configure(Action<IServiceProvider, TracerProviderBu

public TracerProvider Build(IServiceProvider serviceProvider)
{
int i = 0;
while (i < this.configurationActions.Count)
if (serviceProvider == null)
{
this.configurationActions[i++](serviceProvider, this);
throw new ArgumentNullException(nameof(serviceProvider));
}

// Note: Not using a foreach loop because additional actions can be
// added during each call.
for (int i = 0; i < this.configurationActions.Count; i++)
{
this.configurationActions[i](serviceProvider, this);
}

return this.Build();
Expand Down
Loading

0 comments on commit 38ee521

Please sign in to comment.