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 .NET 8 IMetricsBuilder API #4958

Merged
merged 53 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
9331203
Implement an IMetricsListener over OpenTelemetry SDK.
CodeBlanch Jul 28, 2023
b70f529
Tweak.
CodeBlanch Jul 28, 2023
e411e22
Tweak.
CodeBlanch Jul 28, 2023
dc4f60e
Merge from main.
CodeBlanch Oct 9, 2023
6db3bcb
Manually fix merge conflicts and update API to released versions.
CodeBlanch Oct 9, 2023
680c506
Package updates.
CodeBlanch Oct 9, 2023
496002f
Ceremony.
CodeBlanch Oct 10, 2023
3a03fa3
Fixed dropped metrics in IMetricsBuilder when composite reader is used.
CodeBlanch Oct 14, 2023
558df37
Rename API and add todo.
CodeBlanch Oct 16, 2023
32a0388
Define SDK AddOpenTelemetryLogging extension.
CodeBlanch Oct 16, 2023
25e7ef9
Bug fixes.
CodeBlanch Oct 17, 2023
5ea6211
Merge from main.
CodeBlanch Oct 17, 2023
5d645eb
Merge from main.
CodeBlanch Oct 19, 2023
82b384d
Port public api updates.
CodeBlanch Oct 19, 2023
f4c254a
Merge branch 'main' into poc/metricsbuilder
CodeBlanch Oct 24, 2023
995d2c4
Merge branch 'main' into poc/metricsbuilder
CodeBlanch Nov 1, 2023
9c87e60
Code review.
CodeBlanch Nov 1, 2023
3fd9045
Warning cleanup.
CodeBlanch Nov 1, 2023
c507377
Test coverage.
CodeBlanch Nov 6, 2023
fe10eb6
Merge from main.
CodeBlanch Nov 6, 2023
24322e8
Merge fixes.
CodeBlanch Nov 6, 2023
3fe5702
Test coverage for the UseOpenTelemetry logging extension.
CodeBlanch Nov 7, 2023
bfec275
Ignore external subscriptions if AddMeter is also called.
CodeBlanch Nov 9, 2023
c728717
Verify hot reload of metrics via IConfiguration works and doesn't pro…
CodeBlanch Nov 10, 2023
934e75b
Switch WithLogging and WithMetrics to turn on hosting features.
CodeBlanch Nov 13, 2023
467008e
Make IMetricsBuilder.UseOpenTelemetry an experimental API.
CodeBlanch Nov 13, 2023
f22cdf8
XML comment tweaks.
CodeBlanch Nov 13, 2023
f5b2e6f
Test fix.
CodeBlanch Nov 13, 2023
ec19a81
Warning cleanup.
CodeBlanch Nov 13, 2023
01114b3
Update WithMetrics XML comments to capture the IMetricsListener behav…
CodeBlanch Nov 14, 2023
4012f83
Make IMetricsBuilder.UseOpenTelemetry internal.
CodeBlanch Nov 14, 2023
cd393f7
Remove logging changes.
CodeBlanch Nov 14, 2023
1500698
Remove more logging changes.
CodeBlanch Nov 14, 2023
c2cb6be
Merge from main.
CodeBlanch Nov 15, 2023
0b623e9
A few misc changes and fixes.
CodeBlanch Nov 15, 2023
9831c9f
Merge from main.
CodeBlanch Nov 16, 2023
6905583
Refactor a little bit of duplicated code.
CodeBlanch Nov 16, 2023
cc195ba
Code review.
CodeBlanch Nov 17, 2023
d993289
Code review.
CodeBlanch Nov 17, 2023
0862f7f
Code review.
CodeBlanch Nov 17, 2023
f73d309
Code review and race condition cleanup.
CodeBlanch Nov 17, 2023
da5554b
Tweak.
CodeBlanch Nov 17, 2023
f748914
Reuse metric instances when an instrument is reactivated.
CodeBlanch Nov 18, 2023
7685521
Expand TODOs.
CodeBlanch Nov 18, 2023
8c4c9d2
Merge branch 'main' into poc/metricsbuilder
CodeBlanch Nov 20, 2023
54da2d9
Lower reference to Microsoft.Extensions.Diagnostics.Abstractions.
CodeBlanch Nov 20, 2023
97f3bf5
Warning cleanup.
CodeBlanch Nov 20, 2023
abbcbb1
Revert logging options ordering change.
CodeBlanch Nov 21, 2023
7744d5e
Code review.
CodeBlanch Nov 21, 2023
9ab7c58
CHANGELOG update.
CodeBlanch Nov 21, 2023
f2fce4a
Test tweak.
CodeBlanch Nov 21, 2023
152220c
Merge remote-tracking branch 'upstream/main' into poc/metricsbuilder
CodeBlanch Nov 21, 2023
1c264e8
Clean up nits.
CodeBlanch Nov 21, 2023
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
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics" Version="8.0.0" />
utpilla marked this conversation as resolved.
Show resolved Hide resolved
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Configuration" Version="8.0.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// <copyright file="OpenTelemetryMetricsListener.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.Diagnostics;
using System.Diagnostics.Metrics;
using Microsoft.Extensions.Diagnostics.Metrics;

