diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
index b8ce4b2b252..44de41e6946 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/CHANGELOG.md
@@ -2,6 +2,12 @@
## Unreleased
+* Added support for configuring the metric exporter's temporality using the
+ environment variable `OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE` as
+ defined in the
+ [specification](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.23.0/specification/metrics/sdk_exporters/otlp.md#additional-configuration).
+ ([#4667](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4667))
+
## 1.6.0-alpha.1
Released 2023-Jul-12
diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs
index df4f9e5be80..18b8c5b04f9 100644
--- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs
+++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/OtlpMetricExporterExtensions.cs
@@ -14,6 +14,7 @@
// limitations under the License.
//
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using OpenTelemetry.Exporter;
@@ -26,6 +27,8 @@ namespace OpenTelemetry.Metrics
///
public static class OtlpMetricExporterExtensions
{
+ internal const string OtlpMetricExporterTemporalityPreferenceEnvVarKey = "OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE";
+
///
/// Adds to the using default options.
///
@@ -69,6 +72,17 @@ public static MeterProviderBuilder AddOtlpExporter(
}
OtlpExporterOptions.RegisterOtlpExporterOptionsFactory(services);
+
+ services.AddOptions(finalOptionsName).Configure(
+ (readerOptions, config) =>
+ {
+ var otlpTemporalityPreference = config[OtlpMetricExporterTemporalityPreferenceEnvVarKey];
+ if (!string.IsNullOrWhiteSpace(otlpTemporalityPreference)
+ && Enum.TryParse(otlpTemporalityPreference, ignoreCase: true, out var enumValue))
+ {
+ readerOptions.TemporalityPreference = enumValue;
+ }
+ });
});
return builder.AddReader(sp =>
@@ -135,6 +149,17 @@ public static MeterProviderBuilder AddOtlpExporter(
builder.ConfigureServices(services =>
{
OtlpExporterOptions.RegisterOtlpExporterOptionsFactory(services);
+
+ services.AddOptions(name).Configure(
+ (readerOptions, config) =>
+ {
+ var otlpTemporalityPreference = config[OtlpMetricExporterTemporalityPreferenceEnvVarKey];
+ if (!string.IsNullOrWhiteSpace(otlpTemporalityPreference)
+ && Enum.TryParse(otlpTemporalityPreference, ignoreCase: true, out var enumValue))
+ {
+ readerOptions.TemporalityPreference = enumValue;
+ }
+ });
});
return builder.AddReader(sp =>
diff --git a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs
index 465901e6c01..8cf45f910b6 100644
--- a/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs
+++ b/test/OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests/OtlpMetricsExporterTests.cs
@@ -15,6 +15,8 @@
//
using System.Diagnostics.Metrics;
+using System.Reflection;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation;
using OpenTelemetry.Metrics;
@@ -704,6 +706,50 @@ public void TestHistogramToOtlpMetric(string name, string description, string un
Assert.Empty(dataPoint.Exemplars);
}
+ [Theory]
+ [InlineData("cumulative", MetricReaderTemporalityPreference.Cumulative)]
+ [InlineData("Cumulative", MetricReaderTemporalityPreference.Cumulative)]
+ [InlineData("CUMULATIVE", MetricReaderTemporalityPreference.Cumulative)]
+ [InlineData("delta", MetricReaderTemporalityPreference.Delta)]
+ [InlineData("Delta", MetricReaderTemporalityPreference.Delta)]
+ [InlineData("DELTA", MetricReaderTemporalityPreference.Delta)]
+ public void TestTemporalityPreferenceConfiguration(string configValue, MetricReaderTemporalityPreference expectedTemporality)
+ {
+ var configData = new Dictionary { ["OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE"] = configValue };
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(configData)
+ .Build();
+
+ // Check for both the code paths:
+ // 1. The final extension method which accepts `Action`.
+ // 2. The final extension method which accepts `Action`.
+
+ // Test 1st code path
+ using var meterProvider1 = Sdk.CreateMeterProviderBuilder()
+ .ConfigureServices(services => services.AddSingleton(configuration))
+ .AddOtlpExporter() // This would in turn call the extension method which accepts `Action`
+ .Build();
+
+ var assembly = typeof(Sdk).Assembly;
+ var type = assembly.GetType("OpenTelemetry.Metrics.MeterProviderSdk");
+ var fieldInfo = type.GetField("reader", BindingFlags.Instance | BindingFlags.NonPublic);
+ var reader = fieldInfo.GetValue(meterProvider1) as MetricReader;
+ var temporality = reader.TemporalityPreference;
+
+ Assert.Equal(expectedTemporality, temporality);
+
+ // Test 2nd code path
+ using var meterProvider2 = Sdk.CreateMeterProviderBuilder()
+ .ConfigureServices(services => services.AddSingleton(configuration))
+ .AddOtlpExporter((_, _) => { }) // This would in turn call the extension method which accepts `Action`
+ .Build();
+
+ reader = fieldInfo.GetValue(meterProvider2) as MetricReader;
+ temporality = reader.TemporalityPreference;
+
+ Assert.Equal(expectedTemporality, temporality);
+ }
+
private static IEnumerable> ToAttributes(object[] keysValues)
{
var keys = keysValues?.Where((_, index) => index % 2 == 0).ToArray();