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

[sdk] Customize known connection histograms buckets #5008

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
([#5004](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5004))
([#5016](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5016))

* Update `AggregatorStore` to provide known connection metrics with larger
histogram buckets.
([#5008](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5008))

## 1.7.0-alpha.1

Released 2023-Oct-16
Expand Down
15 changes: 12 additions & 3 deletions src/OpenTelemetry/Metrics/AggregatorStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -372,10 +372,19 @@ internal MetricPointsAccessor GetMetricPoints()

private static double[] FindDefaultHistogramBounds(in MetricStreamIdentity metricStreamIdentity)
{
if (metricStreamIdentity.Unit == "s" && Metric.DefaultHistogramBoundMappings
.Contains((metricStreamIdentity.MeterName, metricStreamIdentity.InstrumentName)))
if (metricStreamIdentity.Unit == "s")
{
return Metric.DefaultHistogramBoundsSeconds;
if (Metric.DefaultHistogramBoundShortMappings
.Contains((metricStreamIdentity.MeterName, metricStreamIdentity.InstrumentName)))
{
return Metric.DefaultHistogramBoundsShortSeconds;
}

if (Metric.DefaultHistogramBoundLongMappings
.Contains((metricStreamIdentity.MeterName, metricStreamIdentity.InstrumentName)))
{
return Metric.DefaultHistogramBoundsLongSeconds;
}
}

return Metric.DefaultHistogramBounds;
Expand Down
18 changes: 13 additions & 5 deletions src/OpenTelemetry/Metrics/Metric.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,31 @@ 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()

// Short default histogram bounds. Based on the recommended semantic convention values for http.server.request.duration.
internal static readonly double[] DefaultHistogramBoundsShortSeconds = 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)> DefaultHistogramBoundShortMappings = 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"),
};

// Long default histogram bounds. Not based on a standard. May change in the future.
internal static readonly double[] DefaultHistogramBoundsLongSeconds = new double[] { 0, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 30, 60, 120, 300 };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other buckets bounds have a zero value. I think this PR should focus on one thing, and if removing zero makes sense then it can be a new PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI - we will fix the other buckets as well for 1.7.0 release
#4908

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind if its one PR or two, but we should fix now while we can, and before it gets adopted - although I don't think its a breaking change.

internal static readonly HashSet<(string, string)> DefaultHistogramBoundLongMappings = new()
{
("Microsoft.AspNetCore.Http.Connections", "signalr.server.connection.duration"),
("Microsoft.AspNetCore.Server.Kestrel", "kestrel.connection.duration"),
("System.Net.Http", "http.client.connection.duration"),
};

private readonly AggregatorStore aggStore;

internal Metric(
Expand Down
72 changes: 37 additions & 35 deletions test/OpenTelemetry.Tests/Metrics/AggregatorTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,47 +239,49 @@ public void MultiThreadedHistogramUpdateAndSnapShotTest()
}

[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)
[InlineData("Microsoft.AspNetCore.Hosting", "http.server.request.duration", "s", KnownHistogramBuckets.DefaultShortSeconds)]
[InlineData("Microsoft.AspNetCore.Hosting", "http.server.request.duration", "ms", KnownHistogramBuckets.Default)]
[InlineData("Microsoft.AspNetCore.Hosting", "http.server.request.duration", "By", KnownHistogramBuckets.Default)]
[InlineData("Microsoft.AspNetCore.Hosting", "http.server.request.duration", null, KnownHistogramBuckets.Default)]
[InlineData("Microsoft.AspNetCore.Http.Connections", "signalr.server.connection.duration", "s", KnownHistogramBuckets.DefaultLongSeconds)]
[InlineData("Microsoft.AspNetCore.RateLimiting", "aspnetcore.rate_limiting.request_lease.duration", "s", KnownHistogramBuckets.DefaultShortSeconds)]
[InlineData("Microsoft.AspNetCore.RateLimiting", "aspnetcore.rate_limiting.request.time_in_queue", "s", KnownHistogramBuckets.DefaultShortSeconds)]
[InlineData("Microsoft.AspNetCore.Server.Kestrel", "kestrel.connection.duration", "s", KnownHistogramBuckets.DefaultLongSeconds)]
[InlineData("Microsoft.AspNetCore.Server.Kestrel", "kestrel.tls_handshake.duration", "s", KnownHistogramBuckets.DefaultShortSeconds)]
[InlineData("OpenTelemetry.Instrumentation.AspNetCore", "http.server.duration", "ms", KnownHistogramBuckets.Default)]
[InlineData("OpenTelemetry.Instrumentation.Http", "http.client.duration", "ms", KnownHistogramBuckets.Default)]
[InlineData("System.Net.Http", "http.client.connection.duration", "s", KnownHistogramBuckets.DefaultLongSeconds)]
[InlineData("System.Net.Http", "http.client.request.duration", "s", KnownHistogramBuckets.DefaultShortSeconds)]
[InlineData("System.Net.Http", "http.client.request.time_in_queue", "s", KnownHistogramBuckets.DefaultShortSeconds)]
[InlineData("System.Net.NameResolution", "dns.lookups.duration", "s", KnownHistogramBuckets.DefaultShortSeconds)]
[InlineData("General.App", "simple.alternative.counter", "s", KnownHistogramBuckets.Default)]
public void HistogramBucketsDefaultUpdatesForSecondsTest(string meterName, string instrumentName, string unit, KnownHistogramBuckets expectedHistogramBuckets)
{
RunTest(meterName, instrumentName, unit: "s");
RunTest(meterName, instrumentName, unit: "ms");
RunTest(meterName, instrumentName, unit: "By");
RunTest(meterName, instrumentName, unit: null);
using var meter = new Meter(meterName);

void RunTest(string meterName, string instrumentName, string unit)
{
using var meter = new Meter(meterName);

var instrument = meter.CreateHistogram<double>(instrumentName, unit);
var instrument = meter.CreateHistogram<double>(instrumentName, unit);

var metricStreamIdentity = new MetricStreamIdentity(instrument, metricStreamConfiguration: null);
var metricStreamIdentity = new MetricStreamIdentity(instrument, metricStreamConfiguration: null);

AggregatorStore aggregatorStore = new(
metricStreamIdentity,
AggregationType.Histogram,
AggregationTemporality.Cumulative,
maxMetricPoints: 1024,
this.emitOverflowAttribute);
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);
KnownHistogramBuckets actualHistogramBounds = KnownHistogramBuckets.Default;
if (aggregatorStore.HistogramBounds == Metric.DefaultHistogramBoundsShortSeconds)
{
actualHistogramBounds = KnownHistogramBuckets.DefaultShortSeconds;
}
else if (aggregatorStore.HistogramBounds == Metric.DefaultHistogramBoundsLongSeconds)
{
actualHistogramBounds = KnownHistogramBuckets.DefaultLongSeconds;
}

Assert.NotNull(aggregatorStore.HistogramBounds);
Assert.Equal(expectedHistogramBuckets, actualHistogramBounds);
}

internal static void AssertExponentialBucketsAreCorrect(Base2ExponentialBucketHistogram expectedHistogram, ExponentialHistogramData data)
Expand Down
35 changes: 35 additions & 0 deletions test/OpenTelemetry.Tests/Metrics/KnownHistogramBuckets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// <copyright file="KnownHistogramBuckets.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

namespace OpenTelemetry.Metrics.Tests;

public enum KnownHistogramBuckets
{
/// <summary>
/// Default OpenTelemetry semantic convention buckets.
/// </summary>
Default,

/// <summary>
/// Buckets for up to 10 seconds.
/// </summary>
DefaultShortSeconds,

/// <summary>
/// Buckets for up to 300 seconds.
/// </summary>
DefaultLongSeconds,
}