namespace OpenTelemetry.Metrics;

internal sealed class OpenTelemetryMetricsListener : IMetricsListener, IDisposable
{
private readonly MeterProviderSdk meterProviderSdk;
private IObservableInstrumentsSource? observableInstrumentsSource;

public OpenTelemetryMetricsListener(MeterProvider meterProvider)
{
var meterProviderSdk = meterProvider as MeterProviderSdk;

Debug.Assert(meterProviderSdk != null, "meterProvider was not MeterProviderSdk");

this.meterProviderSdk = meterProviderSdk!;

this.meterProviderSdk.OnCollectObservableInstruments += this.OnCollectObservableInstruments;
}

public string Name => "OpenTelemetry";

public void Dispose()
{
this.meterProviderSdk.OnCollectObservableInstruments -= this.OnCollectObservableInstruments;
}

public MeasurementHandlers GetMeasurementHandlers()
{
return new MeasurementHandlers()
{
ByteHandler = (instrument, value, tags, state)
=> this.MeasurementRecordedLong(instrument, value, tags, state),
ShortHandler = (instrument, value, tags, state)
=> this.MeasurementRecordedLong(instrument, value, tags, state),
IntHandler = (instrument, value, tags, state)
=> this.MeasurementRecordedLong(instrument, value, tags, state),
LongHandler = this.MeasurementRecordedLong,
FloatHandler = (instrument, value, tags, state)
=> this.MeasurementRecordedDouble(instrument, value, tags, state),
DoubleHandler = this.MeasurementRecordedDouble,
};
}

public bool InstrumentPublished(Instrument instrument, out object? userState)
{
userState = this.meterProviderSdk.InstrumentPublished(instrument, listeningIsManagedExternally: true);
return userState != null;
}

public void MeasurementsCompleted(Instrument instrument, object? userState)
{
var meterProvider = this.meterProviderSdk;

if (meterProvider.ViewCount > 0)
{
meterProvider.MeasurementsCompleted(instrument, userState);
}
else
{
meterProvider.MeasurementsCompletedSingleStream(instrument, userState);
}
}

public void Initialize(IObservableInstrumentsSource source)
{
this.observableInstrumentsSource = source;
}

private void OnCollectObservableInstruments()
{
this.observableInstrumentsSource?.RecordObservableInstruments();
}

private void MeasurementRecordedDouble(Instrument instrument, double value, ReadOnlySpan<KeyValuePair<string, object?>> tagsRos, object? userState)
{
var meterProvider = this.meterProviderSdk;

if (meterProvider.ViewCount > 0)
{
meterProvider.MeasurementRecordedDouble(instrument, value, tagsRos, userState);
}
else
{
meterProvider.MeasurementRecordedDoubleSingleStream(instrument, value, tagsRos, userState);
}
}

private void MeasurementRecordedLong(Instrument instrument, long value, ReadOnlySpan<KeyValuePair<string, object?>> tagsRos, object? userState)
{
var meterProvider = this.meterProviderSdk;

if (meterProvider.ViewCount > 0)
{
meterProvider.MeasurementRecordedLong(instrument, value, tagsRos, userState);
}
else
{
meterProvider.MeasurementRecordedLongSingleStream(instrument, value, tagsRos, userState);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Diagnostics" />
</ItemGroup>

<ItemGroup>
Expand Down
25 changes: 15 additions & 10 deletions src/OpenTelemetry.Extensions.Hosting/OpenTelemetryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// </copyright>

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.Metrics;
using OpenTelemetry.Internal;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
Expand Down Expand Up @@ -61,13 +62,13 @@ public OpenTelemetryBuilder ConfigureResource(
Guard.ThrowIfNull(configure);

this.Services.ConfigureOpenTelemetryMeterProvider(
(sp, builder) => builder.ConfigureResource(configure));
builder => builder.ConfigureResource(configure));
utpilla marked this conversation as resolved.
Show resolved Hide resolved

this.Services.ConfigureOpenTelemetryTracerProvider(
(sp, builder) => builder.ConfigureResource(configure));
builder => builder.ConfigureResource(configure));

this.Services.ConfigureOpenTelemetryLoggerProvider(
(sp, builder) => builder.ConfigureResource(configure));
builder => builder.ConfigureResource(configure));

return this;
}
Expand All @@ -76,9 +77,16 @@ public OpenTelemetryBuilder ConfigureResource(
/// Adds metric services into the builder.
/// </summary>
/// <remarks>
/// Note: This is safe to be called multiple times and by library authors.
/// Notes:
/// <list type="bullet">
/// <item>This is safe to be called multiple times and by library authors.
/// Only a single <see cref="MeterProvider"/> will be created for a given
/// <see cref="IServiceCollection"/>.
/// <see cref="IServiceCollection"/>.</item>
/// <item>This method automatically calls <see
utpilla marked this conversation as resolved.
Show resolved Hide resolved
/// cref="MetricsServiceExtensions.AddMetrics(IServiceCollection)"/> and
/// registers an <see cref="IMetricsListener"/> named 'OpenTelemetry' into
/// the <see cref="IServiceCollection"/>.</item>
/// </list>
/// </remarks>
/// <returns>The supplied <see cref="OpenTelemetryBuilder"/> for chaining
/// calls.</returns>
Expand All @@ -95,11 +103,8 @@ public OpenTelemetryBuilder WithMetrics()
/// calls.</returns>
public OpenTelemetryBuilder WithMetrics(Action<MeterProviderBuilder> configure)
{
Guard.ThrowIfNull(configure);

var builder = new MeterProviderBuilderBase(this.Services);

configure(builder);
this.Services.AddMetrics(
builder => builder.UseOpenTelemetry(configure));

return this;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// <copyright file="OpenTelemetryMetricsBuilderExtensions.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 Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using OpenTelemetry.Internal;
using OpenTelemetry.Metrics;

namespace Microsoft.Extensions.Diagnostics.Metrics;

/// <summary>
/// Contains extension methods for registering OpenTelemetry metrics with an
/// <see cref="IMetricsBuilder"/> instance.
/// </summary>
internal static class OpenTelemetryMetricsBuilderExtensions
{
/// <summary>
/// Adds an OpenTelemetry <see cref="IMetricsListener"/> named 'OpenTelemetry' to the <see cref="IMetricsBuilder"/>.
/// </summary>
/// <remarks>
/// Note: This is safe to be called multiple times and by library authors.
utpilla marked this conversation as resolved.
Show resolved Hide resolved
/// Only a single <see cref="MeterProvider"/> will be created for a given
/// <see cref="IServiceCollection"/>.
/// </remarks>
/// <param name="metricsBuilder"><see cref="IMetricsBuilder"/>.</param>
/// <returns>The supplied <see cref="IMetricsBuilder"/> for chaining
/// calls.</returns>
public static IMetricsBuilder UseOpenTelemetry(
this IMetricsBuilder metricsBuilder)
=> UseOpenTelemetry(metricsBuilder, b => { });

/// <summary>
/// Adds an OpenTelemetry <see cref="IMetricsListener"/> named 'OpenTelemetry' to the <see cref="IMetricsBuilder"/>.
/// </summary>
/// <remarks><inheritdoc cref="UseOpenTelemetry(IMetricsBuilder)" path="/remarks"/></remarks>
/// <param name="metricsBuilder"><see cref="IMetricsBuilder"/>.</param>
/// <param name="configure"><see cref="MeterProviderBuilder"/>
/// configuration callback.</param>
/// <returns>The supplied <see cref="IMetricsBuilder"/> for chaining
/// calls.</returns>
public static IMetricsBuilder UseOpenTelemetry(
this IMetricsBuilder metricsBuilder,
Action<MeterProviderBuilder> configure)
{
Guard.ThrowIfNull(metricsBuilder);
Guard.ThrowIfNull(configure);

var builder = new MeterProviderBuilderBase(metricsBuilder.Services);

metricsBuilder.Services.TryAddEnumerable(
ServiceDescriptor.Singleton<IMetricsListener, OpenTelemetryMetricsListener>());

configure(builder);

return metricsBuilder;
}
}
12 changes: 12 additions & 0 deletions src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,18 @@ public void LoggerProcessStateSkipped(string type, string reason)
this.WriteEvent(51, type, reason);
}

[Event(52, Message = "Instrument '{0}', Meter '{1}' has been deactivated.", Level = EventLevel.Informational)]
public void MetricInstrumentDeactivated(string instrumentName, string meterName)
{
this.WriteEvent(52, instrumentName, meterName);
}

[Event(53, Message = "A previously deactivated Instrument '{0}', Meter '{1}' has been reactivated.", Level = EventLevel.Informational)]
public void MetricInstrumentReactivated(string instrumentName, string meterName)
{
this.WriteEvent(53, instrumentName, meterName);
}

#if DEBUG
public class OpenTelemetryEventListener : EventListener
{
Expand Down
52 changes: 30 additions & 22 deletions src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,40 @@ public static class OpenTelemetryLoggingExtensions
/// <returns>The supplied <see cref="ILoggingBuilder"/> for call chaining.</returns>
public static ILoggingBuilder AddOpenTelemetry(
this ILoggingBuilder builder)
=> AddOpenTelemetryInternal(builder, configureBuilder: null, configureOptions: null);

/// <summary>
/// Adds an OpenTelemetry logger named 'OpenTelemetry' to the <see cref="ILoggerFactory"/>.
/// </summary>
/// <remarks><inheritdoc cref="AddOpenTelemetry(ILoggingBuilder)" path="/remarks"/></remarks>
/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
/// <param name="configure">Optional configuration action.</param>
/// <returns>The supplied <see cref="ILoggingBuilder"/> for call chaining.</returns>
public static ILoggingBuilder AddOpenTelemetry(
this ILoggingBuilder builder,
Action<OpenTelemetryLoggerOptions>? configure)
=> AddOpenTelemetryInternal(builder, configureBuilder: null, configureOptions: configure);

private static ILoggingBuilder AddOpenTelemetryInternal(
ILoggingBuilder builder,
Action<LoggerProviderBuilder>? configureBuilder,
utpilla marked this conversation as resolved.
Show resolved Hide resolved
Action<OpenTelemetryLoggerOptions>? configureOptions)
{
Guard.ThrowIfNull(builder);

builder.AddConfiguration();

var services = builder.Services;

// Note: This will bind logger options element (eg "Logging:OpenTelemetry") to OpenTelemetryLoggerOptions
RegisterLoggerProviderOptions(builder.Services);
RegisterLoggerProviderOptions(services);

new LoggerProviderBuilderBase(builder.Services).ConfigureBuilder(
if (configureOptions != null)
utpilla marked this conversation as resolved.
Show resolved Hide resolved
{
services.Configure(configureOptions);
}

var loggingBuilder = new LoggerProviderBuilderBase(services).ConfigureBuilder(
(sp, logging) =>
{
var options = sp.GetRequiredService<IOptionsMonitor<OpenTelemetryLoggerOptions>>().CurrentValue;
Expand All @@ -78,7 +103,9 @@ public static ILoggingBuilder AddOpenTelemetry(
options.Processors.Clear();
});

builder.Services.TryAddEnumerable(
configureBuilder?.Invoke(loggingBuilder);

services.TryAddEnumerable(
ServiceDescriptor.Singleton<ILoggerProvider, OpenTelemetryLoggerProvider>(
sp => new OpenTelemetryLoggerProvider(
sp.GetRequiredService<LoggerProvider>(),
Expand Down Expand Up @@ -107,23 +134,4 @@ static void RegisterLoggerProviderOptions(IServiceCollection services)
LoggerProviderOptions.RegisterProviderOptions<OpenTelemetryLoggerOptions, OpenTelemetryLoggerProvider>(services);
}
}

/// <summary>
/// Adds an OpenTelemetry logger named 'OpenTelemetry' to the <see cref="ILoggerFactory"/>.
/// </summary>
/// <remarks><inheritdoc cref="AddOpenTelemetry(ILoggingBuilder)" path="/remarks"/></remarks>
/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
/// <param name="configure">Optional configuration action.</param>
/// <returns>The supplied <see cref="ILoggingBuilder"/> for call chaining.</returns>
public static ILoggingBuilder AddOpenTelemetry(
this ILoggingBuilder builder,
Action<OpenTelemetryLoggerOptions>? configure)
{
if (configure != null)
{
builder.Services.Configure(configure);
}

return AddOpenTelemetry(builder);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ public static MeterProviderBuilder SetMaxMetricPointsPerMetricStream(this MeterP
/// <returns>The supplied <see cref="MeterProviderBuilder"/> for chaining.</returns>
public static MeterProviderBuilder SetResourceBuilder(this MeterProviderBuilder meterProviderBuilder, ResourceBuilder resourceBuilder)
{
Guard.ThrowIfNull(resourceBuilder);

meterProviderBuilder.ConfigureBuilder((sp, builder) =>
{
if (builder is MeterProviderBuilderSdk meterProviderBuilderSdk)
Expand All @@ -297,6 +299,8 @@ public static MeterProviderBuilder SetResourceBuilder(this MeterProviderBuilder
/// <returns>The supplied <see cref="MeterProviderBuilder"/> for chaining.</returns>
public static MeterProviderBuilder ConfigureResource(this MeterProviderBuilder meterProviderBuilder, Action<ResourceBuilder> configure)
{
Guard.ThrowIfNull(configure);

meterProviderBuilder.ConfigureBuilder((sp, builder) =>
{
if (builder is MeterProviderBuilderSdk meterProviderBuilderSdk)
Expand Down
Loading