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

OTLP support for exporting exponential histograms #4337

Merged
merged 22 commits into from
Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
af6df1f
Make exponential histogram config public
alanwest Mar 22, 2023
7ffdbd3
Stub out console exporter support
alanwest Mar 22, 2023
0fb670d
Add TODO in prometheus exporter
alanwest Mar 22, 2023
b7b1dad
Add OTLP exporter support for exponential histograms
alanwest Mar 22, 2023
763ed8d
Fix console exporter
alanwest Mar 22, 2023
a1090ad
Merge branch 'main' into alanwest/exp-histogram-export
alanwest Mar 23, 2023
c05bea8
Add exponential histogram config option to example ASP.NET Core app
alanwest Mar 23, 2023
330f181
Update TODO in console exporter
alanwest Mar 23, 2023
a76536a
Fix whitespace
alanwest Mar 23, 2023
0a72fd0
Merge branch 'main' into alanwest/exp-histogram-export
alanwest Mar 27, 2023
39f6f08
Update changelogs
alanwest Mar 27, 2023
97e3fbc
Update public api
alanwest Mar 27, 2023
035663c
Update PR number
alanwest Mar 27, 2023
7806a75
Do not use IsHistogram()
alanwest Mar 27, 2023
7a1dcbd
Do not use IsHistogram()
alanwest Mar 27, 2023
20cda0d
Update changelog
alanwest Mar 27, 2023
8d017ba
3rd times the charm. Really have Prometheus ignore exponential histog…
alanwest Mar 27, 2023
5f69b7b
Add message about not displaying buckets
alanwest Mar 27, 2023
73d572b
Merge branch 'main' into alanwest/exp-histogram-export
alanwest Mar 27, 2023
78ac7b4
Merge branch 'main' into alanwest/exp-histogram-export
alanwest Mar 27, 2023
75ad349
Rename buckets to exponentialHistogramData
alanwest Mar 28, 2023
d6cddeb
Merge branch 'alanwest/exp-histogram-export' of github.com:alanwest/o…
alanwest Mar 28, 2023
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
20 changes: 20 additions & 0 deletions examples/AspNetCore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// limitations under the License.
// </copyright>

using System.Diagnostics.Metrics;
using Examples.AspNetCore;
using OpenTelemetry.Exporter;
using OpenTelemetry.Instrumentation.AspNetCore;
Expand All @@ -33,6 +34,9 @@
// Note: Switch between Console/OTLP by setting UseLogExporter in appsettings.json.
var logExporter = appBuilder.Configuration.GetValue<string>("UseLogExporter").ToLowerInvariant();

// Note: Switch between Explicit/Exponential by setting HistogramAggregation in appsettings.json
var histogramAggregation = appBuilder.Configuration.GetValue<string>("HistogramAggregation").ToLowerInvariant();

