Add exponential histogram MaxScale configuration (#4327)
This commit is contained in:
parent
3d1c489998
commit
094124e9ee
|
|
@ -39,6 +39,7 @@ namespace OpenTelemetry.Metrics
|
|||
private readonly AggregationType aggType;
|
||||
private readonly double[] histogramBounds;
|
||||
private readonly int exponentialHistogramMaxSize;
|
||||
private readonly int exponentialHistogramMaxScale;
|
||||
private readonly UpdateLongDelegate updateLongCallback;
|
||||
private readonly UpdateDoubleDelegate updateDoubleCallback;
|
||||
private readonly int maxMetricPoints;
|
||||
|
|
@ -64,6 +65,7 @@ namespace OpenTelemetry.Metrics
|
|||
this.outputDelta = temporality == AggregationTemporality.Delta;
|
||||
this.histogramBounds = metricStreamIdentity.HistogramBucketBounds ?? Metric.DefaultHistogramBounds;
|
||||
this.exponentialHistogramMaxSize = metricStreamIdentity.ExponentialHistogramMaxSize;
|
||||
this.exponentialHistogramMaxScale = metricStreamIdentity.ExponentialHistogramMaxScale;
|
||||
this.StartTimeExclusive = DateTimeOffset.UtcNow;
|
||||
this.exemplarFilter = exemplarFilter ?? new AlwaysOffExemplarFilter();
|
||||
if (metricStreamIdentity.TagKeys == null)
|
||||
|
|
@ -188,7 +190,7 @@ namespace OpenTelemetry.Metrics
|
|||
{
|
||||
if (!this.zeroTagMetricPointInitialized)
|
||||
{
|
||||
this.metricPoints[0] = new MetricPoint(this, this.aggType, null, this.histogramBounds, this.exponentialHistogramMaxSize);
|
||||
this.metricPoints[0] = new MetricPoint(this, this.aggType, null, this.histogramBounds, this.exponentialHistogramMaxSize, this.exponentialHistogramMaxScale);
|
||||
this.zeroTagMetricPointInitialized = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -255,7 +257,7 @@ namespace OpenTelemetry.Metrics
|
|||
}
|
||||
|
||||
ref var metricPoint = ref this.metricPoints[aggregatorIndex];
|
||||
metricPoint = new MetricPoint(this, this.aggType, sortedTags.KeyValuePairs, this.histogramBounds, this.exponentialHistogramMaxSize);
|
||||
metricPoint = new MetricPoint(this, this.aggType, sortedTags.KeyValuePairs, this.histogramBounds, this.exponentialHistogramMaxSize, this.exponentialHistogramMaxScale);
|
||||
|
||||
// Add to dictionary *after* initializing MetricPoint
|
||||
// as other threads can start writing to the
|
||||
|
|
@ -304,7 +306,7 @@ namespace OpenTelemetry.Metrics
|
|||
}
|
||||
|
||||
ref var metricPoint = ref this.metricPoints[aggregatorIndex];
|
||||
metricPoint = new MetricPoint(this, this.aggType, givenTags.KeyValuePairs, this.histogramBounds, this.exponentialHistogramMaxSize);
|
||||
metricPoint = new MetricPoint(this, this.aggType, givenTags.KeyValuePairs, this.histogramBounds, this.exponentialHistogramMaxSize, this.exponentialHistogramMaxScale);
|
||||
|
||||
// Add to dictionary *after* initializing MetricPoint
|
||||
// as other threads can start writing to the
|
||||
|
|
|
|||
|
|
@ -49,12 +49,10 @@ internal sealed class Base2ExponentialBucketHistogram
|
|||
/// <param name="maxBuckets">
|
||||
/// The maximum number of buckets in each of the positive and negative ranges, not counting the special zero bucket. The default value is 160.
|
||||
/// </param>
|
||||
public Base2ExponentialBucketHistogram(int maxBuckets = 160)
|
||||
: this(maxBuckets, 20)
|
||||
{
|
||||
}
|
||||
|
||||
internal Base2ExponentialBucketHistogram(int maxBuckets, int scale)
|
||||
/// <param name="scale">
|
||||
/// Maximum scale factor. The default value is 20.
|
||||
/// </param>
|
||||
public Base2ExponentialBucketHistogram(int maxBuckets = 160, int scale = 20)
|
||||
{
|
||||
/*
|
||||
The following table is calculated based on [ MapToIndex(double.Epsilon), MapToIndex(double.MaxValue) ]:
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ namespace OpenTelemetry.Metrics;
|
|||
/// </summary>
|
||||
internal sealed class Base2ExponentialBucketHistogramConfiguration : HistogramConfiguration
|
||||
{
|
||||
private int maxSize = 160;
|
||||
private int maxSize = Metric.DefaultExponentialHistogramMaxBuckets;
|
||||
private int maxScale = Metric.DefaultExponentialHistogramMaxScale;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of buckets in each of the positive and negative ranges, not counting the special zero bucket.
|
||||
|
|
@ -46,4 +47,29 @@ internal sealed class Base2ExponentialBucketHistogramConfiguration : HistogramCo
|
|||
this.maxSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum scale factor used to determine the resolution of bucket boundaries.
|
||||
/// The higher the scale the higher the resolution.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The default value is 20. The minimum size is -11. The maximum size is 20.
|
||||
/// </remarks>
|
||||
public int MaxScale
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.maxScale;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value < -11 || value > 20)
|
||||
{
|
||||
throw new ArgumentException($"Histogram max scale is invalid. Max scale must be in the range [-11, 20].", nameof(value));
|
||||
}
|
||||
|
||||
this.maxScale = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ namespace OpenTelemetry.Metrics
|
|||
{
|
||||
internal const int DefaultExponentialHistogramMaxBuckets = 160;
|
||||
|
||||
internal const int DefaultExponentialHistogramMaxScale = 20;
|
||||
|
||||
internal static readonly double[] DefaultHistogramBounds = new double[] { 0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000 };
|
||||
|
||||
private readonly AggregatorStore aggStore;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ namespace OpenTelemetry.Metrics
|
|||
AggregationType aggType,
|
||||
KeyValuePair<string, object>[] tagKeysAndValues,
|
||||
double[] histogramExplicitBounds,
|
||||
int exponentialHistogramMaxSize)
|
||||
int exponentialHistogramMaxSize,
|
||||
int exponentialHistogramMaxScale)
|
||||
{
|
||||
Debug.Assert(aggregatorStore != null, "AggregatorStore was null.");
|
||||
Debug.Assert(histogramExplicitBounds != null, "Histogram explicit Bounds was null.");
|
||||
|
|
@ -81,7 +82,7 @@ namespace OpenTelemetry.Metrics
|
|||
this.aggType == AggregationType.Base2ExponentialHistogramWithMinMax)
|
||||
{
|
||||
this.mpComponents = new MetricPointOptionalComponents();
|
||||
this.mpComponents.Base2ExponentialBucketHistogram = new Base2ExponentialBucketHistogram(exponentialHistogramMaxSize);
|
||||
this.mpComponents.Base2ExponentialBucketHistogram = new Base2ExponentialBucketHistogram(exponentialHistogramMaxSize, exponentialHistogramMaxScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ namespace OpenTelemetry.Metrics
|
|||
this.TagKeys = metricStreamConfiguration?.CopiedTagKeys;
|
||||
this.HistogramBucketBounds = (metricStreamConfiguration as ExplicitBucketHistogramConfiguration)?.CopiedBoundaries;
|
||||
this.ExponentialHistogramMaxSize = (metricStreamConfiguration as Base2ExponentialBucketHistogramConfiguration)?.MaxSize ?? 0;
|
||||
this.ExponentialHistogramMaxScale = (metricStreamConfiguration as Base2ExponentialBucketHistogramConfiguration)?.MaxScale ?? 0;
|
||||
this.HistogramRecordMinMax = (metricStreamConfiguration as HistogramConfiguration)?.RecordMinMax ?? true;
|
||||
|
||||
#if NETSTANDARD2_1 || NET6_0_OR_GREATER
|
||||
|
|
@ -50,6 +51,7 @@ namespace OpenTelemetry.Metrics
|
|||
hashCode.Add(this.ViewId);
|
||||
hashCode.Add(this.TagKeys, StringArrayComparer);
|
||||
hashCode.Add(this.ExponentialHistogramMaxSize);
|
||||
hashCode.Add(this.ExponentialHistogramMaxScale);
|
||||
if (this.HistogramBucketBounds != null)
|
||||
{
|
||||
for (var i = 0; i < this.HistogramBucketBounds.Length; ++i)
|
||||
|
|
@ -69,6 +71,7 @@ namespace OpenTelemetry.Metrics
|
|||
hash = (hash * 31) + this.InstrumentName.GetHashCode();
|
||||
hash = (hash * 31) + this.HistogramRecordMinMax.GetHashCode();
|
||||
hash = (hash * 31) + this.ExponentialHistogramMaxSize.GetHashCode();
|
||||
hash = (hash * 31) + this.ExponentialHistogramMaxScale.GetHashCode();
|
||||
hash = (hash * 31) + (this.Unit?.GetHashCode() ?? 0);
|
||||
hash = (hash * 31) + (this.Description?.GetHashCode() ?? 0);
|
||||
hash = (hash * 31) + (this.ViewId ?? 0);
|
||||
|
|
@ -109,6 +112,8 @@ namespace OpenTelemetry.Metrics
|
|||
|
||||
public int ExponentialHistogramMaxSize { get; }
|
||||
|
||||
public int ExponentialHistogramMaxScale { get; }
|
||||
|
||||
public bool HistogramRecordMinMax { get; }
|
||||
|
||||
public static bool operator ==(MetricStreamIdentity metricIdentity1, MetricStreamIdentity metricIdentity2) => metricIdentity1.Equals(metricIdentity2);
|
||||
|
|
@ -131,6 +136,7 @@ namespace OpenTelemetry.Metrics
|
|||
&& this.ViewId == other.ViewId
|
||||
&& this.HistogramRecordMinMax == other.HistogramRecordMinMax
|
||||
&& this.ExponentialHistogramMaxSize == other.ExponentialHistogramMaxSize
|
||||
&& this.ExponentialHistogramMaxScale == other.ExponentialHistogramMaxScale
|
||||
&& StringArrayComparer.Equals(this.TagKeys, other.TagKeys)
|
||||
&& HistogramBoundsEqual(this.HistogramBucketBounds, other.HistogramBucketBounds);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace OpenTelemetry.Metrics.Tests
|
|||
[Fact]
|
||||
public void HistogramDistributeToAllBucketsDefault()
|
||||
{
|
||||
var histogramPoint = new MetricPoint(this.aggregatorStore, AggregationType.HistogramWithBuckets, null, Metric.DefaultHistogramBounds, Metric.DefaultExponentialHistogramMaxBuckets);
|
||||
var histogramPoint = new MetricPoint(this.aggregatorStore, AggregationType.HistogramWithBuckets, null, Metric.DefaultHistogramBounds, Metric.DefaultExponentialHistogramMaxBuckets, Metric.DefaultExponentialHistogramMaxScale);
|
||||
histogramPoint.Update(-1);
|
||||
histogramPoint.Update(0);
|
||||
histogramPoint.Update(2);
|
||||
|
|
@ -81,7 +81,7 @@ namespace OpenTelemetry.Metrics.Tests
|
|||
public void HistogramDistributeToAllBucketsCustom()
|
||||
{
|
||||
var boundaries = new double[] { 10, 20 };
|
||||
var histogramPoint = new MetricPoint(this.aggregatorStore, AggregationType.HistogramWithBuckets, null, boundaries, Metric.DefaultExponentialHistogramMaxBuckets);
|
||||
var histogramPoint = new MetricPoint(this.aggregatorStore, AggregationType.HistogramWithBuckets, null, boundaries, Metric.DefaultExponentialHistogramMaxBuckets, Metric.DefaultExponentialHistogramMaxScale);
|
||||
|
||||
// 5 recordings <=10
|
||||
histogramPoint.Update(-10);
|
||||
|
|
@ -129,7 +129,7 @@ namespace OpenTelemetry.Metrics.Tests
|
|||
boundaries[i] = i;
|
||||
}
|
||||
|
||||
var histogramPoint = new MetricPoint(this.aggregatorStore, AggregationType.HistogramWithBuckets, null, boundaries, Metric.DefaultExponentialHistogramMaxBuckets);
|
||||
var histogramPoint = new MetricPoint(this.aggregatorStore, AggregationType.HistogramWithBuckets, null, boundaries, Metric.DefaultExponentialHistogramMaxBuckets, Metric.DefaultExponentialHistogramMaxScale);
|
||||
|
||||
// Act
|
||||
histogramPoint.Update(-1);
|
||||
|
|
@ -162,7 +162,7 @@ namespace OpenTelemetry.Metrics.Tests
|
|||
public void HistogramWithOnlySumCount()
|
||||
{
|
||||
var boundaries = Array.Empty<double>();
|
||||
var histogramPoint = new MetricPoint(this.aggregatorStore, AggregationType.Histogram, null, boundaries, Metric.DefaultExponentialHistogramMaxBuckets);
|
||||
var histogramPoint = new MetricPoint(this.aggregatorStore, AggregationType.Histogram, null, boundaries, Metric.DefaultExponentialHistogramMaxBuckets, Metric.DefaultExponentialHistogramMaxScale);
|
||||
|
||||
histogramPoint.Update(-10);
|
||||
histogramPoint.Update(0);
|
||||
|
|
@ -331,5 +331,46 @@ namespace OpenTelemetry.Metrics.Tests
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-5)]
|
||||
[InlineData(0)]
|
||||
[InlineData(5)]
|
||||
[InlineData(null)]
|
||||
internal void ExponentialMaxScaleConfigWorks(int? maxScale)
|
||||
{
|
||||
var streamConfiguration = new Base2ExponentialBucketHistogramConfiguration();
|
||||
if (maxScale.HasValue)
|
||||
{
|
||||
streamConfiguration.MaxScale = maxScale.Value;
|
||||
}
|
||||
|
||||
var metricStreamIdentity = new MetricStreamIdentity(Instrument, streamConfiguration);
|
||||
|
||||
var aggregatorStore = new AggregatorStore(
|
||||
metricStreamIdentity,
|
||||
AggregationType.Base2ExponentialHistogram,
|
||||
AggregationTemporality.Cumulative,
|
||||
maxMetricPoints: 1024);
|
||||
|
||||
aggregatorStore.Update(10, Array.Empty<KeyValuePair<string, object>>());
|
||||
|
||||
aggregatorStore.Snapshot();
|
||||
|
||||
var metricPoints = new List<MetricPoint>();
|
||||
|
||||
foreach (ref readonly var mp in aggregatorStore.GetMetricPoints())
|
||||
{
|
||||
metricPoints.Add(mp);
|
||||
}
|
||||
|
||||
Assert.Single(metricPoints);
|
||||
var metricPoint = metricPoints[0];
|
||||
|
||||
// After a single measurement there will not have been a scale down.
|
||||
// Scale will equal MaxScale.
|
||||
var expectedScale = maxScale.HasValue ? maxScale : Metric.DefaultExponentialHistogramMaxScale;
|
||||
Assert.Equal(expectedScale, metricPoint.GetExponentialHistogramData().Scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ namespace OpenTelemetry.Metrics.Tests
|
|||
[InlineData(-1)]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
public void AddViewWithInvalidExponentialHistogramConfigThrowsArgumentException(int maxSize)
|
||||
public void AddViewWithInvalidExponentialHistogramMaxSizeConfigThrowsArgumentException(int maxSize)
|
||||
{
|
||||
var ex = Assert.Throws<ArgumentException>(() => Sdk.CreateMeterProviderBuilder()
|
||||
.AddView("name1", new Base2ExponentialBucketHistogramConfiguration { MaxSize = maxSize }));
|
||||
|
|
@ -216,6 +216,17 @@ namespace OpenTelemetry.Metrics.Tests
|
|||
Assert.Contains("Histogram max size is invalid", ex.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-12)]
|
||||
[InlineData(21)]
|
||||
public void AddViewWithInvalidExponentialHistogramMaxScaleConfigThrowsArgumentException(int maxScale)
|
||||
{
|
||||
var ex = Assert.Throws<ArgumentException>(() => Sdk.CreateMeterProviderBuilder()
|
||||
.AddView("name1", new Base2ExponentialBucketHistogramConfiguration { MaxScale = maxScale }));
|
||||
|
||||
Assert.Contains("Histogram max scale is invalid", ex.Message);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(MetricTestData.InvalidHistogramBoundaries), MemberType = typeof(MetricTestData))]
|
||||
public void AddViewWithInvalidHistogramBoundsIgnored(double[] boundaries)
|
||||
|
|
|
|||
Loading…
Reference in New Issue