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

Specify optional Exponential Histogram Aggregation, add example code in the data model #2252

Merged
merged 44 commits into from May 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
8d31b22
Add more sample code for exponential histogram boundaries; mention su…
jmacd Jan 7, 2022
3e01b83
Specify an optional exponential histogram Aggregation with two config…
jmacd Jan 7, 2022
aefaed5
changelog entry
jmacd Jan 7, 2022
698c2e8
update PR number
jmacd Jan 7, 2022
27af2a7
Lint
jmacd Jan 7, 2022
f8ae27d
clarify & note
jmacd Jan 7, 2022
639d565
markdownlint
jmacd Jan 7, 2022
8ed6d94
revisions from code review
jmacd Jan 25, 2022
590d483
remove range limits; separate normative text a bit more
jmacd Feb 1, 2022
5770ebf
Merge branch 'main' of github.com:open-telemetry/opentelemetry-specif…
jmacd Feb 1, 2022
a653529
missing add
jmacd Feb 1, 2022
6d9baa1
Merge branch 'main' of github.com:open-telemetry/opentelemetry-specif…
jmacd Feb 11, 2022
513e987
Merge branch 'main' of github.com:open-telemetry/opentelemetry-specif…
jmacd Feb 28, 2022
854c9a9
lint
jmacd Feb 28, 2022
ef363b5
lintier
jmacd Feb 28, 2022
8547158
Merge branch 'main' of github.com:open-telemetry/opentelemetry-specif…
jmacd Apr 1, 2022
499831b
edits
jmacd Apr 1, 2022
c49d268
Merge branch 'main' of github.com:open-telemetry/opentelemetry-specif…
jmacd Apr 1, 2022
dd0e2d6
lint
jmacd Apr 1, 2022
bf5c7ab
Merge branch 'main' into jmacd/sdkexpohisto
jmacd Apr 4, 2022
ee22edc
Merge branch 'main' of github.com:open-telemetry/opentelemetry-specif…
jmacd Apr 4, 2022
7c462f7
Merge branch 'jmacd/sdkexpohisto' of github.com:jmacd/opentelemetry-s…
jmacd Apr 4, 2022
77fbca0
use MAY
jmacd Apr 4, 2022
59f3a00
Merge branch 'main' of github.com:open-telemetry/opentelemetry-specif…
jmacd Apr 15, 2022
7952ff4
Merge branch 'main' into jmacd/sdkexpohisto
reyang Apr 18, 2022
3600988
Merge branch 'main' into jmacd/sdkexpohisto
reyang Apr 18, 2022
c5104a4
Merge branch 'main' into jmacd/sdkexpohisto
reyang Apr 19, 2022
f6cf11a
Merge branch 'main' into jmacd/sdkexpohisto
reyang Apr 19, 2022
36e0947
Merge branch 'main' of github.com:open-telemetry/opentelemetry-specif…
jmacd Apr 21, 2022
34f6166
Apply suggestions from code review
jmacd Apr 21, 2022
559a6cb
lint
jmacd Apr 21, 2022
7f46b7c
Merge branch 'jmacd/sdkexpohisto' of github.com:jmacd/opentelemetry-s…
jmacd Apr 21, 2022
a89aba9
Update specification/metrics/sdk.md
jmacd Apr 21, 2022
be7d875
yzhuge suggestion
jmacd Apr 21, 2022
4f62a4c
Merge branch 'main' into jmacd/sdkexpohisto
reyang Apr 29, 2022
d15ea33
note on inclusivity
jmacd Apr 29, 2022
1a473d0
require consistency; do not histogram non-normal values
jmacd Apr 29, 2022
a26fc6e
Merge branch 'jmacd/sdkexpohisto' of github.com:jmacd/opentelemetry-s…
jmacd Apr 29, 2022
e0cf769
Merge branch 'main' into jmacd/sdkexpohisto
reyang May 3, 2022
bdc6593
Merge branch 'main' of github.com:open-telemetry/opentelemetry-specif…
jmacd May 5, 2022
0b32697
Merge branch 'jmacd/sdkexpohisto' of github.com:jmacd/opentelemetry-s…
jmacd May 5, 2022
982e015
Merge branch 'main' of github.com:open-telemetry/opentelemetry-specif…
jmacd May 10, 2022
6d2edef
Changelog.
jmacd May 10, 2022
4b14fed
Merge branch 'main' into jmacd/sdkexpohisto
reyang May 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Expand Up @@ -13,6 +13,17 @@ release.

### Metrics

