Hosting: Support metrics registration via IServiceCollection & deferred 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 <reyang@microsoft.com>

* Code review.

* Warning fixup.

Co-authored-by: Reiley Yang <reyang@microsoft.com>
Co-authored-by: Cijo Thomas <cithomas@microsoft.com>
This commit is contained in:
Mikel Blanchard 2021-09-27 10:30:10 -07:00 committed by GitHub
parent c60dc3697c
commit 38ee521b24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 737 additions and 130 deletions

View File

@ -29,10 +29,10 @@ public class Program
{ {
using var meterProvider = Sdk.CreateMeterProviderBuilder() using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource("MyCompany.MyProduct.MyLibrary") .AddSource("MyCompany.MyProduct.MyLibrary")
.AddMetricReader(new MyReader()) .AddReader(new MyReader())
/** / /** /
TODO: revisit once this exception is removed "System.InvalidOperationException: Only one Metricreader is allowed.". 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(); .Build();

View File

@ -7,6 +7,8 @@ OpenTelemetry.Context.RemotingRuntimeContextSlot<T>.Value.get -> object
OpenTelemetry.Context.RemotingRuntimeContextSlot<T>.Value.set -> void OpenTelemetry.Context.RemotingRuntimeContextSlot<T>.Value.set -> void
OpenTelemetry.Context.ThreadLocalRuntimeContextSlot<T>.Value.get -> object OpenTelemetry.Context.ThreadLocalRuntimeContextSlot<T>.Value.get -> object
OpenTelemetry.Context.ThreadLocalRuntimeContextSlot<T>.Value.set -> void 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>.Get() -> T
override OpenTelemetry.Context.AsyncLocalRuntimeContextSlot<T>.Set(T value) -> void override OpenTelemetry.Context.AsyncLocalRuntimeContextSlot<T>.Set(T value) -> void
OpenTelemetry.Context.AsyncLocalRuntimeContextSlot<T> OpenTelemetry.Context.AsyncLocalRuntimeContextSlot<T>

View File

@ -5,6 +5,8 @@ OpenTelemetry.Context.IRuntimeContextSlotValueAccessor.Value.get -> object
OpenTelemetry.Context.IRuntimeContextSlotValueAccessor.Value.set -> void OpenTelemetry.Context.IRuntimeContextSlotValueAccessor.Value.set -> void
OpenTelemetry.Context.ThreadLocalRuntimeContextSlot<T>.Value.get -> object OpenTelemetry.Context.ThreadLocalRuntimeContextSlot<T>.Value.get -> object
OpenTelemetry.Context.ThreadLocalRuntimeContextSlot<T>.Value.set -> void 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(string slotName) -> object
static OpenTelemetry.Context.RuntimeContext.GetValue<T>(string slotName) -> T static OpenTelemetry.Context.RuntimeContext.GetValue<T>(string slotName) -> T
static OpenTelemetry.Context.RuntimeContext.SetValue(string slotName, object value) -> void static OpenTelemetry.Context.RuntimeContext.SetValue(string slotName, object value) -> void

View File

@ -2,6 +2,9 @@
## Unreleased ## Unreleased
* Added `IDeferredMeterProviderBuilder`
([#2412](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2412))
## 1.2.0-alpha4 ## 1.2.0-alpha4
Released 2021-Sep-23 Released 2021-Sep-23

View File

@ -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);
}
}

View File

@ -43,10 +43,10 @@ namespace OpenTelemetry.Metrics
if (options.MetricExportIntervalMilliseconds == Timeout.Infinite) 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));
} }
} }
} }

View File

@ -40,7 +40,7 @@ namespace OpenTelemetry.Metrics
throw new ArgumentNullException(nameof(exportedItems)); throw new ArgumentNullException(nameof(exportedItems));
} }
return builder.AddMetricReader(new BaseExportingMetricReader(new InMemoryExporter<Metric>(exportedItems))); return builder.AddReader(new BaseExportingMetricReader(new InMemoryExporter<Metric>(exportedItems)));
} }
} }
} }

View File

@ -42,7 +42,7 @@ namespace OpenTelemetry.Metrics
var metricExporter = new OtlpMetricsExporter(options); var metricExporter = new OtlpMetricsExporter(options);
var metricReader = new PeriodicExportingMetricReader(metricExporter, options.MetricExportIntervalMilliseconds); var metricReader = new PeriodicExportingMetricReader(metricExporter, options.MetricExportIntervalMilliseconds);
return builder.AddMetricReader(metricReader); return builder.AddReader(metricReader);
} }
} }
} }

View File

@ -43,7 +43,7 @@ namespace OpenTelemetry.Metrics
var metricsHttpServer = new PrometheusExporterMetricsHttpServer(exporter); var metricsHttpServer = new PrometheusExporterMetricsHttpServer(exporter);
metricsHttpServer.Start(); metricsHttpServer.Start();
return builder.AddMetricReader(reader); return builder.AddReader(reader);
} }
} }
} }

View File

@ -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

View File

@ -1,7 +1,15 @@
Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions Microsoft.Extensions.DependencyInjection.OpenTelemetryServicesExtensions
OpenTelemetry.Metrics.MeterProviderBuilderExtensions
OpenTelemetry.Trace.TracerProviderBuilderExtensions 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) -> 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<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.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.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.Build(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, System.IServiceProvider serviceProvider) -> OpenTelemetry.Trace.TracerProvider

View File

@ -5,6 +5,12 @@
* Removes upper constraint for Microsoft.Extensions.Hosting.Abstractions * Removes upper constraint for Microsoft.Extensions.Hosting.Abstractions
dependency. ([#2179](https://github.com/open-telemetry/opentelemetry-dotnet/pull/2179)) 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 ## 1.0.0-rc7
Released 2021-Jul-12 Released 2021-Jul-12

View File

@ -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();
}
}
}

View File

@ -19,6 +19,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace; using OpenTelemetry.Trace;
namespace OpenTelemetry.Extensions.Hosting.Implementation namespace OpenTelemetry.Extensions.Hosting.Implementation
@ -36,12 +37,15 @@ namespace OpenTelemetry.Extensions.Hosting.Implementation
{ {
try try
{ {
// The sole purpose of this HostedService is to ensure // The sole purpose of this HostedService is to ensure all
// all instrumentations are created and started. // instrumentations, exporters, etc., are created and started.
// This method is invoked when host starts, and var meterProvider = this.serviceProvider.GetService<MeterProvider>();
// by requesting the TracerProvider from DI var tracerProvider = this.serviceProvider.GetService<TracerProvider>();
// it ensures all instrumentations gets started.
this.serviceProvider.GetRequiredService<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) catch (Exception ex)
{ {

View File

@ -47,10 +47,16 @@ namespace OpenTelemetry.Trace
public TracerProvider Build(IServiceProvider serviceProvider) public TracerProvider Build(IServiceProvider serviceProvider)
{ {
int i = 0; if (serviceProvider == null)
while (i < this.configurationActions.Count)
{ {
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(); return this.Build();

View File

@ -0,0 +1,119 @@
// <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 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;
}
}
}

View File

@ -18,6 +18,7 @@ using System;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using OpenTelemetry.Extensions.Hosting.Implementation; using OpenTelemetry.Extensions.Hosting.Implementation;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace; using OpenTelemetry.Trace;
namespace Microsoft.Extensions.DependencyInjection namespace Microsoft.Extensions.DependencyInjection
@ -55,6 +56,34 @@ namespace Microsoft.Extensions.DependencyInjection
return services.AddOpenTelemetryTracing(sp => builder.Build(sp)); return services.AddOpenTelemetryTracing(sp => builder.Build(sp));
} }
/// <summary>
/// Adds OpenTelemetry MeterProvider to the specified <see cref="IServiceCollection" />.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddOpenTelemetryMetrics(this IServiceCollection services)
{
return services.AddOpenTelemetryMetrics(builder => { });
}
/// <summary>
/// Adds OpenTelemetry MeterProvider to the specified <see cref="IServiceCollection" />.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
/// <param name="configure">Callback action to configure the <see cref="MeterProviderBuilder"/>.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddOpenTelemetryMetrics(this IServiceCollection services, Action<MeterProviderBuilder> configure)
{
if (configure is null)
{
throw new ArgumentNullException(nameof(configure));
}
var builder = new MeterProviderBuilderHosting(services);
configure(builder);
return services.AddOpenTelemetryMetrics(sp => builder.Build(sp));
}
/// <summary> /// <summary>
/// Adds OpenTelemetry TracerProvider to the specified <see cref="IServiceCollection" />. /// Adds OpenTelemetry TracerProvider to the specified <see cref="IServiceCollection" />.
/// </summary> /// </summary>
@ -85,5 +114,36 @@ namespace Microsoft.Extensions.DependencyInjection
return services; return services;
} }
/// <summary>
/// Adds OpenTelemetry MeterProvider to the specified <see cref="IServiceCollection" />.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
/// <param name="createMeterProvider">A delegate that provides the tracer provider to be registered.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
private static IServiceCollection AddOpenTelemetryMetrics(this IServiceCollection services, Func<IServiceProvider, MeterProvider> createMeterProvider)
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}
if (createMeterProvider is null)
{
throw new ArgumentNullException(nameof(createMeterProvider));
}
try
{
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, TelemetryHostedService>());
return services.AddSingleton(s => createMeterProvider(s));
}
catch (Exception ex)
{
HostingExtensionsEventSource.Log.FailedInitialize(ex);
}
return services;
}
} }
} }

View File

@ -0,0 +1,120 @@
// <copyright file="MeterProviderBuilderBase.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 OpenTelemetry.Resources;
namespace OpenTelemetry.Metrics
{
/// <summary>
/// Build MeterProvider with Resource, Readers, and Instrumentation.
/// </summary>
public abstract class MeterProviderBuilderBase : MeterProviderBuilder
{
private readonly List<InstrumentationFactory> instrumentationFactories = new List<InstrumentationFactory>();
private readonly List<string> meterSources = new List<string>();
private ResourceBuilder resourceBuilder = ResourceBuilder.CreateDefault();
protected MeterProviderBuilderBase()
{
}
internal List<MetricReader> MetricReaders { get; } = new List<MetricReader>();
/// <inheritdoc />
public override MeterProviderBuilder AddInstrumentation<TInstrumentation>(Func<TInstrumentation> instrumentationFactory)
{
if (instrumentationFactory == null)
{
throw new ArgumentNullException(nameof(instrumentationFactory));
}
this.instrumentationFactories.Add(
new InstrumentationFactory(
typeof(TInstrumentation).Name,
"semver:" + typeof(TInstrumentation).Assembly.GetName().Version,
instrumentationFactory));
return this;
}
/// <inheritdoc />
public override MeterProviderBuilder AddSource(params string[] names)
{
if (names == null)
{
throw new ArgumentNullException(nameof(names));
}
foreach (var name in names)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException($"{nameof(names)} contains null or whitespace string.");
}
this.meterSources.Add(name);
}
return this;
}
internal MeterProviderBuilder AddReader(MetricReader reader)
{
if (this.MetricReaders.Count >= 1)
{
throw new InvalidOperationException("Only one Metricreader is allowed.");
}
this.MetricReaders.Add(reader);
return this;
}
internal MeterProviderBuilder SetResourceBuilder(ResourceBuilder resourceBuilder)
{
this.resourceBuilder = resourceBuilder ?? throw new ArgumentNullException(nameof(resourceBuilder));
return this;
}
/// <summary>
/// Run the configured actions to initialize the <see cref="MeterProvider"/>.
/// </summary>
/// <returns><see cref="MeterProvider"/>.</returns>
protected MeterProvider Build()
{
return new MeterProviderSdk(
this.resourceBuilder.Build(),
this.meterSources,
this.instrumentationFactories,
this.MetricReaders.ToArray());
}
internal readonly struct InstrumentationFactory
{
public readonly string Name;
public readonly string Version;
public readonly Func<object> Factory;
internal InstrumentationFactory(string name, string version, Func<object> factory)
{
this.Name = name;
this.Version = version;
this.Factory = factory;
}
}
}
}

View File

@ -14,6 +14,7 @@
// limitations under the License. // limitations under the License.
// </copyright> // </copyright>
using System;
using OpenTelemetry.Resources; using OpenTelemetry.Resources;
namespace OpenTelemetry.Metrics namespace OpenTelemetry.Metrics
@ -24,16 +25,16 @@ namespace OpenTelemetry.Metrics
public static class MeterProviderBuilderExtensions public static class MeterProviderBuilderExtensions
{ {
/// <summary> /// <summary>
/// Add metric reader. /// Adds a reader to the provider.
/// </summary> /// </summary>
/// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param> /// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param>
/// <param name="metricReader">Metricreader.</param> /// <param name="reader"><see cref="MetricReader"/>.</param>
/// <returns><see cref="MeterProvider"/>.</returns> /// <returns><see cref="MeterProvider"/>.</returns>
public static MeterProviderBuilder AddMetricReader(this MeterProviderBuilder meterProviderBuilder, MetricReader metricReader) public static MeterProviderBuilder AddReader(this MeterProviderBuilder meterProviderBuilder, MetricReader reader)
{ {
if (meterProviderBuilder is MeterProviderBuilderSdk meterProviderBuilderSdk) if (meterProviderBuilder is MeterProviderBuilderBase meterProviderBuilderBase)
{ {
return meterProviderBuilderSdk.AddMetricReader(metricReader); return meterProviderBuilderBase.AddReader(reader);
} }
return meterProviderBuilder; return meterProviderBuilder;
@ -48,9 +49,9 @@ namespace OpenTelemetry.Metrics
/// <returns>Returns <see cref="MeterProviderBuilder"/> for chaining.</returns> /// <returns>Returns <see cref="MeterProviderBuilder"/> for chaining.</returns>
public static MeterProviderBuilder SetResourceBuilder(this MeterProviderBuilder meterProviderBuilder, ResourceBuilder resourceBuilder) public static MeterProviderBuilder SetResourceBuilder(this MeterProviderBuilder meterProviderBuilder, ResourceBuilder resourceBuilder)
{ {
if (meterProviderBuilder is MeterProviderBuilderSdk meterProviderBuilderSdk) if (meterProviderBuilder is MeterProviderBuilderBase meterProviderBuilderBase)
{ {
meterProviderBuilderSdk.SetResourceBuilder(resourceBuilder); meterProviderBuilderBase.SetResourceBuilder(resourceBuilder);
} }
return meterProviderBuilder; return meterProviderBuilder;
@ -63,9 +64,14 @@ namespace OpenTelemetry.Metrics
/// <returns><see cref="MeterProvider"/>.</returns> /// <returns><see cref="MeterProvider"/>.</returns>
public static MeterProvider Build(this MeterProviderBuilder meterProviderBuilder) public static MeterProvider Build(this MeterProviderBuilder meterProviderBuilder)
{ {
if (meterProviderBuilder is IDeferredMeterProviderBuilder)
{
throw new NotSupportedException("DeferredMeterProviderBuilder requires a ServiceProvider to build.");
}
if (meterProviderBuilder is MeterProviderBuilderSdk meterProviderBuilderSdk) if (meterProviderBuilder is MeterProviderBuilderSdk meterProviderBuilderSdk)
{ {
return meterProviderBuilderSdk.Build(); return meterProviderBuilderSdk.BuildSdk();
} }
return null; return null;

View File

@ -14,99 +14,10 @@
// limitations under the License. // limitations under the License.
// </copyright> // </copyright>
using System;
using System.Collections.Generic;
using OpenTelemetry.Resources;
namespace OpenTelemetry.Metrics namespace OpenTelemetry.Metrics
{ {
internal class MeterProviderBuilderSdk : MeterProviderBuilder internal class MeterProviderBuilderSdk : MeterProviderBuilderBase
{ {
private readonly List<InstrumentationFactory> instrumentationFactories = new List<InstrumentationFactory>(); internal MeterProvider BuildSdk() => this.Build();
private readonly List<string> meterSources = new List<string>();
private ResourceBuilder resourceBuilder = ResourceBuilder.CreateDefault();
internal MeterProviderBuilderSdk()
{
}
internal List<MetricReader> MetricReaders { get; } = new List<MetricReader>();
public override MeterProviderBuilder AddInstrumentation<TInstrumentation>(Func<TInstrumentation> instrumentationFactory)
{
if (instrumentationFactory == null)
{
throw new ArgumentNullException(nameof(instrumentationFactory));
}
this.instrumentationFactories.Add(
new InstrumentationFactory(
typeof(TInstrumentation).Name,
"semver:" + typeof(TInstrumentation).Assembly.GetName().Version,
instrumentationFactory));
return this;
}
public override MeterProviderBuilder AddSource(params string[] names)
{
if (names == null)
{
throw new ArgumentNullException(nameof(names));
}
foreach (var name in names)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException($"{nameof(names)} contains null or whitespace string.");
}
this.meterSources.Add(name);
}
return this;
}
internal MeterProviderBuilderSdk AddMetricReader(MetricReader metricReader)
{
if (this.MetricReaders.Count >= 1)
{
throw new InvalidOperationException("Only one Metricreader is allowed.");
}
this.MetricReaders.Add(metricReader);
return this;
}
internal MeterProviderBuilderSdk SetResourceBuilder(ResourceBuilder resourceBuilder)
{
this.resourceBuilder = resourceBuilder ?? throw new ArgumentNullException(nameof(resourceBuilder));
return this;
}
internal MeterProvider Build()
{
return new MeterProviderSdk(
this.resourceBuilder.Build(),
this.meterSources,
this.instrumentationFactories,
this.MetricReaders.ToArray());
}
// TODO: This is copied from TracerProviderBuilderSdk. Move to common location.
internal readonly struct InstrumentationFactory
{
public readonly string Name;
public readonly string Version;
public readonly Func<object> Factory;
internal InstrumentationFactory(string name, string version, Func<object> factory)
{
this.Name = name;
this.Version = version;
this.Factory = factory;
}
}
} }
} }

View File

@ -136,6 +136,10 @@ namespace OpenTelemetry.Metrics
internal Resource Resource { get; } internal Resource Resource { get; }
internal List<object> Instrumentations => this.instrumentations;
internal MetricReader Reader => this.reader;
internal void MeasurementsCompleted(Instrument instrument, object state) internal void MeasurementsCompleted(Instrument instrument, object state)
{ {
Console.WriteLine($"Instrument {instrument.Meter.Name}:{instrument.Name} completed."); Console.WriteLine($"Instrument {instrument.Meter.Name}:{instrument.Name} completed.");

View File

@ -100,7 +100,7 @@ namespace OpenTelemetry.Trace
{ {
if (tracerProviderBuilder is IDeferredTracerProviderBuilder) if (tracerProviderBuilder is IDeferredTracerProviderBuilder)
{ {
throw new NotSupportedException("DeferredTracerBuilder requires a ServiceProvider to build."); throw new NotSupportedException("DeferredTracerProviderBuilder requires a ServiceProvider to build.");
} }
if (tracerProviderBuilder is TracerProviderBuilderSdk tracerProviderBuilderSdk) if (tracerProviderBuilder is TracerProviderBuilderSdk tracerProviderBuilderSdk)

View File

@ -92,7 +92,7 @@ namespace Benchmarks.Metrics
}; };
this.provider = Sdk.CreateMeterProviderBuilder() this.provider = Sdk.CreateMeterProviderBuilder()
.AddSource("TestMeter") .AddSource("TestMeter")
.AddMetricReader(this.reader) .AddReader(this.reader)
.Build(); .Build();
this.meter = new Meter("TestMeter"); this.meter = new Meter("TestMeter");

View File

@ -67,7 +67,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
using var provider = Sdk.CreateMeterProviderBuilder() using var provider = Sdk.CreateMeterProviderBuilder()
.SetResourceBuilder(resourceBuilder) .SetResourceBuilder(resourceBuilder)
.AddSource("TestMeter") .AddSource("TestMeter")
.AddMetricReader(metricReader) .AddReader(metricReader)
.Build(); .Build();
exporter.ParentProvider = provider; exporter.ParentProvider = provider;

View File

@ -0,0 +1,238 @@
// <copyright file="HostingMeterExtensionTests.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 System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OpenTelemetry.Metrics;
using Xunit;
namespace OpenTelemetry.Extensions.Hosting.Tests
{
public class HostingMeterExtensionTests
{
[Fact]
public async Task AddOpenTelemetryMeterProviderInstrumentationCreationAndDisposal()
{
var testInstrumentation = new TestInstrumentation();
var callbackRun = false;
var builder = new HostBuilder().ConfigureServices(services =>
{
services.AddOpenTelemetryMetrics(builder =>
{
builder.AddInstrumentation(() =>
{
callbackRun = true;
return testInstrumentation;
});
});
});
var host = builder.Build();
Assert.False(callbackRun);
Assert.False(testInstrumentation.Disposed);
await host.StartAsync();
Assert.True(callbackRun);
Assert.False(testInstrumentation.Disposed);
await host.StopAsync();
Assert.True(callbackRun);
Assert.False(testInstrumentation.Disposed);
host.Dispose();
Assert.True(callbackRun);
Assert.True(testInstrumentation.Disposed);
}
[Fact]
public void AddOpenTelemetryMeterProvider_HostBuilt_OpenTelemetrySdk_RegisteredAsSingleton()
{
var builder = new HostBuilder().ConfigureServices(services =>
{
services.AddOpenTelemetryMetrics();
});
var host = builder.Build();
var meterProvider1 = host.Services.GetRequiredService<MeterProvider>();
var meterProvider2 = host.Services.GetRequiredService<MeterProvider>();
Assert.Same(meterProvider1, meterProvider2);
}
[Fact]
public void AddOpenTelemetryMeterProvider_ServiceProviderArgument_ServicesRegistered()
{
var testInstrumentation = new TestInstrumentation();
var services = new ServiceCollection();
services.AddSingleton(testInstrumentation);
services.AddOpenTelemetryMetrics(builder =>
{
builder.Configure(
(sp, b) => b.AddInstrumentation(() => sp.GetRequiredService<TestInstrumentation>()));
});
var serviceProvider = services.BuildServiceProvider();
var meterFactory = serviceProvider.GetRequiredService<MeterProvider>();
Assert.NotNull(meterFactory);
Assert.False(testInstrumentation.Disposed);
serviceProvider.Dispose();
Assert.True(testInstrumentation.Disposed);
}
[Fact]
public void AddOpenTelemetryMeterProvider_BadArgs_NullServiceCollection()
{
ServiceCollection services = null;
Assert.Throws<ArgumentNullException>(() => services.AddOpenTelemetryMetrics(null));
Assert.Throws<ArgumentNullException>(() =>
services.AddOpenTelemetryMetrics(builder =>
{
builder.Configure(
(sp, b) => b.AddInstrumentation(() => sp.GetRequiredService<TestInstrumentation>()));
}));
}
[Fact]
public void AddOpenTelemetryMeterProvider_GetServicesExtension()
{
var services = new ServiceCollection();
services.AddOpenTelemetryMetrics(builder => AddMyFeature(builder));
using var serviceProvider = services.BuildServiceProvider();
var meterProvider = (MeterProviderSdk)serviceProvider.GetRequiredService<MeterProvider>();
Assert.True(meterProvider.Reader is TestReader);
}
[Fact]
public void AddOpenTelemetryMeterProvider_NestedConfigureCallbacks()
{
int configureCalls = 0;
var services = new ServiceCollection();
services.AddOpenTelemetryMetrics(builder => builder
.Configure((sp1, builder1) =>
{
configureCalls++;
builder1.Configure((sp2, builder2) =>
{
configureCalls++;
});
}));
using var serviceProvider = services.BuildServiceProvider();
var meterFactory = serviceProvider.GetRequiredService<MeterProvider>();
Assert.Equal(2, configureCalls);
}
[Fact]
public void AddOpenTelemetryMeterProvider_ConfigureCallbacksUsingExtensions()
{
var services = new ServiceCollection();
services.AddSingleton<TestInstrumentation>();
services.AddSingleton<TestReader>();
services.AddOpenTelemetryMetrics(builder => builder
.Configure((sp1, builder1) =>
{
builder1
.AddInstrumentation<TestInstrumentation>()
.AddReader<TestReader>();
}));
using var serviceProvider = services.BuildServiceProvider();
var meterProvider = (MeterProviderSdk)serviceProvider.GetRequiredService<MeterProvider>();
Assert.True(meterProvider.Instrumentations.FirstOrDefault() is TestInstrumentation);
Assert.True(meterProvider.Reader is TestReader);
}
[Fact(Skip = "Known limitation. See issue 1215.")]
public void AddOpenTelemetryMeterProvider_Idempotent()
{
var testInstrumentation1 = new TestInstrumentation();
var testInstrumentation2 = new TestInstrumentation();
var services = new ServiceCollection();
services.AddSingleton(testInstrumentation1);
services.AddOpenTelemetryMetrics(builder =>
{
builder.AddInstrumentation(() => testInstrumentation1);
});
services.AddOpenTelemetryMetrics(builder =>
{
builder.AddInstrumentation(() => testInstrumentation2);
});
var serviceProvider = services.BuildServiceProvider();
var meterFactory = serviceProvider.GetRequiredService<MeterProvider>();
Assert.NotNull(meterFactory);
Assert.False(testInstrumentation1.Disposed);
Assert.False(testInstrumentation2.Disposed);
serviceProvider.Dispose();
Assert.True(testInstrumentation1.Disposed);
Assert.True(testInstrumentation2.Disposed);
}
private static MeterProviderBuilder AddMyFeature(MeterProviderBuilder meterProviderBuilder)
{
(meterProviderBuilder.GetServices() ?? throw new NotSupportedException("MyFeature requires a hosting MeterProviderBuilder instance."))
.AddSingleton<TestReader>();
return meterProviderBuilder.AddReader<TestReader>();
}
internal class TestInstrumentation : IDisposable
{
public bool Disposed { get; private set; }
public void Dispose()
{
this.Disposed = true;
}
}
internal class TestReader : MetricReader
{
protected override bool ProcessMetrics(Batch<Metric> metrics, int timeoutMilliseconds)
{
return true;
}
}
}
}

View File

@ -1,4 +1,4 @@
// <copyright file="HostingExtensionsTests.cs" company="OpenTelemetry Authors"> // <copyright file="HostingTracerExtensionTests.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors // Copyright The OpenTelemetry Authors
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
@ -25,7 +25,7 @@ using Xunit;
namespace OpenTelemetry.Extensions.Hosting.Tests namespace OpenTelemetry.Extensions.Hosting.Tests
{ {
public class HostingExtensionsTests public class HostingTracerExtensionTests
{ {
[Fact] [Fact]
public async Task AddOpenTelemetryTracerProviderInstrumentationCreationAndDisposal() public async Task AddOpenTelemetryTracerProviderInstrumentationCreationAndDisposal()

View File

@ -69,7 +69,7 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
}; };
this.meterProvider = Sdk.CreateMeterProviderBuilder() this.meterProvider = Sdk.CreateMeterProviderBuilder()
.AddAspNetCoreInstrumentation() .AddAspNetCoreInstrumentation()
.AddMetricReader(metricReader) .AddReader(metricReader)
.Build(); .Build();
using (var client = this.factory.CreateClient()) using (var client = this.factory.CreateClient())

View File

@ -71,7 +71,7 @@ namespace OpenTelemetry.Instrumentation.Http.Tests
}; };
var meterProvider = Sdk.CreateMeterProviderBuilder() var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddHttpClientInstrumentation() .AddHttpClientInstrumentation()
.AddMetricReader(metricReader) .AddReader(metricReader)
.Build(); .Build();
using (serverLifeTime) using (serverLifeTime)

View File

@ -36,7 +36,7 @@ namespace OpenTelemetry.Metrics.Tests
using var meterProvider = Sdk.CreateMeterProviderBuilder() using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource("InMemoryExporterTests") .AddSource("InMemoryExporterTests")
.AddMetricReader(inMemoryReader) .AddReader(inMemoryReader)
.Build(); .Build();
var counter = meter.CreateCounter<long>("meter"); var counter = meter.CreateCounter<long>("meter");

View File

@ -64,7 +64,7 @@ namespace OpenTelemetry.Metrics.Tests
using var meterProvider = Sdk.CreateMeterProviderBuilder() using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource("TestDuplicateMetricName1") .AddSource("TestDuplicateMetricName1")
.AddSource("TestDuplicateMetricName2") .AddSource("TestDuplicateMetricName2")
.AddMetricReader(metricReader) .AddReader(metricReader)
.Build(); .Build();
// Expecting one metric stream. // Expecting one metric stream.
@ -117,7 +117,7 @@ namespace OpenTelemetry.Metrics.Tests
var counterLong = meter.CreateCounter<long>("mycounter"); var counterLong = meter.CreateCounter<long>("mycounter");
using var meterProvider = Sdk.CreateMeterProviderBuilder() using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource("TestMeter") .AddSource("TestMeter")
.AddMetricReader(metricReader) .AddReader(metricReader)
.Build(); .Build();
counterLong.Add(10); counterLong.Add(10);
@ -202,7 +202,7 @@ namespace OpenTelemetry.Metrics.Tests
}); });
using var meterProvider = Sdk.CreateMeterProviderBuilder() using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource(meterName) .AddSource(meterName)
.AddMetricReader(metricReader) .AddReader(metricReader)
.Build(); .Build();
metricReader.Collect(); metricReader.Collect();
@ -262,7 +262,7 @@ namespace OpenTelemetry.Metrics.Tests
var counterLong = meter.CreateCounter<long>("mycounterCapTest"); var counterLong = meter.CreateCounter<long>("mycounterCapTest");
using var meterProvider = Sdk.CreateMeterProviderBuilder() using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource("TestPointCapMeter") .AddSource("TestPointCapMeter")
.AddMetricReader(metricReader) .AddReader(metricReader)
.Build(); .Build();
// Make one Add with no tags. // Make one Add with no tags.
@ -314,7 +314,7 @@ namespace OpenTelemetry.Metrics.Tests
var counterLong = meter.CreateCounter<long>("mycounter"); var counterLong = meter.CreateCounter<long>("mycounter");
using var meterProvider = Sdk.CreateMeterProviderBuilder() using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource("TestLongCounterMeter") .AddSource("TestLongCounterMeter")
.AddMetricReader(metricReader) .AddReader(metricReader)
.Build(); .Build();
// setup args to threads. // setup args to threads.
@ -383,7 +383,7 @@ namespace OpenTelemetry.Metrics.Tests
var counterDouble = meter.CreateCounter<double>("mycounter"); var counterDouble = meter.CreateCounter<double>("mycounter");
using var meterProvider = Sdk.CreateMeterProviderBuilder() using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddSource("TestDoubleCounterMeter") .AddSource("TestDoubleCounterMeter")
.AddMetricReader(metricReader) .AddReader(metricReader)
.Build(); .Build();
// setup args to threads. // setup args to threads.

View File

@ -44,7 +44,7 @@ namespace OpenTelemetry.Metrics.Tests
var reader = new BaseExportingMetricReader(exporter); var reader = new BaseExportingMetricReader(exporter);
using var meterProvider = Sdk.CreateMeterProviderBuilder() using var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddMetricReader(reader) .AddReader(reader)
.Build(); .Build();
switch (mode) switch (mode)