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

Add opt-in support for metric overflow attribute #4737

78 changes: 66 additions & 12 deletions src/OpenTelemetry/Metrics/AggregatorStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ internal sealed class AggregatorStore
{
private static readonly string MetricPointCapHitFixMessage = "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.";
private static readonly Comparison<KeyValuePair<string, object>> DimensionComparisonDelegate = (x, y) => x.Key.CompareTo(y.Key);
private static bool emitOverflowAttribute;

private readonly object lockZeroTags = new();
private readonly HashSet<string> tagKeysInteresting;
private readonly int tagsKeysInterestingCount;
Expand All @@ -44,6 +46,7 @@ internal sealed class AggregatorStore
private readonly UpdateDoubleDelegate updateDoubleCallback;
private readonly int maxMetricPoints;
private readonly ExemplarFilter exemplarFilter;

private int metricPointIndex = 0;
private int batchSize = 0;
private int metricCapHitMessageLogged;
Expand Down Expand Up @@ -81,6 +84,25 @@ internal sealed class AggregatorStore
this.tagKeysInteresting = hs;
this.tagsKeysInterestingCount = hs.Count;
}

// If the switch is not set at all or if it's explicitly set to false
if (AppContext.TryGetSwitch("OTel.Dotnet.EmitMetricOverflowAttribute", out bool shouldEmitOverflowAttribute) == false ||
utpilla marked this conversation as resolved.
Show resolved Hide resolved
utpilla marked this conversation as resolved.
Show resolved Hide resolved
shouldEmitOverflowAttribute == false)
{
emitOverflowAttribute = false;
}
else if (this.maxMetricPoints > 1)
{
// We need at least two metric points. One is reserved for zero tags and the other one for overflow attribute

emitOverflowAttribute = true;

// Setting this to as we would reserve the metricPoints[1] for overflow attribute.
// Newer attributes should be added starting at the index: 2
this.metricPointIndex = 1;

this.metricPoints[1] = new MetricPoint(this, this.aggType, new KeyValuePair<string, object>[] { new("otel.metric.overflow", true) }, this.histogramBounds, this.exponentialHistogramMaxSize, this.exponentialHistogramMaxScale);
utpilla marked this conversation as resolved.
Show resolved Hide resolved
}
}

