Skip to content

Commit 9f41ead

Browse files
xiang17reyangCodeBlanch
authored
[sdk-metrics] Promote overflow attribute from experimental to stable (open-telemetry#5909)
Co-authored-by: Reiley Yang <[email protected]> Co-authored-by: Mikel Blanchard <[email protected]>
1 parent 5efc683 commit 9f41ead

File tree

12 files changed

+60
-257
lines changed

12 files changed

+60
-257
lines changed

docs/metrics/README.md

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
* [Instruments](#instruments)
1111
* [MeterProvider Management](#meterprovider-management)
1212
* [Memory Management](#memory-management)
13+
* [Example](#example)
1314
* [Pre-Aggregation](#pre-aggregation)
1415
* [Cardinality Limits](#cardinality-limits)
1516
* [Memory Preallocation](#memory-preallocation)
1617
* [Metrics Correlation](#metrics-correlation)
1718
* [Metrics Enrichment](#metrics-enrichment)
19+
* [Common issues that lead to missing metrics](#common-issues-that-lead-to-missing-metrics)
1820

1921
</details>
2022

@@ -386,22 +388,13 @@ and the `MetricStreamConfiguration.CardinalityLimit` setting. Refer to this
386388
[doc](../../docs/metrics/customizing-the-sdk/README.md#changing-the-cardinality-limit-for-a-metric)
387389
for more information.
388390

389-
Given a metric, once the cardinality limit is reached, any new measurement which
390-
cannot be independently aggregated because of the limit will be dropped or
391-
aggregated using the [overflow
392-
attribute](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#overflow-attribute)
393-
(if enabled). When NOT using the overflow attribute feature a warning is written
394-
to the [self-diagnostic log](../../src/OpenTelemetry/README.md#self-diagnostics)
395-
the first time an overflow is detected for a given metric.
396-
397-
> [!NOTE]
398-
> Overflow attribute was introduced in OpenTelemetry .NET
399-
[1.6.0-rc.1](../../src/OpenTelemetry/CHANGELOG.md#160-rc1). It is currently an
400-
experimental feature which can be turned on by setting the environment
401-
variable `OTEL_DOTNET_EXPERIMENTAL_METRICS_EMIT_OVERFLOW_ATTRIBUTE=true`. Once
402-
the [OpenTelemetry
403-
Specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#overflow-attribute)
404-
become stable, this feature will be turned on by default.
391+
Given a metric, once the cardinality limit is reached, any new measurement
392+
that could not be independently aggregated will be aggregated using the
393+
[overflow attribute](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#overflow-attribute).
394+
In versions prior to 1.10.0, the default behavior when cardinality limit was
395+
reached was to drop the measurement. Users had the ability to opt-in to use
396+
overflow attribute instead, but this behavior is the default and the only
397+
allowed behavior starting with version 1.10.0.
405398

406399
When [Delta Aggregation
407400
Temporality](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/data-model.md#temporality)

src/OpenTelemetry/CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,31 @@ Notes](../../RELEASENOTES.md).
66

77
## Unreleased
88

9+
* Promoted overflow attribute from experimental to stable and removed the
10+
`OTEL_DOTNET_EXPERIMENTAL_METRICS_EMIT_OVERFLOW_ATTRIBUTE` environment variable.
11+
12+
**Previous Behavior:**
13+
By default, when the cardinality limit was reached, measurements were dropped,
14+
and an internal log was emitted the first time this occurred. Users could
15+
opt-in to experimental overflow attribute feature with
16+
`OTEL_DOTNET_EXPERIMENTAL_METRICS_EMIT_OVERFLOW_ATTRIBUTE=true`.
17+
With this setting, the SDK would use an overflow attribute
18+
(`otel.metric.overflow = true`) to aggregate measurements instead of dropping
19+
measurements. No internal log was emitted in this case.
20+
21+
**New Behavior:**
22+
The SDK now always uses the overflow attribute (`otel.metric.overflow = true`)
23+
to aggregate measurements when the cardinality limit is reached. The previous
24+
approach of dropping measurements has been removed. No internal logs are
25+
emitted when the limit is hit.
26+
27+
The default cardinality limit remains 2000 per metric. To set the cardinality
28+
limit for an individual metric, use the [changing cardinality limit for a
29+
Metric](../../docs/metrics/customizing-the-sdk/README.md#changing-the-cardinality-limit-for-a-metric).
30+
31+
There is NO ability to revert to old behavior.
32+
([#5909](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5909))
33+
934
## 1.10.0-beta.1
1035

1136
Released 2024-Sep-30

src/OpenTelemetry/Metrics/AggregatorStore.cs

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,11 @@ internal sealed class AggregatorStore
2222
internal readonly bool OutputDelta;
2323
internal readonly bool OutputDeltaWithUnusedMetricPointReclaimEnabled;
2424
internal readonly int NumberOfMetricPoints;
25-
internal readonly bool EmitOverflowAttribute;
2625
internal readonly ConcurrentDictionary<Tags, LookupData>? TagsToMetricPointIndexDictionaryDelta;
2726
internal readonly Func<ExemplarReservoir?>? ExemplarReservoirFactory;
2827
internal long DroppedMeasurements = 0;
2928

3029
private const ExemplarFilterType DefaultExemplarFilter = ExemplarFilterType.AlwaysOff;
31-
private static readonly string MetricPointCapHitFixMessage = "Consider opting in for the experimental SDK feature to emit all the throttled metrics under the overflow attribute by setting env variable OTEL_DOTNET_EXPERIMENTAL_METRICS_EMIT_OVERFLOW_ATTRIBUTE = true. You could also modify instrumentation to reduce the number of unique key/value pair combinations. Or use Views to drop unwanted tags. Or use MeterProviderBuilder.SetMaxMetricPointsPerMetricStream to set higher limit.";
3230
private static readonly Comparison<KeyValuePair<string, object?>> DimensionComparisonDelegate = (x, y) => x.Key.CompareTo(y.Key);
3331

3432
private readonly Lock lockZeroTags = new();
@@ -42,7 +40,6 @@ internal sealed class AggregatorStore
4240
new();
4341

4442
private readonly string name;
45-
private readonly string metricPointCapHitMessage;
4643
private readonly MetricPoint[] metricPoints;
4744
private readonly int[] currentMetricPointBatch;
4845
private readonly AggregationType aggType;
@@ -56,7 +53,6 @@ internal sealed class AggregatorStore
5653

5754
private int metricPointIndex = 0;
5855
private int batchSize = 0;
59-
private int metricCapHitMessageLogged;
6056
private bool zeroTagMetricPointInitialized;
6157
private bool overflowTagMetricPointInitialized;
6258

@@ -65,7 +61,6 @@ internal AggregatorStore(
6561
AggregationType aggType,
6662
AggregationTemporality temporality,
6763
int cardinalityLimit,
68-
bool emitOverflowAttribute,
6964
bool shouldReclaimUnusedMetricPoints,
7065
ExemplarFilterType? exemplarFilter = null,
7166
Func<ExemplarReservoir?>? exemplarReservoirFactory = null)
@@ -77,7 +72,6 @@ internal AggregatorStore(
7772
// Previously, these were included within the original cardinalityLimit, but now they are explicitly added to enhance clarity.
7873
this.NumberOfMetricPoints = cardinalityLimit + 2;
7974

80-
this.metricPointCapHitMessage = $"Maximum MetricPoints limit reached for this Metric stream. Configured limit: {cardinalityLimit}";
8175
this.metricPoints = new MetricPoint[this.NumberOfMetricPoints];
8276
this.currentMetricPointBatch = new int[this.NumberOfMetricPoints];
8377
this.aggType = aggType;
@@ -105,8 +99,6 @@ internal AggregatorStore(
10599
this.tagsKeysInterestingCount = hs.Count;
106100
}
107101

108-
this.EmitOverflowAttribute = emitOverflowAttribute;
109-
110102
this.exemplarFilter = exemplarFilter ?? DefaultExemplarFilter;
111103
Debug.Assert(
112104
this.exemplarFilter == ExemplarFilterType.AlwaysOff
@@ -245,17 +237,14 @@ internal void SnapshotDeltaWithMetricPointReclaim()
245237
this.batchSize++;
246238
}
247239

248-
if (this.EmitOverflowAttribute)
240+
// TakeSnapshot for the MetricPoint for overflow
241+
ref var metricPointForOverflow = ref this.metricPoints[1];
242+
if (metricPointForOverflow.MetricPointStatus != MetricPointStatus.NoCollectPending)
249243
{
250-
// TakeSnapshot for the MetricPoint for overflow
251-
ref var metricPointForOverflow = ref this.metricPoints[1];
252-
if (metricPointForOverflow.MetricPointStatus != MetricPointStatus.NoCollectPending)
253-
{
254-
this.TakeMetricPointSnapshot(ref metricPointForOverflow, outputDelta: true);
244+
this.TakeMetricPointSnapshot(ref metricPointForOverflow, outputDelta: true);
255245

256-
this.currentMetricPointBatch[this.batchSize] = 1;
257-
this.batchSize++;
258-
}
246+
this.currentMetricPointBatch[this.batchSize] = 1;
247+
this.batchSize++;
259248
}
260249

261250
// Index 0 and 1 are reserved for no tags and overflow
@@ -994,16 +983,8 @@ private void UpdateLongMetricPoint(int metricPointIndex, long value, ReadOnlySpa
994983
if (metricPointIndex < 0)
995984
{
996985
Interlocked.Increment(ref this.DroppedMeasurements);
997-
998-
if (this.EmitOverflowAttribute)
999-
{
1000-
this.InitializeOverflowTagPointIfNotInitialized();
1001-
this.metricPoints[1].Update(value);
1002-
}
1003-
else if (Interlocked.CompareExchange(ref this.metricCapHitMessageLogged, 1, 0) == 0)
1004-
{
1005-
OpenTelemetrySdkEventSource.Log.MeasurementDropped(this.name, this.metricPointCapHitMessage, MetricPointCapHitFixMessage);
1006-
}
986+
this.InitializeOverflowTagPointIfNotInitialized();
987+
this.metricPoints[1].Update(value);
1007988

1008989
return;
1009990
}
@@ -1049,16 +1030,8 @@ private void UpdateDoubleMetricPoint(int metricPointIndex, double value, ReadOnl
10491030
if (metricPointIndex < 0)
10501031
{
10511032
Interlocked.Increment(ref this.DroppedMeasurements);
1052-
1053-
if (this.EmitOverflowAttribute)
1054-
{
1055-
this.InitializeOverflowTagPointIfNotInitialized();
1056-
this.metricPoints[1].Update(value);
1057-
}
1058-
else if (Interlocked.CompareExchange(ref this.metricCapHitMessageLogged, 1, 0) == 0)
1059-
{
1060-
OpenTelemetrySdkEventSource.Log.MeasurementDropped(this.name, this.metricPointCapHitMessage, MetricPointCapHitFixMessage);
1061-
}
1033+
this.InitializeOverflowTagPointIfNotInitialized();
1034+
this.metricPoints[1].Update(value);
10621035

10631036
return;
10641037
}

src/OpenTelemetry/Metrics/MeterProviderSdk.cs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ namespace OpenTelemetry.Metrics;
1313

1414
internal sealed class MeterProviderSdk : MeterProvider
1515
{
16-
internal const string EmitOverFlowAttributeConfigKey = "OTEL_DOTNET_EXPERIMENTAL_METRICS_EMIT_OVERFLOW_ATTRIBUTE";
1716
internal const string ReclaimUnusedMetricPointsConfigKey = "OTEL_DOTNET_EXPERIMENTAL_METRICS_RECLAIM_UNUSED_METRIC_POINTS";
1817
internal const string ExemplarFilterConfigKey = "OTEL_METRICS_EXEMPLAR_FILTER";
1918
internal const string ExemplarFilterHistogramsConfigKey = "OTEL_DOTNET_EXPERIMENTAL_METRICS_EXEMPLAR_FILTER_HISTOGRAMS";
@@ -22,7 +21,6 @@ internal sealed class MeterProviderSdk : MeterProvider
2221
internal readonly IDisposable? OwnedServiceProvider;
2322
internal int ShutdownCount;
2423
internal bool Disposed;
25-
internal bool EmitOverflowAttribute;
2624
internal bool ReclaimUnusedMetricPoints;
2725
internal ExemplarFilterType? ExemplarFilter;
2826
internal ExemplarFilterType? ExemplarFilterForHistograms;
@@ -75,7 +73,7 @@ internal MeterProviderSdk(
7573
this.viewConfigs = state.ViewConfigs;
7674

7775
OpenTelemetrySdkEventSource.Log.MeterProviderSdkEvent(
78-
$"MeterProvider configuration: {{MetricLimit={state.MetricLimit}, CardinalityLimit={state.CardinalityLimit}, EmitOverflowAttribute={this.EmitOverflowAttribute}, ReclaimUnusedMetricPoints={this.ReclaimUnusedMetricPoints}, ExemplarFilter={this.ExemplarFilter}, ExemplarFilterForHistograms={this.ExemplarFilterForHistograms}}}.");
76+
$"MeterProvider configuration: {{MetricLimit={state.MetricLimit}, CardinalityLimit={state.CardinalityLimit}, ReclaimUnusedMetricPoints={this.ReclaimUnusedMetricPoints}, ExemplarFilter={this.ExemplarFilter}, ExemplarFilterForHistograms={this.ExemplarFilterForHistograms}}}.");
7977

8078
foreach (var reader in state.Readers)
8179
{
@@ -86,7 +84,6 @@ internal MeterProviderSdk(
8684
reader.ApplyParentProviderSettings(
8785
state.MetricLimit,
8886
state.CardinalityLimit,
89-
this.EmitOverflowAttribute,
9087
this.ReclaimUnusedMetricPoints,
9188
this.ExemplarFilter,
9289
this.ExemplarFilterForHistograms);
@@ -486,11 +483,6 @@ protected override void Dispose(bool disposing)
486483

487484
private void ApplySpecificationConfigurationKeys(IConfiguration configuration)
488485
{
489-
if (configuration.TryGetBoolValue(OpenTelemetrySdkEventSource.Log, EmitOverFlowAttributeConfigKey, out this.EmitOverflowAttribute))
490-
{
491-
OpenTelemetrySdkEventSource.Log.MeterProviderSdkEvent("Overflow attribute feature enabled via configuration.");
492-
}
493-
494486
if (configuration.TryGetBoolValue(OpenTelemetrySdkEventSource.Log, ReclaimUnusedMetricPointsConfigKey, out this.ReclaimUnusedMetricPoints))
495487
{
496488
OpenTelemetrySdkEventSource.Log.MeterProviderSdkEvent("Reclaim unused metric point feature enabled via configuration.");

src/OpenTelemetry/Metrics/Metric.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ internal Metric(
7070
MetricStreamIdentity instrumentIdentity,
7171
AggregationTemporality temporality,
7272
int cardinalityLimit,
73-
bool emitOverflowAttribute,
7473
bool shouldReclaimUnusedMetricPoints,
7574
ExemplarFilterType? exemplarFilter = null,
7675
Func<ExemplarReservoir?>? exemplarReservoirFactory = null)
@@ -193,7 +192,6 @@ internal Metric(
193192
aggType,
194193
temporality,
195194
cardinalityLimit,
196-
emitOverflowAttribute,
197195
shouldReclaimUnusedMetricPoints,
198196
exemplarFilter,
199197
exemplarReservoirFactory);

src/OpenTelemetry/Metrics/Reader/MetricReaderExt.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ public abstract partial class MetricReader
2222
private Metric?[]? metrics;
2323
private Metric[]? metricsCurrentBatch;
2424
private int metricIndex = -1;
25-
private bool emitOverflowAttribute;
2625
private bool reclaimUnusedMetricPoints;
2726
private ExemplarFilterType? exemplarFilter;
2827
private ExemplarFilterType? exemplarFilterForHistograms;
@@ -82,7 +81,6 @@ internal virtual List<Metric> AddMetricWithNoViews(Instrument instrument)
8281
metricStreamIdentity,
8382
this.GetAggregationTemporality(metricStreamIdentity.InstrumentType),
8483
this.cardinalityLimit,
85-
this.emitOverflowAttribute,
8684
this.reclaimUnusedMetricPoints,
8785
exemplarFilter);
8886
}
@@ -164,7 +162,6 @@ internal virtual List<Metric> AddMetricWithViews(Instrument instrument, List<Met
164162
metricStreamIdentity,
165163
this.GetAggregationTemporality(metricStreamIdentity.InstrumentType),
166164
metricStreamConfig?.CardinalityLimit ?? this.cardinalityLimit,
167-
this.emitOverflowAttribute,
168165
this.reclaimUnusedMetricPoints,
169166
exemplarFilter,
170167
metricStreamConfig?.ExemplarReservoirFactory);
@@ -184,7 +181,6 @@ internal virtual List<Metric> AddMetricWithViews(Instrument instrument, List<Met
184181
internal void ApplyParentProviderSettings(
185182
int metricLimit,
186183
int cardinalityLimit,
187-
bool emitOverflowAttribute,
188184
bool reclaimUnusedMetricPoints,
189185
ExemplarFilterType? exemplarFilter,
190186
ExemplarFilterType? exemplarFilterForHistograms)
@@ -193,7 +189,6 @@ internal void ApplyParentProviderSettings(
193189
this.metrics = new Metric[metricLimit];
194190
this.metricsCurrentBatch = new Metric[metricLimit];
195191
this.cardinalityLimit = cardinalityLimit;
196-
this.emitOverflowAttribute = emitOverflowAttribute;
197192
this.reclaimUnusedMetricPoints = reclaimUnusedMetricPoints;
198193
this.exemplarFilter = exemplarFilter;
199194
this.exemplarFilterForHistograms = exemplarFilterForHistograms;

test/OpenTelemetry.Tests/Metrics/AggregatorTestsBase.cs

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,14 @@ public abstract class AggregatorTestsBase
1616
private static readonly ExplicitBucketHistogramConfiguration HistogramConfiguration = new() { Boundaries = Metric.DefaultHistogramBounds };
1717
private static readonly MetricStreamIdentity MetricStreamIdentity = new(Instrument, HistogramConfiguration);
1818

19-
private readonly bool emitOverflowAttribute;
2019
private readonly bool shouldReclaimUnusedMetricPoints;
2120
private readonly AggregatorStore aggregatorStore;
2221

23-
protected AggregatorTestsBase(bool emitOverflowAttribute, bool shouldReclaimUnusedMetricPoints)
22+
protected AggregatorTestsBase(bool shouldReclaimUnusedMetricPoints)
2423
{
25-
this.emitOverflowAttribute = emitOverflowAttribute;
2624
this.shouldReclaimUnusedMetricPoints = shouldReclaimUnusedMetricPoints;
2725

28-
this.aggregatorStore = new(MetricStreamIdentity, AggregationType.HistogramWithBuckets, AggregationTemporality.Cumulative, 1024, emitOverflowAttribute, this.shouldReclaimUnusedMetricPoints);
26+
this.aggregatorStore = new(MetricStreamIdentity, AggregationType.HistogramWithBuckets, AggregationTemporality.Cumulative, 1024, this.shouldReclaimUnusedMetricPoints);
2927
}
3028

3129
[Fact]
@@ -253,7 +251,6 @@ public void HistogramBucketsDefaultUpdatesForSecondsTest(string meterName, strin
253251
AggregationType.Histogram,
254252
AggregationTemporality.Cumulative,
255253
cardinalityLimit: 1024,
256-
this.emitOverflowAttribute,
257254
this.shouldReclaimUnusedMetricPoints);
258255

259256
KnownHistogramBuckets actualHistogramBounds = KnownHistogramBuckets.Default;
@@ -330,7 +327,6 @@ internal void ExponentialHistogramTests(AggregationType aggregationType, Aggrega
330327
aggregationType,
331328
aggregationTemporality,
332329
cardinalityLimit: 1024,
333-
this.emitOverflowAttribute,
334330
this.shouldReclaimUnusedMetricPoints,
335331
exemplarsEnabled ? ExemplarFilterType.AlwaysOn : null);
336332

@@ -440,7 +436,6 @@ internal void ExponentialMaxScaleConfigWorks(int? maxScale)
440436
AggregationType.Base2ExponentialHistogram,
441437
AggregationTemporality.Cumulative,
442438
cardinalityLimit: 1024,
443-
this.emitOverflowAttribute,
444439
this.shouldReclaimUnusedMetricPoints);
445440

446441
aggregatorStore.Update(10, Array.Empty<KeyValuePair<string, object?>>());
@@ -525,31 +520,15 @@ public ThreadArguments(MetricPoint histogramPoint, ManualResetEvent mreToEnsureA
525520
public class AggregatorTests : AggregatorTestsBase
526521
{
527522
public AggregatorTests()
528-
: base(emitOverflowAttribute: false, shouldReclaimUnusedMetricPoints: false)
529-
{
530-
}
531-
}
532-
533-
public class AggregatorTestsWithOverflowAttribute : AggregatorTestsBase
534-
{
535-
public AggregatorTestsWithOverflowAttribute()
536-
: base(emitOverflowAttribute: true, shouldReclaimUnusedMetricPoints: false)
523+
: base(shouldReclaimUnusedMetricPoints: false)
537524
{
538525
}
539526
}
540527

541528
public class AggregatorTestsWithReclaimAttribute : AggregatorTestsBase
542529
{
543530
public AggregatorTestsWithReclaimAttribute()
544-
: base(emitOverflowAttribute: false, shouldReclaimUnusedMetricPoints: true)
545-
{
546-
}
547-
}
548-
549-
public class AggregatorTestsWithBothReclaimAndOverflowAttributes : AggregatorTestsBase
550-
{
551-
public AggregatorTestsWithBothReclaimAndOverflowAttributes()
552-
: base(emitOverflowAttribute: true, shouldReclaimUnusedMetricPoints: true)
531+
: base(shouldReclaimUnusedMetricPoints: true)
553532
{
554533
}
555534
}

0 commit comments

Comments
 (0)