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

[sdk-metrics] ExemplarFilter updates to match latest specification #5404

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
54 changes: 17 additions & 37 deletions docs/metrics/customizing-the-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,58 +412,38 @@ 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;
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
Expand All @@ -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

Expand Down
39 changes: 1 addition & 38 deletions docs/metrics/extending-the-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion examples/AspNetCore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
builder
.AddMeter(Instrumentation.MeterName)
#if EXPOSE_EXPERIMENTAL_FEATURES
.SetExemplarFilter(new TraceBasedExemplarFilter())
.SetExemplarFilter(ExemplarFilterType.TraceBased)
#endif
.AddRuntimeInstrumentation()
.AddHttpClientInstrumentation()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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?>>
Expand All @@ -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?>
Expand All @@ -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!
10 changes: 10 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@
API.
([#5396](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5396))

* **Experimental (pre-release builds only):** Removed the `ExemplarFilter`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice wording! crisp and clear. 💯

`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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,36 +319,54 @@ public static MeterProvider Build(this MeterProviderBuilder meterProviderBuilder

#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"/>.
Copy link
Member

@vishweshbankwar vishweshbankwar Mar 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default is TraceBased

Edit: I see it says provider configuration. Could we reword this? If someone just does SetExemplarFilter(), the default would be TraceBased

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need some spec work to clarify defaults. It is not clear if exemplars, as a feature, itself is enabled by default. once enabled, yes, tracebased is the default, but we need to see if that makes sense. (I don't think so, but will wait for Blanch's current work before we approach spec)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree it is a bit confusing in the spec.

There's two statements in metrics sdk:

Exemplar sampling SHOULD be turned off by default. If Exemplar sampling is off, the SDK MUST NOT have overhead related to exemplar sampling.

The ExemplarFilter SHOULD be a configuration parameter of a MeterProvider for an SDK. The default value SHOULD be TraceBased.

I tried to satisfy that 🤣

/// </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.");
}
}
});

Expand Down
16 changes: 1 addition & 15 deletions src/OpenTelemetry/Metrics/Exemplar/AlwaysOffExemplarFilter.cs
Original file line number Diff line number Diff line change
@@ -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
CodeBlanch marked this conversation as resolved.
Show resolved Hide resolved
{
/// <inheritdoc/>
public override bool ShouldSample(long value, ReadOnlySpan<KeyValuePair<string, object?>> tags)
Expand Down
19 changes: 1 addition & 18 deletions src/OpenTelemetry/Metrics/Exemplar/AlwaysOnExemplarFilter.cs
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
Loading