private delegate void UpdateLongDelegate(long value, ReadOnlySpan<KeyValuePair<string, object>> tags);
Expand Down Expand Up @@ -329,12 +351,20 @@ private void UpdateLong(long value, ReadOnlySpan<KeyValuePair<string, object>> t
var index = this.FindMetricAggregatorsDefault(tags);
if (index < 0)
{
if (Interlocked.CompareExchange(ref this.metricCapHitMessageLogged, 1, 0) == 0)
if (emitOverflowAttribute)
cijothomas marked this conversation as resolved.
Show resolved Hide resolved
{
OpenTelemetrySdkEventSource.Log.MeasurementDropped(this.name, this.metricPointCapHitMessage, MetricPointCapHitFixMessage);
this.metricPoints[1].Update(value);
return;
}
else
{
if (Interlocked.CompareExchange(ref this.metricCapHitMessageLogged, 1, 0) == 0)
{
OpenTelemetrySdkEventSource.Log.MeasurementDropped(this.name, this.metricPointCapHitMessage, MetricPointCapHitFixMessage);
utpilla marked this conversation as resolved.
Show resolved Hide resolved
}

return;
return;
}
}

// TODO: can special case built-in filters to be bit faster.
Expand All @@ -361,12 +391,20 @@ private void UpdateLongCustomTags(long value, ReadOnlySpan<KeyValuePair<string,
var index = this.FindMetricAggregatorsCustomTag(tags);
if (index < 0)
{
if (Interlocked.CompareExchange(ref this.metricCapHitMessageLogged, 1, 0) == 0)
if (emitOverflowAttribute)
{
OpenTelemetrySdkEventSource.Log.MeasurementDropped(this.name, this.metricPointCapHitMessage, MetricPointCapHitFixMessage);
this.metricPoints[1].Update(value);
return;
}
else
{
if (Interlocked.CompareExchange(ref this.metricCapHitMessageLogged, 1, 0) == 0)
{
OpenTelemetrySdkEventSource.Log.MeasurementDropped(this.name, this.metricPointCapHitMessage, MetricPointCapHitFixMessage);
}

return;
return;
}
}

// TODO: can special case built-in filters to be bit faster.
Expand All @@ -393,12 +431,20 @@ private void UpdateDouble(double value, ReadOnlySpan<KeyValuePair<string, object
var index = this.FindMetricAggregatorsDefault(tags);
if (index < 0)
{
if (Interlocked.CompareExchange(ref this.metricCapHitMessageLogged, 1, 0) == 0)
if (emitOverflowAttribute)
{
OpenTelemetrySdkEventSource.Log.MeasurementDropped(this.name, this.metricPointCapHitMessage, MetricPointCapHitFixMessage);
this.metricPoints[1].Update(value);
return;
}
else
{
if (Interlocked.CompareExchange(ref this.metricCapHitMessageLogged, 1, 0) == 0)
{
OpenTelemetrySdkEventSource.Log.MeasurementDropped(this.name, this.metricPointCapHitMessage, MetricPointCapHitFixMessage);
}

return;
return;
}
}

// TODO: can special case built-in filters to be bit faster.
Expand All @@ -425,12 +471,20 @@ private void UpdateDoubleCustomTags(double value, ReadOnlySpan<KeyValuePair<stri
var index = this.FindMetricAggregatorsCustomTag(tags);
if (index < 0)
{
if (Interlocked.CompareExchange(ref this.metricCapHitMessageLogged, 1, 0) == 0)
if (emitOverflowAttribute)
{
OpenTelemetrySdkEventSource.Log.MeasurementDropped(this.name, this.metricPointCapHitMessage, MetricPointCapHitFixMessage);
this.metricPoints[1].Update(value);
return;
}
else
{
if (Interlocked.CompareExchange(ref this.metricCapHitMessageLogged, 1, 0) == 0)
{
OpenTelemetrySdkEventSource.Log.MeasurementDropped(this.name, this.metricPointCapHitMessage, MetricPointCapHitFixMessage);
}

return;
return;
}
}

// TODO: can special case built-in filters to be bit faster.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="AggregatorTest.cs" company="OpenTelemetry Authors">
// <copyright file="AggregatorTestBase.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -19,13 +19,27 @@

namespace OpenTelemetry.Metrics.Tests;

public class AggregatorTest
#pragma warning disable SA1402

public abstract class AggregatorTestBase : IDisposable
{
private static readonly Meter Meter = new("testMeter");
private static readonly Instrument Instrument = Meter.CreateHistogram<long>("testInstrument");
private static readonly ExplicitBucketHistogramConfiguration HistogramConfiguration = new() { Boundaries = Metric.DefaultHistogramBounds };
private static readonly MetricStreamIdentity MetricStreamIdentity = new(Instrument, HistogramConfiguration);

private readonly AggregatorStore aggregatorStore = new(MetricStreamIdentity, AggregationType.HistogramWithBuckets, AggregationTemporality.Cumulative, 1024);
private readonly bool emitOverflowAttribute;

protected AggregatorTestBase(bool emitOverflowAttribute)
{
this.emitOverflowAttribute = emitOverflowAttribute;

if (emitOverflowAttribute)
{
AppContext.SetSwitch("OTel.Dotnet.EmitMetricOverflowAttribute", true);
}
}

[Fact]
public void HistogramDistributeToAllBucketsDefault()
Expand Down Expand Up @@ -224,6 +238,11 @@ public void MultiThreadedHistogramUpdateAndSnapShotTest()
Assert.Equal(200, argsToThread.SumOfDelta + lastDelta);
}

public void Dispose()
{
AppContext.SetSwitch("OTel.Dotnet.EmitMetricOverflowAttribute", false);
}

internal static void AssertExponentialBucketsAreCorrect(Base2ExponentialBucketHistogram expectedHistogram, ExponentialHistogramData data)
{
Assert.Equal(expectedHistogram.Scale, data.Scale);
Expand Down Expand Up @@ -307,7 +326,15 @@ internal void ExponentialHistogramTests(AggregationType aggregationType, Aggrega
metricPoints.Add(mp);
}

Assert.Single(metricPoints);
if (this.emitOverflowAttribute && aggregationTemporality == AggregationTemporality.Cumulative)
{
Assert.Equal(2, metricPoints.Count);
}
else
{
Assert.Single(metricPoints);
}

var metricPoint = metricPoints[0];

var count = metricPoint.GetHistogramCount();
Expand Down Expand Up @@ -404,7 +431,15 @@ internal void ExponentialMaxScaleConfigWorks(int? maxScale)
metricPoints.Add(mp);
}

Assert.Single(metricPoints);
if (this.emitOverflowAttribute)
{
Assert.Equal(2, metricPoints.Count);
}
else
{
Assert.Single(metricPoints);
}

var metricPoint = metricPoints[0];

// After a single measurement there will not have been a scale down.
Expand Down Expand Up @@ -463,3 +498,19 @@ private class ThreadArguments
public double SumOfDelta;
}
}

public class AggregatorTests : AggregatorTestBase
{
public AggregatorTests()
: base(false)
{
}
}

public class AggregatorTestsWithOverflowAttribute : AggregatorTestBase
{
public AggregatorTestsWithOverflowAttribute()
: base(true)
{
}
}