Skip to content

Commit 39cf140

Browse files
Add support for OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION (#6778)
1 parent d2de188 commit 39cf140

File tree

12 files changed

+307
-9
lines changed

12 files changed

+307
-9
lines changed

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ Notes](../../RELEASENOTES.md).
2626
* Fix `NullReferenceException` when no bucket boundaries configured for a view.
2727
([#6773](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6773))
2828

29+
* Added support for `OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION`
30+
environment variable to configure the default histogram aggregation for
31+
histogram instruments. Valid values are `explicit_bucket_histogram` (default)
32+
and `base2_exponential_bucket_histogram`. Explicit views configured via
33+
`AddView` take precedence over this setting.
34+
([#6778](https://github.com/open-telemetry/opentelemetry-dotnet/pull/6778))
35+
2936
## 1.14.0
3037

3138
Released 2025-Nov-12

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpServiceCollectionExtensions.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,21 @@ public static void AddOtlpExporterMetricsServices(this IServiceCollection servic
3434
{
3535
readerOptions.TemporalityPreference = enumValue;
3636
}
37+
38+
// Parse histogram aggregation using direct string comparison instead of Enum.TryParse.
39+
// The spec defines snake_case values (explicit_bucket_histogram, base2_exponential_bucket_histogram).
40+
// Using direct string comparison ensures we strictly validate against spec-defined values and fail
41+
// gracefully for invalid inputs, rather than attempting to parse arbitrary strings to enum values.
42+
// Case-insensitive comparison is used for flexibility, though the spec uses lowercase.
43+
var otlpDefaultHistogramAggregation = config[OtlpSpecConfigDefinitions.MetricsDefaultHistogramAggregationEnvVarName];
44+
if (string.Equals(otlpDefaultHistogramAggregation, "base2_exponential_bucket_histogram", StringComparison.OrdinalIgnoreCase))
45+
{
46+
readerOptions.DefaultHistogramAggregation = MetricReaderHistogramAggregation.Base2ExponentialBucketHistogram;
47+
}
48+
else if (string.Equals(otlpDefaultHistogramAggregation, "explicit_bucket_histogram", StringComparison.OrdinalIgnoreCase))
49+
{
50+
readerOptions.DefaultHistogramAggregation = MetricReaderHistogramAggregation.ExplicitBucketHistogram;
51+
}
3752
});
3853
}
3954

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpSpecConfigDefinitions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ internal static class OtlpSpecConfigDefinitions
2626
public const string MetricsTimeoutEnvVarName = "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT";
2727
public const string MetricsProtocolEnvVarName = "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL";
2828
public const string MetricsTemporalityPreferenceEnvVarName = "OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE";
29+
public const string MetricsDefaultHistogramAggregationEnvVarName = "OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION";
2930

3031
public const string TracesEndpointEnvVarName = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT";
3132
public const string TracesHeadersEnvVarName = "OTEL_EXPORTER_OTLP_TRACES_HEADERS";

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/README.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -484,13 +484,21 @@ or reader
484484

485485
* Metrics:
486486

487-
The following environment variables can be used to override the default value
488-
of the `TemporalityPreference` setting for the reader configured for metrics
489-
when using OTLP exporter:
487+
The following environment variables can be used to override the default values
488+
for the reader configured for metrics when using OTLP exporter:
490489

491-
| Environment variable | `MetricReaderOptions` property |
492-
| ----------------------------------------------------| ------------------------------------------------|
493-
| `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` | `TemporalityPreference` |
490+
| Environment variable | `MetricReaderOptions` property |
491+
| ------------------------------------------------------------| ------------------------------------------------|
492+
| `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` | `TemporalityPreference` |
493+
| `OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION` | `DefaultHistogramAggregation` |
494+
495+
`OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION` specifies the
496+
default aggregation to use for histogram instruments. Valid values are:
497+
* `explicit_bucket_histogram` (default)
498+
* `base2_exponential_bucket_histogram`
499+
500+
Note: Explicit views configured via `AddView` take precedence over the
501+
default histogram aggregation.
494502

495503
The following environment variables can be used to override the default values
496504
of the periodic exporting metric reader configured for metrics:

src/OpenTelemetry/Metrics/Reader/MetricReader.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ public MetricReaderTemporalityPreference TemporalityPreference
9393
}
9494
}
9595

96+
/// <summary>
97+
/// Gets or sets the default histogram aggregation.
98+
/// </summary>
99+
internal MetricReaderHistogramAggregation? DefaultHistogramAggregation { get; set; }
100+
96101
/// <summary>
97102
/// Attempts to collect the metrics, blocks the current thread until
98103
/// metrics collection completed, shutdown signaled or timed out.

src/OpenTelemetry/Metrics/Reader/MetricReaderExt.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,15 @@ internal virtual List<Metric> AddMetricWithNoViews(Instrument instrument)
5252
Debug.Assert(instrument != null, "instrument was null");
5353
Debug.Assert(this.metrics != null, "this.metrics was null");
5454