- Clarify that API support for multi-instrument callbacks is permitted.
([#2263](https://github.com/open-telemetry/opentelemetry-specification/pull/2263)).
- Clarify SDK behavior when view conflicts are present
([#2462](https://github.com/open-telemetry/opentelemetry-specification/pull/2462)).
- Clarify MetricReader.Collect result
([#2495](https://github.com/open-telemetry/opentelemetry-specification/pull/2495)).
- Add database connection pool metrics semantic conventions
([#2273](https://github.com/open-telemetry/opentelemetry-specification/pull/2273)).
- Specify optional support for an Exponential Histogram Aggregation.
([#2252](https://github.com/open-telemetry/opentelemetry-specification/pull/2252))

### Logs

### Resource
Expand Down
77 changes: 71 additions & 6 deletions specification/metrics/datamodel.md
Expand Up @@ -22,6 +22,7 @@
* [Sums](#sums)
* [Gauge](#gauge)
* [Histogram](#histogram)
+ [Histogram: Bucket inclusivity](#histogram-bucket-inclusivity)
* [ExponentialHistogram](#exponentialhistogram)
+ [Exponential Scale](#exponential-scale)
+ [Exponential Buckets](#exponential-buckets)
Expand All @@ -33,6 +34,7 @@
- [Positive Scale: Use a Lookup Table](#positive-scale-use-a-lookup-table)
+ [ExponentialHistogram: Producer Recommendations](#exponentialhistogram-producer-recommendations)
+ [ExponentialHistogram: Consumer Recommendations](#exponentialhistogram-consumer-recommendations)
+ [ExponentialHistogram: Bucket inclusivity](#exponentialhistogram-bucket-inclusivity)
* [Summary (Legacy)](#summary-legacy)
- [Exemplars](#exemplars)
- [Single-Writer](#single-writer)
Expand Down Expand Up @@ -522,6 +524,8 @@ Bucket counts are optional. A Histogram without buckets conveys a
population in terms of only the sum and count, and may be interpreted
as a histogram with single bucket covering `(-Inf, +Inf)`.

#### Histogram: Bucket inclusivity

Bucket upper-bounds are inclusive (except for the case where the
upper-bound is +Inf) while bucket lower-bounds are exclusive. That is,
buckets express the number of values that are greater than their lower
Expand Down Expand Up @@ -716,6 +720,21 @@ func GetExponent(value float64) int32 {
}
```

Implementations are permitted to round subnormal values up to the
smallest normal value, which may permit the use of a built-in function:

```golang

func GetExponent(value float64) int {
// Note: Frexp() rounds submnormal values to the smallest normal
// value and returns an exponent corresponding to fractions in the
// range [0.5, 1), whereas we want [1, 2), so subtract 1 from the
// exponent.
_, exp := math.Frexp(value)
return exp - 1
}
```

##### Negative Scale: Extract and Shift the Exponent

For negative scales, the index of a value equals the normalized
Expand All @@ -727,19 +746,59 @@ correct rounding for the negative indices. This may be written as:
return GetExponent(value) >> -scale
```

The reverse mapping function is:

```golang
return math.Ldexp(1, index << -scale)
```

Note that the reverse mapping function is expected to produce
subnormal values even when the mapping function rounds them into
normal values, since the lower boundary of the bucket containing the
smallest normal value may be subnormal. For example, at scale -4 the
smallest normal value `0x1p-1022` falls into a bucket with lower
boundary `0x1p-1024`.

##### All Scales: Use the Logarithm Function

For any scale, use of the built-in natural logarithm
function. A multiplicative factor equal to `2**scale / ln(2)`
proves useful (where `ln()` is the natural logarithm), for example:
For any scale, the built-in natural logarithm function can be used to
compute the bucket index. A multiplicative factor equal to `2**scale
/ ln(2)` proves useful (where `ln()` is the natural logarithm), for
example:

```golang
scaleFactor := math.Log2E * math.Exp2(scale)
return int64(math.Floor(math.Log(value) * scaleFactor))
scaleFactor := math.Ldexp(math.Log2E, scale)
return math.Floor(math.Log(value) * scaleFactor)
```

Note that in the example Golang code above, the built-in `math.Log2E`
is defined as `1 / ln(2)`.
is defined as the inverse of the natural logarithm of 2, i.e., `1 / ln(2)`.

The reverse mapping function is:

```golang
inverseFactor := math.Ldexp(math.Ln2, -scale)
return math.Exp(index * inverseFactor), nil
```

Implementations are expected to verify that their mapping function and
inverse mapping function are correct near the lowest and highest IEEE
floating point values. A mathematically correct formula may produce
wrong result, because of accumulated floating point calculation error
or underflow/overflow of intermediate results. In the Golang
reference implementation, for example, the above formula computes
`+Inf` for the maximum-index bucket. In this case, it is appropriate
to subtract `1<<scale` from the index and multiply the result by `2`.

jmacd marked this conversation as resolved.
Show resolved Hide resolved
```golang
// Use this form in case the equation above computes +Inf
// as the lower boundary of a valid bucket.
inverseFactor := math.Ldexp(math.Ln2, -scale)
return 2.0 * math.Exp((index - (1 << scale)) * inverseFactor), nil
```

*Note that floating-point to integer type conversions have been
omitted from the code fragments above, to improve readability.*

##### Positive Scale: Use a Lookup Table

Expand Down Expand Up @@ -781,6 +840,12 @@ bucket indices that overflow or underflow this representation.
Consumers that reject such data SHOULD warn the user through error
logging that out-of-range data was received.

#### ExponentialHistogram: Bucket inclusivity

The [specification on bucket inclusivity made for explicit-boundary
Histogram data](#histogram-bucket-inclusivity) applies equally to
ExponentialHistogram data.

### Summary (Legacy)

[Summary](https://github.com/open-telemetry/opentelemetry-proto/blob/v0.9.0/opentelemetry/proto/metrics/v1/metrics.proto#L268)
Expand Down
115 changes: 109 additions & 6 deletions specification/metrics/sdk.md
Expand Up @@ -17,7 +17,13 @@
+ [Default Aggregation](#default-aggregation)
+ [Sum Aggregation](#sum-aggregation)
+ [Last Value Aggregation](#last-value-aggregation)
- [Histogram Aggregation common behavior](#histogram-aggregation-common-behavior)
+ [Explicit Bucket Histogram Aggregation](#explicit-bucket-histogram-aggregation)
+ [Exponential Histogram Aggregation](#exponential-histogram-aggregation)
- [Exponential Histogram Aggregation: Handle all normal values](#exponential-histogram-aggregation-handle-all-normal-values)
- [Exponential Histogram Aggregation: Support a minimum and maximum scale](#exponential-histogram-aggregation-support-a-minimum-and-maximum-scale)
- [Exponential Histogram Aggregation: Use the maximum scale for single measurements](#exponential-histogram-aggregation-use-the-maximum-scale-for-single-measurements)
- [Exponential Histogram Aggregation: Maintain the ideal scale](#exponential-histogram-aggregation-maintain-the-ideal-scale)
* [Observations inside asynchronous callbacks](#observations-inside-asynchronous-callbacks)
* [Resolving duplicate instrument registration conflicts](#resolving-duplicate-instrument-registration-conflicts)
- [Attribute limits](#attribute-limits)
Expand Down Expand Up @@ -339,6 +345,10 @@ The SDK MUST provide the following `Aggregation` to support the
- [Last Value](./sdk.md#last-value-aggregation)
- [Explicit Bucket Histogram](./sdk.md#explicit-bucket-histogram-aggregation)

The SDK MAY provide the following `Aggregation`:

- [Exponential Histogram Aggregation](./sdk.md#exponential-histogram-aggregation)

#### Drop Aggregation

The Drop Aggregation informs the SDK to ignore/drop all Instrument Measurements
Expand Down Expand Up @@ -397,6 +407,16 @@ This Aggregation informs the SDK to collect:
- The last `Measurement`.
- The timestamp of the last `Measurement`.

##### Histogram Aggregation common behavior

All histogram Aggregations inform the SDK to collect:

- Count of `Measurement` values in population.
- Arithmetic sum of `Measurement` values in population. This SHOULD NOT be collected when used with
instruments that record negative measurements (e.g. `UpDownCounter` or `ObservableGauge`).
jmacd marked this conversation as resolved.
Show resolved Hide resolved
- Min (optional) `Measurement` value in population.
- Max (optional) `Measurement` value in population.

#### Explicit Bucket Histogram Aggregation

The Explicit Bucket Histogram Aggregation informs the SDK to collect data for
Expand All @@ -410,13 +430,96 @@ This Aggregation honors the following configuration parameters:
| Boundaries | double\[\] | [ 0, 5, 10, 25, 50, 75, 100, 250, 500, 1000 ] | Array of increasing values representing explicit bucket boundary values.<br><br>The Default Value represents the following buckets:<br>(-&infin;, 0], (0, 5.0], (5.0, 10.0], (10.0, 25.0], (25.0, 50.0], (50.0, 75.0], (75.0, 100.0], (100.0, 250.0], (250.0, 500.0], (500.0, 1000.0], (1000.0, +&infin;) |
| RecordMinMax | true, false | true | Whether to record min and max. |

This Aggregation informs the SDK to collect:
Explicit buckets are stated in terms of their upper boundary. Buckets
are exclusive of their lower boundary and inclusive of their upper
bound (except at positive infinity). A measurement is defined to fall
into the greatest-numbered bucket with boundary that is greater than
or equal to the measurement.

- Count of `Measurement` values falling within explicit bucket boundaries.
- Arithmetic sum of `Measurement` values in population. This SHOULD NOT be collected when used with
instruments that record negative measurements, e.g. `UpDownCounter` or `ObservableGauge`.
- Min (optional) `Measurement` value in population.
- Max (optional) `Measurement` value in population.
#### Exponential Histogram Aggregation

The Exponential Histogram Aggregation informs the SDK to collect data
for the [Exponential Histogram Metric
Point](./datamodel.md#exponentialhistogram), which uses an exponential
formula to determine bucket boundaries and an integer `scale`
parameter to control resolution.

Scale is not a configurable property of this Aggregation, the
implementation will adjust it as necessary given the data. This
Aggregation honors the following configuration parameter:

| Key | Value | Default Value | Description |
|---------|---------|---------------|--------------------------------------------------------------------------------------------------------------|
| MaxSize | integer | 160 | Maximum number of buckets in each of the positive and negative ranges, not counting the special zero bucket. |

The default of 160 buckets is selected to establish default support
for a high-resolution histogram able to cover a long-tail latency
distribution from 1ms to 100s with less than 5% relative error.
jmacd marked this conversation as resolved.
Show resolved Hide resolved
Because 160 can be factored into `10 * 2**K`, maximum contrast is
relatively simple to derive for scale `K`:

| Scale | Maximum data contrast at 10 * 2**K buckets |
|-------|--------------------------------------------|
| K+2 | 5.657 (2**(10/4)) |
| K+1 | 32 (2**(10/2)) |
| K | 1024 (2**10) |
| K-1 | 1048576 (2**20) |

The following table shows how the ideal scale for 160 buckets is
calculated as a function of the input range:

| Input range | Contrast | Ideal Scale | Base | Relative error |
|-------------|----------|-------------|----------|----------------|
| 1ms - 4ms | 4 | 6 | 1.010889 | 0.542% |
| 1ms - 20ms | 20 | 5 | 1.021897 | 1.083% |
| 1ms - 1s | 10**3 | 4 | 1.044274 | 2.166% |
| 1ms - 100s | 10**5 | 3 | 1.090508 | 4.329% |
| 1μs - 10s | 10**7 | 2 | 1.189207 | 8.643% |

Note that relative error is calculated as half of the bucket width
divided by the bucket midpoint, which is the same in every bucket.
Using the bucket from [1, base), we have `(bucketWidth / 2) /
bucketMidpoint = ((base - 1) / 2) / ((base + 1) / 2) = (base - 1) /
(base + 1)`.

This Aggregation uses the notion of "ideal" scale. The ideal scale is
either:

1. The maximum supported scale, generally used for single-value histogram Aggregations where scale is not otherwise constrained
2. The largest value of scale such that no more than the maximum number of buckets are needed to represent the full range of input data in either of the positive or negative ranges.

##### Exponential Histogram Aggregation: Handle all normal values

Implementations are REQUIRED to accept the entire normal range of IEEE
floating point values (i.e., all values except for +Inf, -Inf and NaN
values).
jmacd marked this conversation as resolved.
Show resolved Hide resolved

Implementations SHOULD NOT incorporate non-normal values (i.e., +Inf,
-Inf, and NaNs) into the `sum`, `min`, and `max` fields, because these
values do not map into a valid bucket.

Implementations MAY round subnormal values away from zero to the
nearest normal value.

##### Exponential Histogram Aggregation: Support a minimum and maximum scale

The implementation MUST maintain reasonable minimum and maximum scale
parameters that the automatic scale parameter will not exceed.

##### Exponential Histogram Aggregation: Use the maximum scale for single measurements

When the histogram contains not more than one value in either of the
positive or negative ranges, the implementation SHOULD use the maximum
scale.

##### Exponential Histogram Aggregation: Maintain the ideal scale

Implementations SHOULD adjust the histogram scale as necessary to
maintain the best resolution possible, within the constraint of
maximum size (max number of buckets). Best resolution (highest scale)
is achieved when the number of positive or negative range buckets
exceeds half the maximum size, such that increasing scale by one would
not be possible given the size constraint.

### Observations inside asynchronous callbacks

Expand Down