[sdk-metrics] ExemplarFilter updates to match latest specification (#5404)
This commit is contained in:
parent
42ecd73bd0
commit
b754b13cdb
|
|
@ -412,26 +412,23 @@ exemplars.
|
|||
|
||||
#### ExemplarFilter
|
||||
|
||||
`ExemplarFilter` determines which measurements are eligible to become an
|
||||
Exemplar. i.e. `ExemplarFilter` determines which measurements are offered to
|
||||
`ExemplarReservoir`, which makes the final decision about whether the offered
|
||||
measurement gets stored as an exemplar. They can be used to control the noise
|
||||
and overhead associated with Exemplar collection.
|
||||
`ExemplarFilter` determines which measurements are offered to the configured
|
||||
`ExemplarReservoir`, which makes the final decision about whether or not the
|
||||
offered measurement gets recorded as an `Exemplar`. Generally `ExemplarFilter`
|
||||
is a mechanism to control the overhead associated with `Exemplar` offering.
|
||||
|
||||
OpenTelemetry SDK comes with the following Filters:
|
||||
OpenTelemetry SDK comes with the following `ExemplarFilters` (defined on
|
||||
`ExemplarFilterType`):
|
||||
|
||||
* `AlwaysOnExemplarFilter` - makes all measurements eligible for being an Exemplar.
|
||||
* `AlwaysOffExemplarFilter` - makes no measurements eligible for being an
|
||||
Exemplar. Using this is as good as turning off Exemplar feature, and is the current
|
||||
* `AlwaysOff`: Makes no measurements eligible for becoming an `Exemplar`. Using
|
||||
this is as good as turning off the `Exemplar` feature and is the current
|
||||
default.
|
||||
* `TraceBasedExemplarFilter` - makes those measurements eligible for being an
|
||||
Exemplar, which are recorded in the context of a sampled parent `Activity`
|
||||
(span).
|
||||
* `AlwaysOn`: Makes all measurements eligible for becoming an `Exemplar`.
|
||||
* `TraceBased`: Makes those measurements eligible for becoming an `Exemplar`
|
||||
which are recorded in the context of a sampled `Activity` (span).
|
||||
|
||||
`SetExemplarFilter` method on `MeterProviderBuilder` can be used to set the
|
||||
desired `ExemplarFilter`.
|
||||
|
||||
The snippet below shows how to set `ExemplarFilter`.
|
||||
The `SetExemplarFilter` extension method on `MeterProviderBuilder` can be used
|
||||
to set the desired `ExemplarFilterType` and enable `Exemplar` collection:
|
||||
|
||||
```csharp
|
||||
using OpenTelemetry;
|
||||
|
|
@ -439,31 +436,14 @@ using OpenTelemetry.Metrics;
|
|||
|
||||
using var meterProvider = Sdk.CreateMeterProviderBuilder()
|
||||
// rest of config not shown
|
||||
.SetExemplarFilter(new TraceBasedExemplarFilter())
|
||||
.SetExemplarFilter(ExemplarFilterType.TraceBased)
|
||||
.Build();
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> As of today, there is no separate toggle for enable/disable Exemplar feature.
|
||||
Exemplars can be disabled by setting filter as `AlwaysOffExemplarFilter`, which
|
||||
is also the default (i.e Exemplar feature is disabled by default). Users can
|
||||
enable the feature by setting filter to anything other than
|
||||
`AlwaysOffExemplarFilter`. For example: `.SetExemplarFilter(new TraceBasedExemplarFilter())`.
|
||||
|
||||
If the built-in `ExemplarFilter`s are not meeting the needs, one may author
|
||||
custom `ExemplarFilter` as shown
|
||||
[here](../extending-the-sdk/README.md#exemplarfilter). A custom filter, which
|
||||
eliminates all un-interesting measurements from becoming Exemplar is a
|
||||
recommended way to control performance overhead associated with collecting
|
||||
Exemplars. See
|
||||
[benchmark](../../../test/Benchmarks/Metrics/ExemplarBenchmarks.cs) to see how
|
||||
much impact can `ExemplarFilter` have on performance.
|
||||
|
||||
#### ExemplarReservoir
|
||||
|
||||
`ExemplarReservoir` receives the measurements sampled in by the `ExemplarFilter`
|
||||
and is responsible for storing Exemplars. `ExemplarReservoir` ultimately decides
|
||||
which measurements get stored as exemplars. The following are the default
|
||||
`ExemplarReservoir` receives the measurements sampled by the `ExemplarFilter`
|
||||
and is responsible for recording `Exemplar`s. The following are the default
|
||||
reservoirs:
|
||||
|
||||
* `AlignedHistogramBucketExemplarReservoir` is the default reservoir used for
|
||||
|
|
@ -479,7 +459,7 @@ size (currently defaulting to 1) determines the maximum number of exemplars
|
|||
stored.
|
||||
|
||||
> [!NOTE]
|
||||
> Currently there is no ability to change or configure Reservoir.
|
||||
> Currently there is no ability to change or configure `ExemplarReservoir`.
|
||||
|
||||
### Instrumentation
|
||||
|
||||
|
|
|
|||
|
|
@ -74,44 +74,7 @@ Not supported.
|
|||
|
||||
## ExemplarFilter
|
||||
|
||||
OpenTelemetry .NET SDK has provided the following built-in `ExemplarFilter`s:
|
||||
|
||||
* [AlwaysOnExemplarFilter](../../../src/OpenTelemetry/Metrics/Exemplar/AlwaysOnExemplarFilter.cs)
|
||||
* [AlwaysOffExemplarFilter](../../../src/OpenTelemetry/Metrics/Exemplar/AlwaysOffExemplarFilter.cs)
|
||||
* [TraceBasedExemplarFilter](../../../src/OpenTelemetry/Metrics/Exemplar/TraceBasedExemplarFilter.cs)
|
||||
|
||||
Custom exemplar filters can be implemented to achieve filtering based on other criterion:
|
||||
|
||||
* `ExemplarFilter` should derive from `OpenTelemetry.ExemplarFilter` (which
|
||||
belongs to the [OpenTelemetry](../../../src/OpenTelemetry/README.md) package)
|
||||
and implement the `ShouldSample` method.
|
||||
|
||||
One example is a filter, which filters all measurements of value lower
|
||||
than given threshold is given below. Such a filter prevents any measurements
|
||||
below the given threshold from ever becoming a `Exemplar`. Such filters could
|
||||
also incorporate the `TraceBasedExemplarFilter` condition as well, as storing
|
||||
exemplars for non-sampled traces may be undesired.
|
||||
|
||||
```csharp
|
||||
public sealed class HighValueFilter : ExemplarFilter
|
||||
{
|
||||
private readonly double maxValue;
|
||||
|
||||
public HighValueFilter(double maxValue)
|
||||
{
|
||||
this.maxValue = maxValue;
|
||||
}
|
||||
public override bool ShouldSample(long value, ReadOnlySpan<KeyValuePair<string, object>> tags)
|
||||
{
|
||||
return Activity.Current?.Recorded && value > this.maxValue;
|
||||
}
|
||||
|
||||
public override bool ShouldSample(double value, ReadOnlySpan<KeyValuePair<string, object>> tags)
|
||||
{
|
||||
return Activity.Current?.Recorded && value > this.maxValue;
|
||||
}
|
||||
}
|
||||
```
|
||||
Not supported.
|
||||
|
||||
## ExemplarReservoir
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ appBuilder.Services.AddOpenTelemetry()
|
|||
builder
|
||||
.AddMeter(Instrumentation.MeterName)
|
||||
#if EXPOSE_EXPERIMENTAL_FEATURES
|
||||
.SetExemplarFilter(new TraceBasedExemplarFilter())
|
||||
.SetExemplarFilter(ExemplarFilterType.TraceBased)
|
||||
#endif
|
||||
.AddRuntimeInstrumentation()
|
||||
.AddHttpClientInstrumentation()
|
||||
|
|
|
|||
|
|
@ -5,10 +5,6 @@ OpenTelemetry.Logs.LogRecord.Severity.get -> OpenTelemetry.Logs.LogRecordSeverit
|
|||
OpenTelemetry.Logs.LogRecord.Severity.set -> void
|
||||
OpenTelemetry.Logs.LogRecord.SeverityText.get -> string?
|
||||
OpenTelemetry.Logs.LogRecord.SeverityText.set -> void
|
||||
OpenTelemetry.Metrics.AlwaysOffExemplarFilter
|
||||
OpenTelemetry.Metrics.AlwaysOffExemplarFilter.AlwaysOffExemplarFilter() -> void
|
||||
OpenTelemetry.Metrics.AlwaysOnExemplarFilter
|
||||
OpenTelemetry.Metrics.AlwaysOnExemplarFilter.AlwaysOnExemplarFilter() -> void
|
||||
OpenTelemetry.Metrics.Exemplar
|
||||
OpenTelemetry.Metrics.Exemplar.DoubleValue.get -> double
|
||||
OpenTelemetry.Metrics.Exemplar.Exemplar() -> void
|
||||
|
|
@ -17,8 +13,10 @@ OpenTelemetry.Metrics.Exemplar.LongValue.get -> long
|
|||
OpenTelemetry.Metrics.Exemplar.SpanId.get -> System.Diagnostics.ActivitySpanId
|
||||
OpenTelemetry.Metrics.Exemplar.Timestamp.get -> System.DateTimeOffset
|
||||
OpenTelemetry.Metrics.Exemplar.TraceId.get -> System.Diagnostics.ActivityTraceId
|
||||
OpenTelemetry.Metrics.ExemplarFilter
|
||||
OpenTelemetry.Metrics.ExemplarFilter.ExemplarFilter() -> void
|
||||
OpenTelemetry.Metrics.ExemplarFilterType
|
||||
OpenTelemetry.Metrics.ExemplarFilterType.AlwaysOff = 0 -> OpenTelemetry.Metrics.ExemplarFilterType
|
||||
OpenTelemetry.Metrics.ExemplarFilterType.AlwaysOn = 1 -> OpenTelemetry.Metrics.ExemplarFilterType
|
||||
OpenTelemetry.Metrics.ExemplarFilterType.TraceBased = 2 -> OpenTelemetry.Metrics.ExemplarFilterType
|
||||
OpenTelemetry.Metrics.ExemplarMeasurement<T>
|
||||
OpenTelemetry.Metrics.ExemplarMeasurement<T>.ExemplarMeasurement() -> void
|
||||
OpenTelemetry.Metrics.ExemplarMeasurement<T>.Tags.get -> System.ReadOnlySpan<System.Collections.Generic.KeyValuePair<string!, object?>>
|
||||
|
|
@ -33,8 +31,6 @@ OpenTelemetry.Metrics.ReadOnlyExemplarCollection.Enumerator.Enumerator() -> void
|
|||
OpenTelemetry.Metrics.ReadOnlyExemplarCollection.Enumerator.MoveNext() -> bool
|
||||
OpenTelemetry.Metrics.ReadOnlyExemplarCollection.GetEnumerator() -> OpenTelemetry.Metrics.ReadOnlyExemplarCollection.Enumerator
|
||||
OpenTelemetry.Metrics.ReadOnlyExemplarCollection.ReadOnlyExemplarCollection() -> void
|
||||
OpenTelemetry.Metrics.TraceBasedExemplarFilter
|
||||
OpenTelemetry.Metrics.TraceBasedExemplarFilter.TraceBasedExemplarFilter() -> void
|
||||
OpenTelemetry.ReadOnlyFilteredTagCollection
|
||||
OpenTelemetry.ReadOnlyFilteredTagCollection.Enumerator
|
||||
OpenTelemetry.ReadOnlyFilteredTagCollection.Enumerator.Current.get -> System.Collections.Generic.KeyValuePair<string!, object?>
|
||||
|
|
@ -51,19 +47,11 @@ static OpenTelemetry.Logs.LoggerProviderBuilderExtensions.SetResourceBuilder(thi
|
|||
static OpenTelemetry.Logs.LoggerProviderExtensions.AddProcessor(this OpenTelemetry.Logs.LoggerProvider! provider, OpenTelemetry.BaseProcessor<OpenTelemetry.Logs.LogRecord!>! processor) -> OpenTelemetry.Logs.LoggerProvider!
|
||||
static OpenTelemetry.Logs.LoggerProviderExtensions.ForceFlush(this OpenTelemetry.Logs.LoggerProvider! provider, int timeoutMilliseconds = -1) -> bool
|
||||
static OpenTelemetry.Logs.LoggerProviderExtensions.Shutdown(this OpenTelemetry.Logs.LoggerProvider! provider, int timeoutMilliseconds = -1) -> bool
|
||||
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.SetExemplarFilter(this OpenTelemetry.Metrics.MeterProviderBuilder! meterProviderBuilder, OpenTelemetry.Metrics.ExemplarFilter! exemplarFilter) -> OpenTelemetry.Metrics.MeterProviderBuilder!
|
||||
static OpenTelemetry.Metrics.MeterProviderBuilderExtensions.SetExemplarFilter(this OpenTelemetry.Metrics.MeterProviderBuilder! meterProviderBuilder, OpenTelemetry.Metrics.ExemplarFilterType exemplarFilter = OpenTelemetry.Metrics.ExemplarFilterType.TraceBased) -> OpenTelemetry.Metrics.MeterProviderBuilder!
|
||||
static OpenTelemetry.OpenTelemetryBuilderSdkExtensions.WithLogging(this OpenTelemetry.IOpenTelemetryBuilder! builder) -> OpenTelemetry.IOpenTelemetryBuilder!
|
||||
static OpenTelemetry.OpenTelemetryBuilderSdkExtensions.WithLogging(this OpenTelemetry.IOpenTelemetryBuilder! builder, System.Action<OpenTelemetry.Logs.LoggerProviderBuilder!>! configure) -> OpenTelemetry.IOpenTelemetryBuilder!
|
||||
static OpenTelemetry.OpenTelemetryBuilderSdkExtensions.WithLogging(this OpenTelemetry.IOpenTelemetryBuilder! builder, System.Action<OpenTelemetry.Logs.LoggerProviderBuilder!>? configureBuilder, System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>? configureOptions) -> OpenTelemetry.IOpenTelemetryBuilder!
|
||||
static OpenTelemetry.Sdk.CreateLoggerProviderBuilder() -> OpenTelemetry.Logs.LoggerProviderBuilder!
|
||||
abstract OpenTelemetry.Metrics.ExemplarFilter.ShouldSample(double value, System.ReadOnlySpan<System.Collections.Generic.KeyValuePair<string!, object?>> tags) -> bool
|
||||
abstract OpenTelemetry.Metrics.ExemplarFilter.ShouldSample(long value, System.ReadOnlySpan<System.Collections.Generic.KeyValuePair<string!, object?>> tags) -> bool
|
||||
override OpenTelemetry.Metrics.AlwaysOffExemplarFilter.ShouldSample(double value, System.ReadOnlySpan<System.Collections.Generic.KeyValuePair<string!, object?>> tags) -> bool
|
||||
override OpenTelemetry.Metrics.AlwaysOffExemplarFilter.ShouldSample(long value, System.ReadOnlySpan<System.Collections.Generic.KeyValuePair<string!, object?>> tags) -> bool
|
||||
override OpenTelemetry.Metrics.AlwaysOnExemplarFilter.ShouldSample(double value, System.ReadOnlySpan<System.Collections.Generic.KeyValuePair<string!, object?>> tags) -> bool
|
||||
override OpenTelemetry.Metrics.AlwaysOnExemplarFilter.ShouldSample(long value, System.ReadOnlySpan<System.Collections.Generic.KeyValuePair<string!, object?>> tags) -> bool
|
||||
override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(double value, System.ReadOnlySpan<System.Collections.Generic.KeyValuePair<string!, object?>> tags) -> bool
|
||||
override OpenTelemetry.Metrics.TraceBasedExemplarFilter.ShouldSample(long value, System.ReadOnlySpan<System.Collections.Generic.KeyValuePair<string!, object?>> tags) -> bool
|
||||
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.UseOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder) -> Microsoft.Extensions.Logging.ILoggingBuilder!
|
||||
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.UseOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action<OpenTelemetry.Logs.LoggerProviderBuilder!>! configure) -> Microsoft.Extensions.Logging.ILoggingBuilder!
|
||||
static Microsoft.Extensions.Logging.OpenTelemetryLoggingExtensions.UseOpenTelemetry(this Microsoft.Extensions.Logging.ILoggingBuilder! builder, System.Action<OpenTelemetry.Logs.LoggerProviderBuilder!>? configureBuilder, System.Action<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>? configureOptions) -> Microsoft.Extensions.Logging.ILoggingBuilder!
|
||||
|
|
|
|||
|
|
@ -55,6 +55,16 @@
|
|||
API.
|
||||
([#5396](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5396))
|
||||
|
||||
* **Experimental (pre-release builds only):** Removed the `ExemplarFilter`,
|
||||
`AlwaysOffExemplarFilter`, `AlwaysOnExemplarFilter`, and
|
||||
`TraceBasedExemplarFilter` APIs. The `MeterProviderBuilder.SetExemplarFilter`
|
||||
extension method now accepts an `ExemplarFilterType` enumeration (which
|
||||
contains definitions for the supported filter types `AlwaysOff`, `AlwaysOn`,
|
||||
and `TraceBased`) instead of an `ExemplarFilter` instance. This was done in
|
||||
response to changes made to the [OpenTelemetry Metrics SDK
|
||||
Specification](https://github.com/open-telemetry/opentelemetry-specification/pull/3820).
|
||||
([#5404](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5404))
|
||||
|
||||
## 1.7.0
|
||||
|
||||
Released 2023-Dec-08
|
||||
|
|
|
|||
|
|
@ -319,36 +319,54 @@ public static class MeterProviderBuilderExtensions
|
|||
|
||||
#if EXPOSE_EXPERIMENTAL_FEATURES
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExemplarFilter"/> to be used for this provider.
|
||||
/// This is applied to all the metrics from this provider.
|
||||
/// Sets the <see cref="ExemplarFilterType"/> to be used for this provider
|
||||
/// which controls how measurements will be offered to exemplar reservoirs.
|
||||
/// Default provider configuration: <see
|
||||
/// cref="ExemplarFilterType.AlwaysOff"/>.
|
||||
/// </summary>
|
||||
/// <remarks><inheritdoc cref="Exemplar" path="/remarks/para[@experimental-warning='true']"/></remarks>
|
||||
/// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param>
|
||||
/// <param name="exemplarFilter"><see cref="ExemplarFilter"/> ExemplarFilter to use.</param>
|
||||
/// <returns>The supplied <see cref="MeterProviderBuilder"/> for chaining.</returns>
|
||||
/// <remarks>
|
||||
/// <inheritdoc cref="Exemplar"
|
||||
/// path="/remarks/para[@experimental-warning='true']"/>
|
||||
/// <para>Note: Use <see cref="ExemplarFilterType.TraceBased"/> or <see
|
||||
/// cref="ExemplarFilterType.AlwaysOn"/> to enable exemplars.</para>
|
||||
/// <para>Specification: <see
|
||||
/// href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#exemplarfilter"/>.</para>
|
||||
/// </remarks>
|
||||
/// <param name="meterProviderBuilder"><see
|
||||
/// cref="MeterProviderBuilder"/>.</param>
|
||||
/// <param name="exemplarFilter"><see cref="ExemplarFilterType"/> to
|
||||
/// use.</param>
|
||||
/// <returns>The supplied <see cref="MeterProviderBuilder"/> for
|
||||
/// chaining.</returns>
|
||||
#if NET8_0_OR_GREATER
|
||||
[Experimental(DiagnosticDefinitions.ExemplarExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
|
||||
#endif
|
||||
public
|
||||
#else
|
||||
/// <summary>
|
||||
/// Sets the <see cref="ExemplarFilter"/> to be used for this provider.
|
||||
/// This is applied to all the metrics from this provider.
|
||||
/// </summary>
|
||||
/// <param name="meterProviderBuilder"><see cref="MeterProviderBuilder"/>.</param>
|
||||
/// <param name="exemplarFilter"><see cref="ExemplarFilter"/> ExemplarFilter to use.</param>
|
||||
/// <returns>The supplied <see cref="MeterProviderBuilder"/> for chaining.</returns>
|
||||
internal
|
||||
#endif
|
||||
static MeterProviderBuilder SetExemplarFilter(this MeterProviderBuilder meterProviderBuilder, ExemplarFilter exemplarFilter)
|
||||
static MeterProviderBuilder SetExemplarFilter(
|
||||
this MeterProviderBuilder meterProviderBuilder,
|
||||
ExemplarFilterType exemplarFilter = ExemplarFilterType.TraceBased)
|
||||
{
|
||||
Guard.ThrowIfNull(exemplarFilter);
|
||||
|
||||
meterProviderBuilder.ConfigureBuilder((sp, builder) =>
|
||||
{
|
||||
if (builder is MeterProviderBuilderSdk meterProviderBuilderSdk)
|
||||
{
|
||||
meterProviderBuilderSdk.SetExemplarFilter(exemplarFilter);
|
||||
switch (exemplarFilter)
|
||||
{
|
||||
case ExemplarFilterType.AlwaysOn:
|
||||
meterProviderBuilderSdk.SetExemplarFilter(new AlwaysOnExemplarFilter());
|
||||
break;
|
||||
case ExemplarFilterType.AlwaysOff:
|
||||
meterProviderBuilderSdk.SetExemplarFilter(new AlwaysOffExemplarFilter());
|
||||
break;
|
||||
case ExemplarFilterType.TraceBased:
|
||||
meterProviderBuilderSdk.SetExemplarFilter(new TraceBasedExemplarFilter());
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"SdkExemplarFilter '{exemplarFilter}' is not supported.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,31 +1,17 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using OpenTelemetry.Internal;
|
||||
#endif
|
||||
|
||||
namespace OpenTelemetry.Metrics;
|
||||
|
||||
#if EXPOSE_EXPERIMENTAL_FEATURES
|
||||
/// <summary>
|
||||
/// An <see cref="ExemplarFilter"/> implementation which makes no measurements
|
||||
/// eligible for becoming an <see cref="Exemplar"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <inheritdoc cref="Exemplar" path="/remarks/para[@experimental-warning='true']"/>
|
||||
/// Specification: <see
|
||||
/// href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#alwaysoff"/>.
|
||||
/// </remarks>
|
||||
#if NET8_0_OR_GREATER
|
||||
[Experimental(DiagnosticDefinitions.ExemplarExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
|
||||
#endif
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
sealed class AlwaysOffExemplarFilter : ExemplarFilter
|
||||
internal sealed class AlwaysOffExemplarFilter : ExemplarFilter
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override bool ShouldSample(long value, ReadOnlySpan<KeyValuePair<string, object?>> tags)
|
||||
|
|
|
|||
|
|
@ -1,34 +1,17 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using OpenTelemetry.Internal;
|
||||
#endif
|
||||
|
||||
namespace OpenTelemetry.Metrics;
|
||||
|
||||
#if EXPOSE_EXPERIMENTAL_FEATURES
|
||||
/// <summary>
|
||||
/// An <see cref="ExemplarFilter"/> implementation which makes all measurements
|
||||
/// eligible for becoming an <see cref="Exemplar"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <inheritdoc cref="Exemplar" path="/remarks/para[@experimental-warning='true']"/>
|
||||
/// Specification: <see
|
||||
/// href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#alwayson"/>.
|
||||
/// </remarks>
|
||||
#if NET8_0_OR_GREATER
|
||||
[Experimental(DiagnosticDefinitions.ExemplarExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
|
||||
#endif
|
||||
public
|
||||
#else
|
||||
/// <summary>
|
||||
/// An ExemplarFilter which makes all measurements eligible for being an Exemplar.
|
||||
/// </summary>
|
||||
internal
|
||||
#endif
|
||||
sealed class AlwaysOnExemplarFilter : ExemplarFilter
|
||||
internal sealed class AlwaysOnExemplarFilter : ExemplarFilter
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override bool ShouldSample(long value, ReadOnlySpan<KeyValuePair<string, object?>> tags)
|
||||
|
|
|
|||
|
|
@ -1,30 +1,16 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using OpenTelemetry.Internal;
|
||||
#endif
|
||||
|
||||
namespace OpenTelemetry.Metrics;
|
||||
|
||||
#if EXPOSE_EXPERIMENTAL_FEATURES
|
||||
/// <summary>
|
||||
/// ExemplarFilter base implementation and contract.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <inheritdoc cref="Exemplar" path="/remarks/para[@experimental-warning='true']"/>
|
||||
/// Specification: <see
|
||||
/// href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#exemplarfilter"/>.
|
||||
/// </remarks>
|
||||
#if NET8_0_OR_GREATER
|
||||
[Experimental(DiagnosticDefinitions.ExemplarExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
|
||||
#endif
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
abstract class ExemplarFilter
|
||||
internal abstract class ExemplarFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines if a given measurement is eligible for being
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
using System.Diagnostics;
|
||||
#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using OpenTelemetry.Internal;
|
||||
#endif
|
||||
|
||||
namespace OpenTelemetry.Metrics;
|
||||
|
||||
#if EXPOSE_EXPERIMENTAL_FEATURES
|
||||
/// <summary>
|
||||
/// Defines the supported exemplar filters.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <inheritdoc cref="Exemplar" path="/remarks/para[@experimental-warning='true']"/>
|
||||
/// Specification: <see
|
||||
/// href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#exemplarfilter"/>.
|
||||
/// </remarks>
|
||||
#if NET8_0_OR_GREATER
|
||||
[Experimental(DiagnosticDefinitions.ExemplarExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
|
||||
#endif
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
enum ExemplarFilterType
|
||||
{
|
||||
/// <summary>
|
||||
/// An exemplar filter which makes no measurements eligible for becoming an
|
||||
/// <see cref="Exemplar"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Note: Setting <see cref="AlwaysOff"/> on a meter provider
|
||||
/// effectively disables exemplars.</para>
|
||||
/// <para>Specification: <see
|
||||
/// href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#alwaysoff"/>.</para>
|
||||
/// </remarks>
|
||||
AlwaysOff,
|
||||
|
||||
/// <summary>
|
||||
/// An exemplar filter which makes all measurements eligible for becoming an
|
||||
/// <see cref="Exemplar"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Specification: <see
|
||||
/// href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#alwayson"/>.
|
||||
/// </remarks>
|
||||
AlwaysOn,
|
||||
|
||||
/// <summary>
|
||||
/// An exemplar filter which makes measurements recorded in the context of a
|
||||
/// sampled <see cref="Activity"/> (span) eligible for becoming an <see
|
||||
/// cref="Exemplar"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Specification: <see
|
||||
/// href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#tracebased"/>.
|
||||
/// </remarks>
|
||||
TraceBased,
|
||||
}
|
||||
|
|
@ -1,34 +1,20 @@
|
|||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#if EXPOSE_EXPERIMENTAL_FEATURES && NET8_0_OR_GREATER
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using OpenTelemetry.Internal;
|
||||
#endif
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace OpenTelemetry.Metrics;
|
||||
|
||||
#if EXPOSE_EXPERIMENTAL_FEATURES
|
||||
/// <summary>
|
||||
/// An <see cref="ExemplarFilter"/> implementation which makes measurements
|
||||
/// recorded in the context of a sampled <see cref="Activity"/> (span) eligible
|
||||
/// for becoming an <see cref="Exemplar"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <inheritdoc cref="Exemplar" path="/remarks/para[@experimental-warning='true']"/>
|
||||
/// Specification: <see
|
||||
/// href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#tracebased"/>.
|
||||
/// </remarks>
|
||||
#if NET8_0_OR_GREATER
|
||||
[Experimental(DiagnosticDefinitions.ExemplarExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
|
||||
#endif
|
||||
public
|
||||
#else
|
||||
internal
|
||||
#endif
|
||||
sealed class TraceBasedExemplarFilter : ExemplarFilter
|
||||
internal sealed class TraceBasedExemplarFilter : ExemplarFilter
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override bool ShouldSample(long value, ReadOnlySpan<KeyValuePair<string, object?>> tags)
|
||||
|
|
|
|||
|
|
@ -9,21 +9,31 @@ using OpenTelemetry.Metrics;
|
|||
using OpenTelemetry.Tests;
|
||||
|
||||
/*
|
||||
BenchmarkDotNet v0.13.10, Windows 11 (10.0.23424.1000)
|
||||
Intel Core i7-9700 CPU 3.00GHz, 1 CPU, 8 logical and 8 physical cores
|
||||
.NET SDK 8.0.100
|
||||
[Host] : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
|
||||
DefaultJob : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
|
||||
BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3155/23H2/2023Update/SunValley3)
|
||||
12th Gen Intel Core i9-12900HK, 1 CPU, 20 logical and 14 physical cores
|
||||
.NET SDK 8.0.200
|
||||
[Host] : .NET 8.0.2 (8.0.224.6711), X64 RyuJIT AVX2
|
||||
DefaultJob : .NET 8.0.2 (8.0.224.6711), X64 RyuJIT AVX2
|
||||
|
||||
|
||||
| Method | ExemplarFilter | Mean | Error | StdDev | Allocated |
|
||||
|-------------------------- |--------------- |---------:|--------:|--------:|----------:|
|
||||
| HistogramNoTagReduction | AlwaysOff | 274.2 ns | 2.94 ns | 2.60 ns | - |
|
||||
| HistogramWithTagReduction | AlwaysOff | 241.6 ns | 1.78 ns | 1.58 ns | - |
|
||||
| HistogramNoTagReduction | AlwaysOn | 300.9 ns | 3.10 ns | 2.90 ns | - |
|
||||
| HistogramWithTagReduction | AlwaysOn | 312.9 ns | 4.81 ns | 4.50 ns | - |
|
||||
| HistogramNoTagReduction | HighValueOnly | 262.8 ns | 2.24 ns | 1.99 ns | - |
|
||||
| HistogramWithTagReduction | HighValueOnly | 258.3 ns | 5.12 ns | 5.03 ns | - |
|
||||
| Method | ExemplarConfiguration | Mean | Error | StdDev | Allocated |
|
||||
|-------------------------- |---------------------- |---------:|--------:|--------:|----------:|
|
||||
| HistogramNoTagReduction | AlwaysOff | 174.6 ns | 1.32 ns | 1.24 ns | - |
|
||||
| HistogramWithTagReduction | AlwaysOff | 161.8 ns | 2.63 ns | 2.46 ns | - |
|
||||
| CounterNoTagReduction | AlwaysOff | 141.6 ns | 2.12 ns | 1.77 ns | - |
|
||||
| CounterWithTagReduction | AlwaysOff | 141.7 ns | 2.11 ns | 1.87 ns | - |
|
||||
| HistogramNoTagReduction | AlwaysOn | 201.1 ns | 3.05 ns | 2.86 ns | - |
|
||||
| HistogramWithTagReduction | AlwaysOn | 196.5 ns | 1.91 ns | 1.78 ns | - |
|
||||
| CounterNoTagReduction | AlwaysOn | 149.7 ns | 1.42 ns | 1.33 ns | - |
|
||||
| CounterWithTagReduction | AlwaysOn | 143.5 ns | 2.09 ns | 1.95 ns | - |
|
||||
| HistogramNoTagReduction | TraceBased | 171.9 ns | 2.33 ns | 2.18 ns | - |
|
||||
| HistogramWithTagReduction | TraceBased | 164.9 ns | 2.70 ns | 2.52 ns | - |
|
||||
| CounterNoTagReduction | TraceBased | 148.1 ns | 2.76 ns | 2.58 ns | - |
|
||||
| CounterWithTagReduction | TraceBased | 141.2 ns | 1.43 ns | 1.34 ns | - |
|
||||
| HistogramNoTagReduction | Alway(...)pling [29] | 183.9 ns | 1.49 ns | 1.39 ns | - |
|
||||
| HistogramWithTagReduction | Alway(...)pling [29] | 176.1 ns | 3.35 ns | 3.29 ns | - |
|
||||
| CounterNoTagReduction | Alway(...)pling [29] | 159.3 ns | 3.12 ns | 4.38 ns | - |
|
||||
| CounterWithTagReduction | Alway(...)pling [29] | 158.7 ns | 3.06 ns | 3.65 ns | - |
|
||||
*/
|
||||
|
||||
namespace Benchmarks.Metrics;
|
||||
|
|
@ -32,51 +42,74 @@ public class ExemplarBenchmarks
|
|||
{
|
||||
private static readonly ThreadLocal<Random> ThreadLocalRandom = new(() => new Random());
|
||||
private readonly string[] dimensionValues = new string[] { "DimVal1", "DimVal2", "DimVal3", "DimVal4", "DimVal5", "DimVal6", "DimVal7", "DimVal8", "DimVal9", "DimVal10" };
|
||||
private Histogram<long> histogramWithoutTagReduction;
|
||||
|
||||
private Histogram<long> histogramWithTagReduction;
|
||||
|
||||
private Histogram<double> histogramWithoutTagReduction;
|
||||
private Histogram<double> histogramWithTagReduction;
|
||||
private Counter<long> counterWithoutTagReduction;
|
||||
private Counter<long> counterWithTagReduction;
|
||||
private MeterProvider meterProvider;
|
||||
private Meter meter;
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "Test only.")]
|
||||
public enum ExemplarFilterToUse
|
||||
public enum ExemplarConfigurationType
|
||||
{
|
||||
AlwaysOff,
|
||||
AlwaysOn,
|
||||
HighValueOnly,
|
||||
TraceBased,
|
||||
AlwaysOnWithHighValueSampling,
|
||||
}
|
||||
|
||||
[Params(ExemplarFilterToUse.AlwaysOn, ExemplarFilterToUse.AlwaysOff, ExemplarFilterToUse.HighValueOnly)]
|
||||
public ExemplarFilterToUse ExemplarFilter { get; set; }
|
||||
[Params(ExemplarConfigurationType.AlwaysOn, ExemplarConfigurationType.AlwaysOff, ExemplarConfigurationType.TraceBased, ExemplarConfigurationType.AlwaysOnWithHighValueSampling)]
|
||||
public ExemplarConfigurationType ExemplarConfiguration { get; set; }
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
this.meter = new Meter(Utils.GetCurrentMethodName());
|
||||
this.histogramWithoutTagReduction = this.meter.CreateHistogram<long>("HistogramWithoutTagReduction");
|
||||
this.histogramWithTagReduction = this.meter.CreateHistogram<long>("HistogramWithTagReduction");
|
||||
this.histogramWithoutTagReduction = this.meter.CreateHistogram<double>("HistogramWithoutTagReduction");
|
||||
this.histogramWithTagReduction = this.meter.CreateHistogram<double>("HistogramWithTagReduction");
|
||||
this.counterWithoutTagReduction = this.meter.CreateCounter<long>("CounterWithoutTagReduction");
|
||||
this.counterWithTagReduction = this.meter.CreateCounter<long>("CounterWithTagReduction");
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
ExemplarFilter exemplarFilter = new AlwaysOffExemplarFilter();
|
||||
if (this.ExemplarFilter == ExemplarFilterToUse.AlwaysOn)
|
||||
{
|
||||
exemplarFilter = new AlwaysOnExemplarFilter();
|
||||
}
|
||||
else if (this.ExemplarFilter == ExemplarFilterToUse.HighValueOnly)
|
||||
{
|
||||
exemplarFilter = new HighValueExemplarFilter();
|
||||
}
|
||||
var exemplarFilter = this.ExemplarConfiguration == ExemplarConfigurationType.TraceBased
|
||||
? ExemplarFilterType.TraceBased
|
||||
: this.ExemplarConfiguration != ExemplarConfigurationType.AlwaysOff
|
||||
? ExemplarFilterType.AlwaysOn
|
||||
: ExemplarFilterType.AlwaysOff;
|
||||
|
||||
this.meterProvider = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(this.meter.Name)
|
||||
.SetExemplarFilter(exemplarFilter)
|
||||
.AddView("HistogramWithTagReduction", new MetricStreamConfiguration() { TagKeys = new string[] { "DimName1", "DimName2", "DimName3" } })
|
||||
.AddView(i =>
|
||||
{
|
||||
if (i.Name.Contains("WithTagReduction"))
|
||||
{
|
||||
return new MetricStreamConfiguration()
|
||||
{
|
||||
TagKeys = new string[] { "DimName1", "DimName2", "DimName3" },
|
||||
ExemplarReservoirFactory = CreateExemplarReservoir,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return new MetricStreamConfiguration()
|
||||
{
|
||||
ExemplarReservoirFactory = CreateExemplarReservoir,
|
||||
};
|
||||
}
|
||||
})
|
||||
.AddInMemoryExporter(exportedItems, metricReaderOptions =>
|
||||
{
|
||||
metricReaderOptions.PeriodicExportingMetricReaderOptions.ExportIntervalMilliseconds = 1000;
|
||||
})
|
||||
.Build();
|
||||
|
||||
ExemplarReservoir CreateExemplarReservoir()
|
||||
{
|
||||
return this.ExemplarConfiguration == ExemplarConfigurationType.AlwaysOnWithHighValueSampling
|
||||
? new HighValueExemplarReservoir(800D)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
|
|
@ -99,7 +132,7 @@ public class ExemplarBenchmarks
|
|||
{ "DimName5", this.dimensionValues[random.Next(0, 10)] },
|
||||
};
|
||||
|
||||
this.histogramWithoutTagReduction.Record(random.Next(1000), tags);
|
||||
this.histogramWithoutTagReduction.Record(random.NextDouble() * 1000D, tags);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
|
|
@ -115,19 +148,71 @@ public class ExemplarBenchmarks
|
|||
{ "DimName5", this.dimensionValues[random.Next(0, 10)] },
|
||||
};
|
||||
|
||||
this.histogramWithTagReduction.Record(random.Next(1000), tags);
|
||||
this.histogramWithTagReduction.Record(random.NextDouble() * 1000D, tags);
|
||||
}
|
||||
|
||||
internal class HighValueExemplarFilter : ExemplarFilter
|
||||
[Benchmark]
|
||||
public void CounterNoTagReduction()
|
||||
{
|
||||
public override bool ShouldSample(long value, ReadOnlySpan<KeyValuePair<string, object>> tags)
|
||||
var random = ThreadLocalRandom.Value;
|
||||
var tags = new TagList
|
||||
{
|
||||
return value > 800;
|
||||
{ "DimName1", this.dimensionValues[random.Next(0, 2)] },
|
||||
{ "DimName2", this.dimensionValues[random.Next(0, 2)] },
|
||||
{ "DimName3", this.dimensionValues[random.Next(0, 5)] },
|
||||
{ "DimName4", this.dimensionValues[random.Next(0, 5)] },
|
||||
{ "DimName5", this.dimensionValues[random.Next(0, 10)] },
|
||||
};
|
||||
|
||||
this.counterWithoutTagReduction.Add(random.Next(1000), tags);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void CounterWithTagReduction()
|
||||
{
|
||||
var random = ThreadLocalRandom.Value;
|
||||
var tags = new TagList
|
||||
{
|
||||
{ "DimName1", this.dimensionValues[random.Next(0, 2)] },
|
||||
{ "DimName2", this.dimensionValues[random.Next(0, 2)] },
|
||||
{ "DimName3", this.dimensionValues[random.Next(0, 5)] },
|
||||
{ "DimName4", this.dimensionValues[random.Next(0, 5)] },
|
||||
{ "DimName5", this.dimensionValues[random.Next(0, 10)] },
|
||||
};
|
||||
|
||||
this.counterWithTagReduction.Add(random.Next(1000), tags);
|
||||
}
|
||||
|
||||
private sealed class HighValueExemplarReservoir : FixedSizeExemplarReservoir
|
||||
{
|
||||
private readonly double threshold;
|
||||
private int measurementCount;
|
||||
|
||||
public HighValueExemplarReservoir(double threshold)
|
||||
: base(10)
|
||||
{
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
public override bool ShouldSample(double value, ReadOnlySpan<KeyValuePair<string, object>> tags)
|
||||
public override void Offer(in ExemplarMeasurement<long> measurement)
|
||||
{
|
||||
return value > 800;
|
||||
if (measurement.Value >= this.threshold)
|
||||
{
|
||||
this.UpdateExemplar(this.measurementCount++ % this.Capacity, in measurement);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Offer(in ExemplarMeasurement<double> measurement)
|
||||
{
|
||||
if (measurement.Value >= this.threshold)
|
||||
{
|
||||
this.UpdateExemplar(this.measurementCount++ % this.Capacity, in measurement);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnCollected()
|
||||
{
|
||||
this.measurementCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ public class OtlpMetricsExporterTests : Http2UnencryptedSupportTests
|
|||
using var meter = new Meter(Utils.GetCurrentMethodName());
|
||||
using var provider = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.SetExemplarFilter(enableExemplars ? new AlwaysOnExemplarFilter() : new AlwaysOffExemplarFilter())
|
||||
.SetExemplarFilter(enableExemplars ? ExemplarFilterType.AlwaysOn : ExemplarFilterType.AlwaysOff)
|
||||
.AddInMemoryExporter(metrics)
|
||||
.Build();
|
||||
|
||||
|
|
@ -309,7 +309,7 @@ public class OtlpMetricsExporterTests : Http2UnencryptedSupportTests
|
|||
using var meter = new Meter(Utils.GetCurrentMethodName());
|
||||
using var provider = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.SetExemplarFilter(enableExemplars ? new AlwaysOnExemplarFilter() : new AlwaysOffExemplarFilter())
|
||||
.SetExemplarFilter(enableExemplars ? ExemplarFilterType.AlwaysOn : ExemplarFilterType.AlwaysOff)
|
||||
.AddInMemoryExporter(metrics, metricReaderOptions =>
|
||||
{
|
||||
metricReaderOptions.TemporalityPreference = aggregationTemporality;
|
||||
|
|
@ -406,7 +406,7 @@ public class OtlpMetricsExporterTests : Http2UnencryptedSupportTests
|
|||
using var meter = new Meter(Utils.GetCurrentMethodName());
|
||||
using var provider = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.SetExemplarFilter(enableExemplars ? new AlwaysOnExemplarFilter() : new AlwaysOffExemplarFilter())
|
||||
.SetExemplarFilter(enableExemplars ? ExemplarFilterType.AlwaysOn : ExemplarFilterType.AlwaysOff)
|
||||
.AddInMemoryExporter(metrics, metricReaderOptions =>
|
||||
{
|
||||
metricReaderOptions.TemporalityPreference = aggregationTemporality;
|
||||
|
|
@ -503,7 +503,7 @@ public class OtlpMetricsExporterTests : Http2UnencryptedSupportTests
|
|||
using var meter = new Meter(Utils.GetCurrentMethodName());
|
||||
using var provider = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.SetExemplarFilter(enableExemplars ? new AlwaysOnExemplarFilter() : new AlwaysOffExemplarFilter())
|
||||
.SetExemplarFilter(enableExemplars ? ExemplarFilterType.AlwaysOn : ExemplarFilterType.AlwaysOff)
|
||||
.AddInMemoryExporter(metrics, metricReaderOptions =>
|
||||
{
|
||||
metricReaderOptions.TemporalityPreference = aggregationTemporality;
|
||||
|
|
@ -643,7 +643,7 @@ public class OtlpMetricsExporterTests : Http2UnencryptedSupportTests
|
|||
using var meter = new Meter(Utils.GetCurrentMethodName());
|
||||
using var provider = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.SetExemplarFilter(enableExemplars ? new AlwaysOnExemplarFilter() : new AlwaysOffExemplarFilter())
|
||||
.SetExemplarFilter(enableExemplars ? ExemplarFilterType.AlwaysOn : ExemplarFilterType.AlwaysOff)
|
||||
.AddInMemoryExporter(metrics, metricReaderOptions =>
|
||||
{
|
||||
metricReaderOptions.TemporalityPreference = aggregationTemporality;
|
||||
|
|
@ -793,7 +793,7 @@ public class OtlpMetricsExporterTests : Http2UnencryptedSupportTests
|
|||
|
||||
using var meterProvider = Sdk.CreateMeterProviderBuilder()
|
||||
.AddMeter(meter.Name)
|
||||
.SetExemplarFilter(new AlwaysOnExemplarFilter())
|
||||
.SetExemplarFilter(ExemplarFilterType.AlwaysOn)
|
||||
.AddView(i =>
|
||||
{
|
||||
return !enableTagFiltering
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ public static class Program
|
|||
|
||||
if (options.EnableExemplars)
|
||||
{
|
||||
builder.SetExemplarFilter(new AlwaysOnExemplarFilter());
|
||||
builder.SetExemplarFilter(ExemplarFilterType.AlwaysOn);
|
||||
}
|
||||
|
||||
if (options.AddViewToFilterTags)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public class MetricExemplarTests : MetricTestsBase
|
|||
|
||||
using var container = this.BuildMeterProvider(out var meterProvider, builder => builder
|
||||
.AddMeter(meter.Name)
|
||||
.SetExemplarFilter(new AlwaysOnExemplarFilter())
|
||||
.SetExemplarFilter(ExemplarFilterType.AlwaysOn)
|
||||
.AddView(i =>
|
||||
{
|
||||
if (i.Name.StartsWith("testCounter"))
|
||||
|
|
@ -153,7 +153,7 @@ public class MetricExemplarTests : MetricTestsBase
|
|||
|
||||
using var container = this.BuildMeterProvider(out var meterProvider, builder => builder
|
||||
.AddMeter(meter.Name)
|
||||
.SetExemplarFilter(new AlwaysOnExemplarFilter())
|
||||
.SetExemplarFilter(ExemplarFilterType.AlwaysOn)
|
||||
.AddInMemoryExporter(exportedItems, metricReaderOptions =>
|
||||
{
|
||||
metricReaderOptions.TemporalityPreference = temporality;
|
||||
|
|
@ -237,7 +237,7 @@ public class MetricExemplarTests : MetricTestsBase
|
|||
|
||||
using var container = this.BuildMeterProvider(out var meterProvider, builder => builder
|
||||
.AddMeter(meter.Name)
|
||||
.SetExemplarFilter(new AlwaysOnExemplarFilter())
|
||||
.SetExemplarFilter(ExemplarFilterType.AlwaysOn)
|
||||
.AddView(i =>
|
||||
{
|
||||
if (i.Name.StartsWith("histogramWithBucketsAndMinMax"))
|
||||
|
|
@ -367,7 +367,7 @@ public class MetricExemplarTests : MetricTestsBase
|
|||
|
||||
using var container = this.BuildMeterProvider(out var meterProvider, builder => builder
|
||||
.AddMeter(meter.Name)
|
||||
.SetExemplarFilter(new AlwaysOnExemplarFilter())
|
||||
.SetExemplarFilter(ExemplarFilterType.AlwaysOn)
|
||||
.AddView(i =>
|
||||
{
|
||||
if (i.Name.StartsWith("histogramWithoutBucketsAndMinMax"))
|
||||
|
|
@ -495,7 +495,7 @@ public class MetricExemplarTests : MetricTestsBase
|
|||
|
||||
using var container = this.BuildMeterProvider(out var meterProvider, builder => builder
|
||||
.AddMeter(meter.Name)
|
||||
.SetExemplarFilter(new AlwaysOnExemplarFilter())
|
||||
.SetExemplarFilter(ExemplarFilterType.AlwaysOn)
|
||||
.AddView(i =>
|
||||
{
|
||||
if (i.Name.StartsWith("exponentialHistogramWithMinMax"))
|
||||
|
|
@ -601,6 +601,53 @@ public class MetricExemplarTests : MetricTestsBase
|
|||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void TestTraceBasedExemplarFilter(bool enableTracing)
|
||||
{
|
||||
var exportedItems = new List<Metric>();
|
||||
|
||||
using var meter = new Meter($"{Utils.GetCurrentMethodName()}");
|
||||
|
||||
var counter = meter.CreateCounter<long>("testCounter");
|
||||
|
||||
using var container = this.BuildMeterProvider(out var meterProvider, builder => builder
|
||||
.AddMeter(meter.Name)
|
||||
.SetExemplarFilter(ExemplarFilterType.TraceBased)
|
||||
.AddInMemoryExporter(exportedItems));
|
||||
|
||||
if (enableTracing)
|
||||
{
|
||||
using var act = new Activity("test").Start();
|
||||
act.ActivityTraceFlags = ActivityTraceFlags.Recorded;
|
||||
counter.Add(18);
|
||||
}
|
||||
else
|
||||
{
|
||||
counter.Add(18);
|
||||
}
|
||||
|
||||
meterProvider.ForceFlush();
|
||||
|
||||
Assert.Single(exportedItems);
|
||||
|
||||
var metricPoint = GetFirstMetricPoint(exportedItems);
|
||||
|
||||
Assert.NotNull(metricPoint);
|
||||
|
||||
var exemplars = GetExemplars(metricPoint.Value);
|
||||
|
||||
if (enableTracing)
|
||||
{
|
||||
Assert.Single(exemplars);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Empty(exemplars);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestExemplarsFilterTags()
|
||||
{
|
||||
|
|
@ -612,7 +659,7 @@ public class MetricExemplarTests : MetricTestsBase
|
|||
|
||||
using var container = this.BuildMeterProvider(out var meterProvider, builder => builder
|
||||
.AddMeter(meter.Name)
|
||||
.SetExemplarFilter(new AlwaysOnExemplarFilter())
|
||||
.SetExemplarFilter(ExemplarFilterType.AlwaysOn)
|
||||
.AddView(histogram.Name, new MetricStreamConfiguration() { TagKeys = new string[] { "key1" } })
|
||||
.AddInMemoryExporter(exportedItems, metricReaderOptions =>
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue