Skip to content

Commit

Permalink
[docs-metrics] Customer ExemplarReservoir and View API configuration (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeBlanch committed May 20, 2024
1 parent 16f2bf0 commit 2abf2a9
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 16 deletions.
8 changes: 4 additions & 4 deletions docs/diagnostics/experimental-apis/OTEL1004.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ We are exposing these APIs experimentally for the following reasons:
**TL;DR** We want to gather feedback on the usability of the API and for the
need(s) in general for custom reservoirs before exposing a stable API.

<!--
## Provide feedback

Please provide feedback on [this issue](TODO) if you need stable support for
custom `ExemplarReservoir`s.
-->
Please provide feedback on [this
issue](https://github.com/open-telemetry/opentelemetry-dotnet/issues/5629) if
you need stable support for custom `ExemplarReservoir`s. The feedback will help
inform decisions about what to expose stable and when.
40 changes: 33 additions & 7 deletions docs/metrics/customizing-the-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ within the maximum number of buckets defined by `MaxSize`. The default
`MaxSize` is 160 buckets and the default `MaxScale` is 20.

```csharp
// Change the maximum number of buckets
// Change the maximum number of buckets for "MyHistogram"
.AddView(
instrumentName: "MyHistogram",
new Base2ExponentialBucketHistogramConfiguration { MaxSize = 40 })
Expand All @@ -251,6 +251,28 @@ by using Views.

See [Program.cs](./Program.cs) for a complete example.

#### Change the ExemplarReservoir

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

To set the [ExemplarReservoir](#exemplarreservoir) for an instrument, use the
`MetricStreamConfiguration.ExemplarReservoirFactory` property on the View API:

> [!IMPORTANT]
> Setting `MetricStreamConfiguration.ExemplarReservoirFactory` alone will NOT
enable `Exemplar`s for an instrument. An [ExemplarFilter](#exemplarfilter)
MUST also be used.

```csharp
// Use MyCustomExemplarReservoir for "MyFruitCounter"
.AddView(
instrumentName: "MyFruitCounter",
new MetricStreamConfiguration { ExemplarReservoirFactory = () => new MyCustomExemplarReservoir() })
```

### Changing maximum Metric Streams

Every instrument results in the creation of a single Metric stream. With Views,
Expand Down Expand Up @@ -421,20 +443,24 @@ 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 [Change the ExemplarReservoir](#change-the-exemplarreservoir) for details on
how to use the View API to change `ExemplarReservoir`s for an instrument.

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

### Instrumentation

Expand Down
100 changes: 95 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,103 @@ 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). Please [provide
feedback](https://github.com/open-telemetry/opentelemetry-dotnet/issues/5629)
to help inform decisions about what should be exposed stable and when.

## ExemplarReservoir
Custom [ExemplarReservoir](../customizing-the-sdk/README.md#exemplarreservoir)s
can be implemented to control how `Exemplar`s are recorded for a metric:

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`.
* The `Offer` and `Collect` `ExemplarReservoir` methods are called concurrently
by the OpenTelemetry SDK. As such any state required by custom
`ExemplarReservoir` implementations needs to be managed using appropriate
thread-safety/concurrency mechanisms (`lock`, `Interlocked`, etc.).
* Custom `ExemplarReservoir` implementations MUST NOT throw exceptions.
Exceptions thrown in custom implementations MAY lead to unreleased locks and
undefined behaviors.

The following example demonstrates a custom `ExemplarReservoir` implementation
which records `Exemplar`s for measurements which have the highest value. When
delta aggregation temporality is used the recorded `Exemplar` will be the
highest value for a given collection cycle. When cumulative aggregation
temporality is used the recorded `Exemplar` will be the highest value for the
lifetime of the process.

```csharp
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)
{
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: [Change the
ExemplarReservoir](../customizing-the-sdk/README.md#change-the-exemplarreservoir).

## References

0 comments on commit 2abf2a9

Please sign in to comment.