diff --git a/src/OpenTelemetry/Metrics/AggregatorStore.cs b/src/OpenTelemetry/Metrics/AggregatorStore.cs index 489f3b63c29..69e68f4b6c1 100644 --- a/src/OpenTelemetry/Metrics/AggregatorStore.cs +++ b/src/OpenTelemetry/Metrics/AggregatorStore.cs @@ -69,7 +69,7 @@ internal AggregatorStore( this.currentMetricPointBatch = new int[maxMetricPoints]; this.aggType = aggType; this.outputDelta = temporality == AggregationTemporality.Delta; - this.histogramBounds = metricStreamIdentity.HistogramBucketBounds ?? Metric.DefaultHistogramBounds; + this.histogramBounds = metricStreamIdentity.HistogramBucketBounds ?? FindDefaultHistogramBounds(in metricStreamIdentity); this.exponentialHistogramMaxSize = metricStreamIdentity.ExponentialHistogramMaxSize; this.exponentialHistogramMaxScale = metricStreamIdentity.ExponentialHistogramMaxScale; this.StartTimeExclusive = DateTimeOffset.UtcNow; @@ -106,6 +106,8 @@ internal AggregatorStore( internal DateTimeOffset EndTimeInclusive { get; private set; } + internal double[] HistogramBounds => this.histogramBounds; + internal bool IsExemplarEnabled() { // Using this filter to indicate On/Off @@ -196,6 +198,17 @@ internal void SnapshotCumulative(int indexSnapshot) internal MetricPointsAccessor GetMetricPoints() => new(this.metricPoints, this.currentMetricPointBatch, this.batchSize); + private static double[] FindDefaultHistogramBounds(in MetricStreamIdentity metricStreamIdentity) + { + if (metricStreamIdentity.Unit == "s" && Metric.DefaultHistogramBoundMappings + .Contains((metricStreamIdentity.MeterName, metricStreamIdentity.InstrumentName))) + { + return Metric.DefaultHistogramBoundsSeconds; + } + + return Metric.DefaultHistogramBounds; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void InitializeZeroTagPointIfNotInitialized() { diff --git a/src/OpenTelemetry/Metrics/Metric.cs b/src/OpenTelemetry/Metrics/Metric.cs index cc42f96ffc2..8c1d819476a 100644 --- a/src/OpenTelemetry/Metrics/Metric.cs +++ b/src/OpenTelemetry/Metrics/Metric.cs @@ -28,6 +28,22 @@ public sealed class Metric 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 }; + internal static readonly double[] DefaultHistogramBoundsSeconds = new double[] { 0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 }; + internal static readonly HashSet<(string, string)> DefaultHistogramBoundMappings = new() + { + ("Microsoft.AspNetCore.Hosting", "http.server.request.duration"), + ("Microsoft.AspNetCore.Http.Connections", "signalr.server.connection.duration"), + ("Microsoft.AspNetCore.RateLimiting", "aspnetcore.rate_limiting.request.time_in_queue"), + ("Microsoft.AspNetCore.RateLimiting", "aspnetcore.rate_limiting.request_lease.duration"), + ("Microsoft.AspNetCore.Server.Kestrel", "kestrel.connection.duration"), + ("Microsoft.AspNetCore.Server.Kestrel", "kestrel.tls_handshake.duration"), + ("OpenTelemetry.Instrumentation.AspNetCore", "http.server.request.duration"), + ("OpenTelemetry.Instrumentation.Http", "http.client.request.duration"), + ("System.Net.Http", "http.client.connection.duration"), + ("System.Net.Http", "http.client.request.duration"), + ("System.Net.Http", "http.client.request.time_in_queue"), + ("System.Net.NameResolution", "dns.lookups.duration"), + }; private readonly AggregatorStore aggStore; diff --git a/test/OpenTelemetry.Tests/Metrics/AggregatorTestsBase.cs b/test/OpenTelemetry.Tests/Metrics/AggregatorTestsBase.cs index 969c6890a87..9e51d2ed09b 100644 --- a/test/OpenTelemetry.Tests/Metrics/AggregatorTestsBase.cs +++ b/test/OpenTelemetry.Tests/Metrics/AggregatorTestsBase.cs @@ -238,6 +238,50 @@ public void MultiThreadedHistogramUpdateAndSnapShotTest() Assert.Equal(200, argsToThread.SumOfDelta + lastDelta); } + [Theory] + [InlineData("Microsoft.AspNetCore.Hosting", "http.server.request.duration")] + [InlineData("Microsoft.AspNetCore.Http.Connections", "signalr.server.connection.duration")] + [InlineData("Microsoft.AspNetCore.RateLimiting", "aspnetcore.rate_limiting.request_lease.duration")] + [InlineData("Microsoft.AspNetCore.RateLimiting", "aspnetcore.rate_limiting.request.time_in_queue")] + [InlineData("Microsoft.AspNetCore.Server.Kestrel", "kestrel.connection.duration")] + [InlineData("Microsoft.AspNetCore.Server.Kestrel", "kestrel.tls_handshake.duration")] + [InlineData("OpenTelemetry.Instrumentation.AspNetCore", "http.server.duration")] + [InlineData("OpenTelemetry.Instrumentation.Http", "http.client.duration")] + [InlineData("System.Net.Http", "http.client.connection.duration")] + [InlineData("System.Net.Http", "http.client.request.duration")] + [InlineData("System.Net.Http", "http.client.request.time_in_queue")] + [InlineData("System.Net.NameResolution", "dns.lookups.duration")] + [InlineData("General.App", "simple.alternative.counter")] + public void HistogramBucketsDefaultUpdatesForSecondsTest(string meterName, string instrumentName) + { + RunTest(meterName, instrumentName, unit: "s"); + RunTest(meterName, instrumentName, unit: "ms"); + RunTest(meterName, instrumentName, unit: "By"); + RunTest(meterName, instrumentName, unit: null); + + void RunTest(string meterName, string instrumentName, string unit) + { + using var meter = new Meter(meterName); + + var instrument = meter.CreateHistogram(instrumentName, unit); + + var metricStreamIdentity = new MetricStreamIdentity(instrument, metricStreamConfiguration: null); + + AggregatorStore aggregatorStore = new( + metricStreamIdentity, + AggregationType.Histogram, + AggregationTemporality.Cumulative, + maxMetricPoints: 1024, + this.emitOverflowAttribute); + + Assert.NotNull(aggregatorStore.HistogramBounds); + Assert.Equal( + unit == "s" && Metric.DefaultHistogramBoundMappings.Contains((meterName, instrumentName)) ? + Metric.DefaultHistogramBoundsSeconds : Metric.DefaultHistogramBounds, + aggregatorStore.HistogramBounds); + } + } + internal static void AssertExponentialBucketsAreCorrect(Base2ExponentialBucketHistogram expectedHistogram, ExponentialHistogramData data) { Assert.Equal(expectedHistogram.Scale, data.Scale);