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

[docs-metrics] Customer ExemplarReservoir and View API configuration #5624

Merged
39 changes: 33 additions & 6 deletions docs/metrics/customizing-the-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,28 @@ var meterProvider = Sdk.CreateMeterProviderBuilder()
.Build();
```

### Changing the ExemplarReservoir for a Metric

To set the [ExemplarReservoir](#exemplarreservoir) for an individual metric, use
Copy link
Member

Choose a reason for hiding this comment

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

start with saying "The default Reservoir is xyz. There is no ability to change default Reservoir. However, an experimental API is available to change the Reservoir using the View API"...

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't feel this is really necessary. The ExemplarReservoir link/anchor there jumps down to the section with all that detail.

I did add this though: 9dcb5be

the `MetricStreamConfiguration.ExemplarReservoirFactory` property on the View
API:

> [!NOTE]
> `MetricStreamConfiguration.ExemplarReservoirFactory` is an experimental API only
available in pre-release builds. For details see:
[OTEL1004](../../diagnostics/experimental-apis/OTEL1004.md).

```csharp
var meterProvider = Sdk.CreateMeterProviderBuilder()
.AddMeter("MyCompany.MyProduct.MyLibrary")
// Use MyCustomExemplarReservoir for "MyFruitCounter"
.AddView(
instrumentName: "MyFruitCounter",
new MetricStreamConfiguration { ExemplarReservoirFactory = () => new MyCustomExemplarReservoir() })
.AddConsoleExporter()
.Build();
```

### Exemplars

Exemplars are example data points for aggregated data. They provide access to
Expand Down Expand Up @@ -421,20 +443,25 @@ and is responsible for recording `Exemplar`s. The following are the default
reservoirs:

* `AlignedHistogramBucketExemplarReservoir` is the default reservoir used for
Histograms with buckets, and it stores at most one exemplar per histogram
bucket. The exemplar stored is the last measurement recorded - i.e. any new
Histograms with buckets, and it stores at most one `Exemplar` per histogram
bucket. The `Exemplar` stored is the last measurement recorded - i.e. any new
measurement overwrites the previous one in that bucket.

* `SimpleFixedSizeExemplarReservoir` is the default reservoir used for all
metrics except Histograms with buckets. It has a fixed reservoir pool, and
metrics except histograms with buckets. It has a fixed reservoir pool, and
implements the equivalent of [naive
reservoir](https://en.wikipedia.org/wiki/Reservoir_sampling). The reservoir pool
size (currently defaulting to 1) determines the maximum number of exemplars
size (currently defaulting to 1) determines the maximum number of `Exemplar`s
stored. Exponential histograms use a `SimpleFixedSizeExemplarReservoir` with a
pool size equal to the number of buckets up to a max of `20`.

> [!NOTE]
> Currently there is no ability to change or configure `ExemplarReservoir`.
See [Changing the ExemplarReservoir for a
Metric](#changing-the-exemplarreservoir-for-a-metric) for details on how to use
the View API to change `ExemplarReservoir`s for a Metric.

See [Building your own exemplar
reservoir](../extending-the-sdk/README.md#exemplarreservoir) for details on how
to implement custom `ExemplarReservoir`s.

### Instrumentation

Expand Down
85 changes: 80 additions & 5 deletions docs/metrics/extending-the-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

* [Building your own exporter](#exporter)
* [Building your own reader](#reader)
* [Building your own exemplar filter](#exemplarfilter)
* [Building your own exemplar reservoir](#exemplarreservoir)
* [Building your own resource detector](../../resources/README.md#resource-detector)
* [References](#references)
Expand Down Expand Up @@ -72,12 +71,88 @@ to the `MeterProvider` as shown in the example [here](./Program.cs).

Not supported.

## ExemplarFilter
## ExemplarReservoir

Not supported.
> [!NOTE]
> `ExemplarReservoir` is an experimental API only available in pre-release
builds. For details see:
[OTEL1004](../../diagnostics/experimental-apis/OTEL1004.md).

## ExemplarReservoir
Custom [ExemplarReservoir](../customizing-the-sdk/README.md#exemplarreservoir)s
can be implemented to control how `Exemplar`s are recorded for a metric:
cijothomas marked this conversation as resolved.
Show resolved Hide resolved

Not supported.
* `ExemplarReservoir`s should derive from `FixedSizeExemplarReservoir` (which
belongs to the [OpenTelemetry](../../../src/OpenTelemetry/README.md) package)
and implement the `Offer` methods.
* The `FixedSizeExemplarReservoir` constructor accepts a `capacity` parameter to
control the number of `Exemplar`s which may be recorded by the
`ExemplarReservoir`.
* The `virtual` `OnCollected` method is called after the `ExemplarReservoir`
collection operation has completed and may be used to implement cleanup or
reset logic.
* The `bool` `ResetOnCollect` property on `ExemplarReservoir` is set to `true`
when delta aggregation temporality is used for the metric using the
`ExemplarReservoir`.

```csharp
cijothomas marked this conversation as resolved.
Show resolved Hide resolved
class HighestValueExemplarReservoir : FixedSizeExemplarReservoir
{
private readonly object lockObject = new();
private long? previousValueLong;
private double? previousValueDouble;

public HighestValueExemplarReservoir()
: base(capacity: 1)
{
}

public override void Offer(in ExemplarMeasurement<long> measurement)
{
if (!this.previousValueLong.HasValue || measurement.Value > this.previousValueLong.Value)
{
lock (this.lockObject)
cijothomas marked this conversation as resolved.
Show resolved Hide resolved
{
if (!this.previousValueLong.HasValue || measurement.Value > this.previousValueLong.Value)
{
this.UpdateExemplar(0, in measurement);
this.previousValueLong = measurement.Value;
}
}
}
}

public override void Offer(in ExemplarMeasurement<double> measurement)
{
if (!this.previousValueDouble.HasValue || measurement.Value > this.previousValueDouble.Value)
{
lock (this.lockObject)
{
if (!this.previousValueDouble.HasValue || measurement.Value > this.previousValueDouble.Value)
{
this.UpdateExemplar(0, in measurement);
this.previousValueDouble = measurement.Value;
}
}
}
}

protected override void OnCollected()
{
if (this.ResetOnCollect)
{
lock (this.lockObject)
{
this.previousValueLong = null;
this.previousValueDouble = null;
}
}
}
}
```

Custom [ExemplarReservoir](../customizing-the-sdk/README.md#exemplarreservoir)s
can be configured using the View API. For details see: [Changing the
ExemplarReservoir for a
Metric](../customizing-the-sdk/README.md#changing-the-exemplarreservoir-for-a-metric).

## References