// Build a resource configuration action to set service information.
Action<ResourceBuilder> configureResource = r => r.AddService(
serviceName: appBuilder.Configuration.GetValue<string>("ServiceName"),
Expand Down Expand Up @@ -111,6 +115,22 @@
.AddHttpClientInstrumentation()
.AddAspNetCoreInstrumentation();

switch (histogramAggregation)
{
case "exponential":
builder.AddView(instrument =>
{
return instrument.GetType().GetGenericTypeDefinition() == typeof(Histogram<>)
? new Base2ExponentialBucketHistogramConfiguration()
: null;
});
break;
default:
// Explicit bounds histogram is the default.
// No additional configuration necessary.
break;
}

switch (metricsExporter)
{
case "prometheus":
Expand Down
1 change: 1 addition & 0 deletions examples/AspNetCore/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"UseTracingExporter": "console",
"UseMetricsExporter": "console",
"UseLogExporter": "console",
"HistogramAggregation": "explicit",
"Jaeger": {
"AgentHost": "localhost",
"AgentPort": 6831,
Expand Down
58 changes: 33 additions & 25 deletions src/OpenTelemetry.Exporter.Console/ConsoleMetricExporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public override ExportResult Export(in Batch<Metric> batch)

var metricType = metric.MetricType;

if (metricType.IsHistogram())
if (metricType == MetricType.Histogram || metricType == MetricType.ExponentialHistogram)
cijothomas marked this conversation as resolved.
Show resolved Hide resolved
{
var bucketsBuilder = new StringBuilder();
var sum = metricPoint.GetHistogramSum();
Expand All @@ -105,41 +105,49 @@ public override ExportResult Export(in Batch<Metric> batch)

bucketsBuilder.AppendLine();

bool isFirstIteration = true;
double previousExplicitBound = default;
foreach (var histogramMeasurement in metricPoint.GetHistogramBuckets())
if (metricType == MetricType.Histogram)
{
if (isFirstIteration)
bool isFirstIteration = true;
double previousExplicitBound = default;
foreach (var histogramMeasurement in metricPoint.GetHistogramBuckets())
{
bucketsBuilder.Append("(-Infinity,");
bucketsBuilder.Append(histogramMeasurement.ExplicitBound);
bucketsBuilder.Append(']');
bucketsBuilder.Append(':');
bucketsBuilder.Append(histogramMeasurement.BucketCount);
previousExplicitBound = histogramMeasurement.ExplicitBound;
isFirstIteration = false;
}
else
{
bucketsBuilder.Append('(');
bucketsBuilder.Append(previousExplicitBound);
bucketsBuilder.Append(',');
if (histogramMeasurement.ExplicitBound != double.PositiveInfinity)
if (isFirstIteration)
{
bucketsBuilder.Append("(-Infinity,");
bucketsBuilder.Append(histogramMeasurement.ExplicitBound);
bucketsBuilder.Append(']');
bucketsBuilder.Append(':');
bucketsBuilder.Append(histogramMeasurement.BucketCount);
previousExplicitBound = histogramMeasurement.ExplicitBound;
isFirstIteration = false;
}
else
{
bucketsBuilder.Append("+Infinity");
bucketsBuilder.Append('(');
bucketsBuilder.Append(previousExplicitBound);
bucketsBuilder.Append(',');
if (histogramMeasurement.ExplicitBound != double.PositiveInfinity)
{
bucketsBuilder.Append(histogramMeasurement.ExplicitBound);
previousExplicitBound = histogramMeasurement.ExplicitBound;
}
else
{
bucketsBuilder.Append("+Infinity");
}

bucketsBuilder.Append(']');
bucketsBuilder.Append(':');
bucketsBuilder.Append(histogramMeasurement.BucketCount);
}

bucketsBuilder.Append(']');
bucketsBuilder.Append(':');
bucketsBuilder.Append(histogramMeasurement.BucketCount);
bucketsBuilder.AppendLine();
}

bucketsBuilder.AppendLine();
}
else
{
// TODO: Consider how/if to display buckets for exponential histograms.
cijothomas marked this conversation as resolved.
Show resolved Hide resolved
bucketsBuilder.AppendLine("Buckets are not displayed for exponential histograms.");
}

valueDisplay = bucketsBuilder.ToString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

* Add support for exporting histograms aggregated using the
[Base2 Exponential Bucket Histogram Aggregation](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#base2-exponential-bucket-histogram-aggregation).
([#4337](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4337))

* Added support to set `TraceState` when converting the
System.Diagnostics.Activity object to its corresponding
OpenTelemetry.Proto.Trace.V1.Span object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,57 @@ internal static OtlpMetrics.Metric ToOtlpMetric(this Metric metric)
otlpMetric.Histogram = histogram;
break;
}

case MetricType.ExponentialHistogram:
{
var histogram = new OtlpMetrics.ExponentialHistogram
{
AggregationTemporality = temporality,
};

foreach (ref readonly var metricPoint in metric.GetMetricPoints())
{
var dataPoint = new OtlpMetrics.ExponentialHistogramDataPoint
{
StartTimeUnixNano = (ulong)metricPoint.StartTime.ToUnixTimeNanoseconds(),
TimeUnixNano = (ulong)metricPoint.EndTime.ToUnixTimeNanoseconds(),
};

AddAttributes(metricPoint.Tags, dataPoint.Attributes);
dataPoint.Count = (ulong)metricPoint.GetHistogramCount();
dataPoint.Sum = metricPoint.GetHistogramSum();

if (metricPoint.TryGetHistogramMinMaxValues(out double min, out double max))
{
dataPoint.Min = min;
dataPoint.Max = max;
}

var buckets = metricPoint.GetExponentialHistogramData();
alanwest marked this conversation as resolved.
Show resolved Hide resolved
dataPoint.Scale = buckets.Scale;

dataPoint.Positive = new OtlpMetrics.ExponentialHistogramDataPoint.Types.Buckets();
dataPoint.Positive.Offset = buckets.PositiveBuckets.Offset;
foreach (var bucketCount in buckets.PositiveBuckets)
{
dataPoint.Positive.BucketCounts.Add((ulong)bucketCount);
}

dataPoint.Negative = new OtlpMetrics.ExponentialHistogramDataPoint.Types.Buckets();
dataPoint.Negative.Offset = buckets.NegativeBuckets.Offset;
foreach (var bucketCount in buckets.NegativeBuckets)
{
dataPoint.Negative.BucketCounts.Add((ulong)bucketCount);
}

// TODO: exemplars.

histogram.DataPoints.Add(dataPoint);
}

otlpMetric.ExponentialHistogram = histogram;
break;
}
}

return otlpMetric;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ UpDownCounter becomes gauge

public static int WriteMetric(byte[] buffer, int cursor, Metric metric)
{
if (metric.MetricType == MetricType.ExponentialHistogram)
{
// Exponential histograms are not yet support by Prometheus.
// They are ignored for now.
return cursor;
}

int metricType = (int)metric.MetricType >> 4;
cursor = WriteTypeMetadata(buffer, cursor, metric.Name, metric.Unit, MetricTypes[metricType]);
cursor = WriteUnitMetadata(buffer, cursor, metric.Name, metric.Unit);
Expand Down
6 changes: 6 additions & 0 deletions src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ OpenTelemetry.Metrics.AlwaysOffExemplarFilter
OpenTelemetry.Metrics.AlwaysOffExemplarFilter.AlwaysOffExemplarFilter() -> void
OpenTelemetry.Metrics.AlwaysOnExemplarFilter
OpenTelemetry.Metrics.AlwaysOnExemplarFilter.AlwaysOnExemplarFilter() -> void
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.Base2ExponentialBucketHistogramConfiguration() -> void
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxScale.get -> int
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxScale.set -> void
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxSize.get -> int
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxSize.set -> void
OpenTelemetry.Metrics.Exemplar
OpenTelemetry.Metrics.Exemplar.DoubleValue.get -> double
OpenTelemetry.Metrics.Exemplar.Exemplar() -> void
Expand Down
6 changes: 6 additions & 0 deletions src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ OpenTelemetry.Metrics.AlwaysOffExemplarFilter
OpenTelemetry.Metrics.AlwaysOffExemplarFilter.AlwaysOffExemplarFilter() -> void
OpenTelemetry.Metrics.AlwaysOnExemplarFilter
OpenTelemetry.Metrics.AlwaysOnExemplarFilter.AlwaysOnExemplarFilter() -> void
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.Base2ExponentialBucketHistogramConfiguration() -> void
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxScale.get -> int
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxScale.set -> void
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxSize.get -> int
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxSize.set -> void
OpenTelemetry.Metrics.Exemplar
OpenTelemetry.Metrics.Exemplar.DoubleValue.get -> double
OpenTelemetry.Metrics.Exemplar.Exemplar() -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ OpenTelemetry.Metrics.AlwaysOffExemplarFilter
OpenTelemetry.Metrics.AlwaysOffExemplarFilter.AlwaysOffExemplarFilter() -> void
OpenTelemetry.Metrics.AlwaysOnExemplarFilter
OpenTelemetry.Metrics.AlwaysOnExemplarFilter.AlwaysOnExemplarFilter() -> void
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.Base2ExponentialBucketHistogramConfiguration() -> void
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxScale.get -> int
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxScale.set -> void
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxSize.get -> int
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxSize.set -> void
OpenTelemetry.Metrics.Exemplar
OpenTelemetry.Metrics.Exemplar.DoubleValue.get -> double
OpenTelemetry.Metrics.Exemplar.Exemplar() -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ OpenTelemetry.Metrics.AlwaysOffExemplarFilter
OpenTelemetry.Metrics.AlwaysOffExemplarFilter.AlwaysOffExemplarFilter() -> void
OpenTelemetry.Metrics.AlwaysOnExemplarFilter
OpenTelemetry.Metrics.AlwaysOnExemplarFilter.AlwaysOnExemplarFilter() -> void
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.Base2ExponentialBucketHistogramConfiguration() -> void
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxScale.get -> int
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxScale.set -> void
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxSize.get -> int
OpenTelemetry.Metrics.Base2ExponentialBucketHistogramConfiguration.MaxSize.set -> void
OpenTelemetry.Metrics.Exemplar
OpenTelemetry.Metrics.Exemplar.DoubleValue.get -> double
OpenTelemetry.Metrics.Exemplar.Exemplar() -> void
Expand Down
6 changes: 6 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

* Add support for configuring the
[Base2 Exponential Bucket Histogram Aggregation](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/sdk.md#base2-exponential-bucket-histogram-aggregation)
using the `AddView` API. This aggregation is supported by OTLP but not yet by
Prometheus.
([#4337](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4337))

* Implementation of `SuppressInstrumentationScope` changed to improve
performance.
([#4304](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4304))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace OpenTelemetry.Metrics;
/// <summary>
/// Stores configuration for a histogram metric stream with base-2 exponential bucket boundaries.
/// </summary>
internal sealed class Base2ExponentialBucketHistogramConfiguration : HistogramConfiguration
public sealed class Base2ExponentialBucketHistogramConfiguration : HistogramConfiguration
{
private int maxSize = Metric.DefaultExponentialHistogramMaxBuckets;
private int maxScale = Metric.DefaultExponentialHistogramMaxScale;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,103 @@ public void TestUpDownCounterToOtlpMetric(string name, string description, strin
Assert.Empty(dataPoint.Exemplars);
}

[Theory]
[InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)]
[InlineData("test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)]
[InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta)]
[InlineData("test_histogram", "description", "unit", 123L, null, MetricReaderTemporalityPreference.Cumulative)]
[InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Delta, "key1", "value1", "key2", 123)]
public void TestExponentialHistogramToOtlpMetric(string name, string description, string unit, long? longValue, double? doubleValue, MetricReaderTemporalityPreference aggregationTemporality, params object[] keysValues)
{
var metrics = new List<Metric>();

using var meter = new Meter(Utils.GetCurrentMethodName());
using var provider = Sdk.CreateMeterProviderBuilder()
.AddMeter(meter.Name)
.AddInMemoryExporter(metrics, metricReaderOptions =>
{
metricReaderOptions.TemporalityPreference = aggregationTemporality;
})
.AddView(instrument =>
{
return new Base2ExponentialBucketHistogramConfiguration();
})
.Build();

var attributes = ToAttributes(keysValues).ToArray();
if (longValue.HasValue)
{
var histogram = meter.CreateHistogram<long>(name, unit, description);
histogram.Record(longValue.Value, attributes);
}
else
{
var histogram = meter.CreateHistogram<double>(name, unit, description);
histogram.Record(doubleValue.Value, attributes);
}

provider.ForceFlush();

var batch = new Batch<Metric>(metrics.ToArray(), metrics.Count);

var request = new OtlpCollector.ExportMetricsServiceRequest();
request.AddMetrics(ResourceBuilder.CreateEmpty().Build().ToOtlpResource(), batch);

var resourceMetric = request.ResourceMetrics.Single();
var scopeMetrics = resourceMetric.ScopeMetrics.Single();
var actual = scopeMetrics.Metrics.Single();

Assert.Equal(name, actual.Name);
Assert.Equal(description ?? string.Empty, actual.Description);
Assert.Equal(unit ?? string.Empty, actual.Unit);

Assert.Equal(OtlpMetrics.Metric.DataOneofCase.ExponentialHistogram, actual.DataCase);

Assert.Null(actual.Gauge);
Assert.Null(actual.Sum);
Assert.Null(actual.Histogram);
Assert.NotNull(actual.ExponentialHistogram);
Assert.Null(actual.Summary);

var otlpAggregationTemporality = aggregationTemporality == MetricReaderTemporalityPreference.Cumulative
? OtlpMetrics.AggregationTemporality.Cumulative
: OtlpMetrics.AggregationTemporality.Delta;
Assert.Equal(otlpAggregationTemporality, actual.ExponentialHistogram.AggregationTemporality);

Assert.Single(actual.ExponentialHistogram.DataPoints);
var dataPoint = actual.ExponentialHistogram.DataPoints.First();
Assert.True(dataPoint.StartTimeUnixNano > 0);
Assert.True(dataPoint.TimeUnixNano > 0);

Assert.Equal(1UL, dataPoint.Count);

if (longValue.HasValue)
{
Assert.Equal((double)longValue, dataPoint.Sum);
}
else
{
Assert.Equal(doubleValue, dataPoint.Sum);
}

Assert.Equal(0UL, dataPoint.ZeroCount);
Assert.Equal(20, dataPoint.Scale);
Assert.True(dataPoint.Positive.Offset > 0);
Assert.Equal(1UL, dataPoint.Positive.BucketCounts[0]);
Assert.True(dataPoint.Negative.Offset <= 0);

if (attributes.Length > 0)
{
OtlpTestHelpers.AssertOtlpAttributes(attributes, dataPoint.Attributes);
}
else
{
Assert.Empty(dataPoint.Attributes);
}

Assert.Empty(dataPoint.Exemplars);
}

[Theory]
[InlineData("test_histogram", null, null, 123L, null, MetricReaderTemporalityPreference.Cumulative)]
[InlineData("test_histogram", null, null, null, 123.45, MetricReaderTemporalityPreference.Cumulative)]
Expand Down
Loading