55-
var metricStreamIdentity = new MetricStreamIdentity(instrument!, metricStreamConfiguration: null);
55+
MetricStreamConfiguration? metricStreamConfiguration = null;
56+
57+
// Apply default histogram aggregation if configured
58+
if (this.DefaultHistogramAggregation is { } aggregation)
59+
{
60+
metricStreamConfiguration = CreateDefaultHistogramConfiguration(instrument!, aggregation);
61+
}
62+
63+
var metricStreamIdentity = new MetricStreamIdentity(instrument!, metricStreamConfiguration);
5664

5765
var exemplarFilter = metricStreamIdentity.IsHistogram
5866
? this.exemplarFilterForHistograms ?? this.exemplarFilter
@@ -120,6 +128,13 @@ internal virtual List<Metric> AddMetricWithViews(Instrument instrument, List<Met
120128
for (int i = 0; i < maxCountMetricsToBeCreated; i++)
121129
{
122130
var metricStreamConfig = metricStreamConfigs[i];
131+
132+
// Apply default histogram aggregation if no explicit view is provided
133+
if (metricStreamConfig == null && this.DefaultHistogramAggregation is { } aggregation)
134+
{
135+
metricStreamConfig = CreateDefaultHistogramConfiguration(instrument!, aggregation);
136+
}
137+
123138
var metricStreamIdentity = new MetricStreamIdentity(instrument!, metricStreamConfig);
124139

125140
var exemplarFilter = metricStreamIdentity.IsHistogram
@@ -189,6 +204,25 @@ internal void ApplyParentProviderSettings(
189204
this.exemplarFilterForHistograms = exemplarFilterForHistograms;
190205
}
191206

207+
private static MetricStreamConfiguration? CreateDefaultHistogramConfiguration(Instrument instrument, MetricReaderHistogramAggregation aggregation)
208+
{
209+
Debug.Assert(instrument != null, "instrument was null");
210+
211+
var instrumentType = instrument!.GetType();
212+
if (instrumentType.IsGenericType)
213+
{
214+
var genericType = instrumentType.GetGenericTypeDefinition();
215+
if (genericType == typeof(Histogram<>))
216+
{
217+
return aggregation == MetricReaderHistogramAggregation.Base2ExponentialBucketHistogram
218+
? new Base2ExponentialBucketHistogramConfiguration()
219+
: new ExplicitBucketHistogramConfiguration();
220+
}
221+
}
222+
223+
return null;
224+
}
225+
192226
private bool TryGetExistingMetric(in MetricStreamIdentity metricStreamIdentity, [NotNullWhen(true)] out Metric? existingMetric)
193227
=> this.instrumentIdentityToMetric.TryGetValue(metricStreamIdentity, out existingMetric)
194228
&& existingMetric != null && existingMetric.Active;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
namespace OpenTelemetry.Metrics;
5+
6+
/// <summary>
7+
/// Defines the default histogram aggregation for a <see cref="MetricReader" />.
8+
/// </summary>
9+
internal enum MetricReaderHistogramAggregation
10+
{
11+
/// <summary>
12+
/// Explicit bucket histogram aggregation.
13+
/// </summary>
14+
ExplicitBucketHistogram = 0,
15+
16+
/// <summary>
17+
/// Base2 exponential bucket histogram aggregation.
18+
/// </summary>
19+
Base2ExponentialBucketHistogram = 1,
20+
}

src/OpenTelemetry/Metrics/Reader/MetricReaderOptions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,9 @@ public PeriodicExportingMetricReaderOptions PeriodicExportingMetricReaderOptions
4848
this.periodicExportingMetricReaderOptions = value;
4949
}
5050
}
51+
52+
/// <summary>
53+
/// Gets or sets the default histogram aggregation.
54+
/// </summary>
55+
internal MetricReaderHistogramAggregation? DefaultHistogramAggregation { get; set; }
5156
}

src/Shared/PeriodicExportingMetricReaderHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ internal static PeriodicExportingMetricReader CreatePeriodicExportingMetricReade
2323
var metricReader = new PeriodicExportingMetricReader(exporter, exportInterval, exportTimeout)
2424
{
2525
TemporalityPreference = options.TemporalityPreference,
26+
DefaultHistogramAggregation = options.DefaultHistogramAggregation,
2627
};
2728

2829
return metricReader;

test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
</ItemGroup>
4545

4646
<ItemGroup>
47+
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\EnvironmentVariableScope.cs" Link="Includes\EnvironmentVariableScope.cs" />
4748
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\DelegatingExporter.cs" Link="Includes\DelegatingExporter.cs" />
4849
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\EventSourceTestHelper.cs" Link="Includes\EventSourceTestHelper.cs" />
4950
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\SkipUnlessEnvVarFoundTheoryAttribute.cs" Link="Includes\SkipUnlessEnvVarFoundTheoryAttribute.cs" />

0 commit comments

Comments
 (0)