From 65e7145e9c6151416042757941d45f8970819248 Mon Sep 17 00:00:00 2001 From: jack-berg <34418638+jack-berg@users.noreply.github.com> Date: Thu, 11 Nov 2021 20:03:20 -0600 Subject: [PATCH] OTLP metric temporality configuration (#3847) * Add ability to configure preferred temporality to OTLP grpc and http metric exporters * Make OTLP metric preferred temporality configurable * Make temporality configuration case insensitive --- .../http/metrics/OtlpHttpMetricExporter.java | 14 +++---- .../OtlpHttpMetricExporterBuilder.java | 18 ++++++++- .../metrics/OtlpHttpMetricExporterTest.java | 18 +++++++++ .../otlp/metrics/OtlpGrpcMetricExporter.java | 11 +++--- .../OtlpGrpcMetricExporterBuilder.java | 18 ++++++++- .../metrics/OtlpGrpcMetricExporterTest.java | 18 +++++++++ sdk-extensions/autoconfigure/README.md | 1 + .../MetricExporterConfiguration.java | 2 + .../sdk/autoconfigure/OtlpConfigUtil.java | 17 +++++++++ .../sdk/autoconfigure/OtlpConfigUtilTest.java | 37 +++++++++++++++++++ .../sdk/autoconfigure/OtlpGrpcConfigTest.java | 2 + .../sdk/autoconfigure/OtlpHttpConfigTest.java | 2 + 12 files changed, 143 insertions(+), 15 deletions(-) diff --git a/exporters/otlp-http/metrics/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporter.java b/exporters/otlp-http/metrics/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporter.java index 2222a4d2d5..c1518a62ea 100644 --- a/exporters/otlp-http/metrics/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporter.java +++ b/exporters/otlp-http/metrics/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporter.java @@ -12,7 +12,6 @@ import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.export.MetricExporter; import java.util.Collection; -import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; /** Exports metrics using OTLP via HTTP, using OpenTelemetry's protobuf model. */ @@ -20,9 +19,13 @@ import javax.annotation.concurrent.ThreadSafe; public final class OtlpHttpMetricExporter implements MetricExporter { private final OkHttpExporter delegate; + private final AggregationTemporality preferredTemporality; - OtlpHttpMetricExporter(OkHttpExporter delegate) { + OtlpHttpMetricExporter( + OkHttpExporter delegate, + AggregationTemporality preferredTemporality) { this.delegate = delegate; + this.preferredTemporality = preferredTemporality; } /** @@ -43,12 +46,9 @@ public final class OtlpHttpMetricExporter implements MetricExporter { return new OtlpHttpMetricExporterBuilder(); } - @Nullable @Override - public final AggregationTemporality getPreferredTemporality() { - // TODO: Lookup based on specification, or constructor - // https://github.com/open-telemetry/opentelemetry-java/issues/3790 - return null; + public AggregationTemporality getPreferredTemporality() { + return preferredTemporality; } /** diff --git a/exporters/otlp-http/metrics/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilder.java b/exporters/otlp-http/metrics/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilder.java index 3b01c96926..3194b0ebb7 100644 --- a/exporters/otlp-http/metrics/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilder.java +++ b/exporters/otlp-http/metrics/src/main/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterBuilder.java @@ -10,6 +10,7 @@ import static java.util.Objects.requireNonNull; import io.opentelemetry.exporter.otlp.internal.metrics.MetricsRequestMarshaler; import io.opentelemetry.exporter.otlp.internal.okhttp.OkHttpExporterBuilder; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import java.time.Duration; import java.util.concurrent.TimeUnit; @@ -18,7 +19,11 @@ public final class OtlpHttpMetricExporterBuilder { private static final String DEFAULT_ENDPOINT = "http://localhost:4318/v1/metrics"; + private static final AggregationTemporality DEFAULT_TEMPORALITY = + AggregationTemporality.CUMULATIVE; + private final OkHttpExporterBuilder delegate; + private AggregationTemporality preferredTemporality = DEFAULT_TEMPORALITY; OtlpHttpMetricExporterBuilder() { delegate = new OkHttpExporterBuilder<>("metric", DEFAULT_ENDPOINT); @@ -83,12 +88,23 @@ public final class OtlpHttpMetricExporterBuilder { return this; } + /** + * Set the preferred aggregation temporality. If unset, defaults to {@link + * AggregationTemporality#CUMULATIVE}. + */ + public OtlpHttpMetricExporterBuilder setPreferredTemporality( + AggregationTemporality preferredTemporality) { + requireNonNull(preferredTemporality, "preferredTemporality"); + this.preferredTemporality = preferredTemporality; + return this; + } + /** * Constructs a new instance of the exporter based on the builder's values. * * @return a new exporter's instance */ public OtlpHttpMetricExporter build() { - return new OtlpHttpMetricExporter(delegate.build()); + return new OtlpHttpMetricExporter(delegate.build(), preferredTemporality); } } diff --git a/exporters/otlp-http/metrics/src/test/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterTest.java b/exporters/otlp-http/metrics/src/test/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterTest.java index e6ccdecd5e..01e883ad4a 100644 --- a/exporters/otlp-http/metrics/src/test/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterTest.java +++ b/exporters/otlp-http/metrics/src/test/java/io/opentelemetry/exporter/otlp/http/metrics/OtlpHttpMetricExporterTest.java @@ -133,6 +133,20 @@ class OtlpHttpMetricExporterTest { OtlpHttpMetricExporter.builder() .setTrustedCertificates("foobar".getBytes(StandardCharsets.UTF_8))) .doesNotThrowAnyException(); + + assertThatCode( + () -> + OtlpHttpMetricExporter.builder() + .setPreferredTemporality(AggregationTemporality.DELTA)) + .doesNotThrowAnyException(); + assertThat( + OtlpHttpMetricExporter.builder() + .setPreferredTemporality(AggregationTemporality.DELTA) + .build() + .getPreferredTemporality()) + .isEqualTo(AggregationTemporality.DELTA); + assertThat(OtlpHttpMetricExporter.builder().build().getPreferredTemporality()) + .isEqualTo(AggregationTemporality.CUMULATIVE); } @Test @@ -168,6 +182,10 @@ class OtlpHttpMetricExporterTest { .isInstanceOf(IllegalArgumentException.class) .hasMessage( "Unsupported compression method. Supported compression methods include: gzip, none."); + + assertThatThrownBy(() -> OtlpHttpMetricExporter.builder().setPreferredTemporality(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("preferredTemporality"); } @Test diff --git a/exporters/otlp/metrics/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporter.java b/exporters/otlp/metrics/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporter.java index 6cf6d19fb6..37ecc876a2 100644 --- a/exporters/otlp/metrics/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporter.java +++ b/exporters/otlp/metrics/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporter.java @@ -12,7 +12,6 @@ import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.metrics.export.MetricExporter; import java.util.Collection; -import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; /** Exports metrics using OTLP via gRPC, using OpenTelemetry's protobuf model. */ @@ -20,6 +19,7 @@ import javax.annotation.concurrent.ThreadSafe; public final class OtlpGrpcMetricExporter implements MetricExporter { private final GrpcExporter delegate; + private final AggregationTemporality preferredTemporality; /** * Returns a new {@link OtlpGrpcMetricExporter} reading the configuration values from the @@ -41,16 +41,15 @@ public final class OtlpGrpcMetricExporter implements MetricExporter { return new OtlpGrpcMetricExporterBuilder(); } - OtlpGrpcMetricExporter(GrpcExporter delegate) { + OtlpGrpcMetricExporter( + GrpcExporter delegate, AggregationTemporality preferredTemporality) { this.delegate = delegate; + this.preferredTemporality = preferredTemporality; } - @Nullable @Override public AggregationTemporality getPreferredTemporality() { - // TODO: Lookup based on specification, or constructor - // https://github.com/open-telemetry/opentelemetry-java/issues/3790 - return null; + return preferredTemporality; } /** diff --git a/exporters/otlp/metrics/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilder.java b/exporters/otlp/metrics/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilder.java index f5f3867413..5e8a1850d2 100644 --- a/exporters/otlp/metrics/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilder.java +++ b/exporters/otlp/metrics/src/main/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterBuilder.java @@ -12,6 +12,7 @@ import io.grpc.ManagedChannel; import io.opentelemetry.exporter.otlp.internal.grpc.GrpcExporter; import io.opentelemetry.exporter.otlp.internal.grpc.GrpcExporterBuilder; import io.opentelemetry.exporter.otlp.internal.metrics.MetricsRequestMarshaler; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import java.net.URI; import java.time.Duration; import java.util.concurrent.TimeUnit; @@ -27,10 +28,14 @@ public final class OtlpGrpcMetricExporterBuilder { private static final String DEFAULT_ENDPOINT_URL = "http://localhost:4317"; private static final URI DEFAULT_ENDPOINT = URI.create(DEFAULT_ENDPOINT_URL); private static final long DEFAULT_TIMEOUT_SECS = 10; + private static final AggregationTemporality DEFAULT_TEMPORALITY = + AggregationTemporality.CUMULATIVE; // Visible for testing final GrpcExporterBuilder delegate; + private AggregationTemporality preferredTemporality = DEFAULT_TEMPORALITY; + OtlpGrpcMetricExporterBuilder() { delegate = GrpcExporter.builder( @@ -121,12 +126,23 @@ public final class OtlpGrpcMetricExporterBuilder { return this; } + /** + * Set the preferred aggregation temporality. If unset, defaults to {@link + * AggregationTemporality#CUMULATIVE}. + */ + public OtlpGrpcMetricExporterBuilder setPreferredTemporality( + AggregationTemporality preferredTemporality) { + requireNonNull(preferredTemporality, "preferredTemporality"); + this.preferredTemporality = preferredTemporality; + return this; + } + /** * Constructs a new instance of the exporter based on the builder's values. * * @return a new exporter's instance */ public OtlpGrpcMetricExporter build() { - return new OtlpGrpcMetricExporter(delegate.build()); + return new OtlpGrpcMetricExporter(delegate.build(), preferredTemporality); } } diff --git a/exporters/otlp/metrics/src/test/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterTest.java b/exporters/otlp/metrics/src/test/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterTest.java index f5b34c7812..56f9bde819 100644 --- a/exporters/otlp/metrics/src/test/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterTest.java +++ b/exporters/otlp/metrics/src/test/java/io/opentelemetry/exporter/otlp/metrics/OtlpGrpcMetricExporterTest.java @@ -119,6 +119,20 @@ class OtlpGrpcMetricExporterTest { OtlpGrpcMetricExporter.builder() .setTrustedCertificates("foobar".getBytes(StandardCharsets.UTF_8))) .doesNotThrowAnyException(); + + assertThatCode( + () -> + OtlpGrpcMetricExporter.builder() + .setPreferredTemporality(AggregationTemporality.DELTA)) + .doesNotThrowAnyException(); + assertThat( + OtlpGrpcMetricExporter.builder() + .setPreferredTemporality(AggregationTemporality.DELTA) + .build() + .getPreferredTemporality()) + .isEqualTo(AggregationTemporality.DELTA); + assertThat(OtlpGrpcMetricExporter.builder().build().getPreferredTemporality()) + .isEqualTo(AggregationTemporality.CUMULATIVE); } @Test @@ -155,6 +169,10 @@ class OtlpGrpcMetricExporterTest { .isInstanceOf(IllegalArgumentException.class) .hasMessage( "Unsupported compression method. Supported compression methods include: gzip, none."); + + assertThatThrownBy(() -> OtlpGrpcMetricExporter.builder().setPreferredTemporality(null)) + .isInstanceOf(NullPointerException.class) + .hasMessage("preferredTemporality"); } @Test diff --git a/sdk-extensions/autoconfigure/README.md b/sdk-extensions/autoconfigure/README.md index 04b8075c03..03f6ae9d2e 100644 --- a/sdk-extensions/autoconfigure/README.md +++ b/sdk-extensions/autoconfigure/README.md @@ -70,6 +70,7 @@ The [OpenTelemetry Protocol (OTLP)](https://github.com/open-telemetry/openteleme | otel.exporter.otlp.protocol | OTEL_EXPORTER_OTLP_PROTOCOL | The transport protocol to use on OTLP trace and metrics requests. Options include `grpc` and `http/protobuf`. Default is `grpc`. | | otel.exporter.otlp.traces.protocol | OTEL_EXPORTER_OTLP_TRACES_PROTOCOL | The transport protocol to use on OTLP trace requests. Options include `grpc` and `http/protobuf`. Default is `grpc`. | | otel.exporter.otlp.metrics.protocol | OTEL_EXPORTER_OTLP_METRICS_PROTOCOL | The transport protocol to use on OTLP metrics requests. Options include `grpc` and `http/protobuf`. Default is `grpc`. | +| otel.exporter.otlp.metrics.temporality | OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY | The preferred output aggregation temporality. Options include `DELTA` and `CUMULATIVE`. Default is `CUMULATIVE`. | | otel.experimental.exporter.otlp.retry.enabled | OTEL_EXPERIMENTAL_EXPORTER_OTLP_RETRY_ENABLED | If `true`, enable [experimental retry support](#otlp-exporter-retry). Default is `false`. | To configure the service name for the OTLP exporter, add the `service.name` key diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfiguration.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfiguration.java index 80dc2a60f7..50880c4dcd 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfiguration.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfiguration.java @@ -106,6 +106,7 @@ final class MetricExporterConfiguration { builder::setTimeout, builder::setTrustedCertificates, (unused) -> {}); + OtlpConfigUtil.configureOtlpAggregationTemporality(config, builder::setPreferredTemporality); exporter = builder.build(); } else if (protocol.equals(PROTOCOL_GRPC)) { @@ -134,6 +135,7 @@ final class MetricExporterConfiguration { DefaultGrpcExporterBuilder.getDelegateBuilder( OtlpGrpcMetricExporterBuilder.class, builder) .addRetryPolicy(retryPolicy)); + OtlpConfigUtil.configureOtlpAggregationTemporality(config, builder::setPreferredTemporality); exporter = builder.build(); } else { diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/OtlpConfigUtil.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/OtlpConfigUtil.java index f6e7a41413..05a7af743f 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/OtlpConfigUtil.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/OtlpConfigUtil.java @@ -8,6 +8,7 @@ package io.opentelemetry.sdk.autoconfigure; import io.opentelemetry.exporter.otlp.internal.RetryPolicy; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; @@ -114,6 +115,22 @@ final class OtlpConfigUtil { } } + static void configureOtlpAggregationTemporality( + ConfigProperties config, Consumer setAggregationTemporality) { + String temporalityStr = config.getString("otel.exporter.otlp.metrics.temporality"); + if (temporalityStr == null) { + return; + } + AggregationTemporality temporality; + try { + temporality = AggregationTemporality.valueOf(temporalityStr.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new ConfigurationException( + "Unrecognized aggregation temporality: " + temporalityStr, e); + } + setAggregationTemporality.accept(temporality); + } + private static URL createUrl(URL context, String spec) { try { return new URL(context, spec); diff --git a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/OtlpConfigUtilTest.java b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/OtlpConfigUtilTest.java index 34c8e0e0ee..0d8b3c2b6e 100644 --- a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/OtlpConfigUtilTest.java +++ b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/OtlpConfigUtilTest.java @@ -15,6 +15,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.google.common.collect.ImmutableMap; import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; +import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import java.util.Collections; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; @@ -266,4 +267,40 @@ class OtlpConfigUtilTest { return endpoint.get(); } + + @Test + void configureOtlpAggregationTemporality() { + assertThatThrownBy( + () -> + configureAggregationTemporality( + ImmutableMap.of("otel.exporter.otlp.metrics.temporality", "foo"))) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("Unrecognized aggregation temporality:"); + + assertThat( + configureAggregationTemporality( + ImmutableMap.of("otel.exporter.otlp.metrics.temporality", "CUMULATIVE"))) + .isEqualTo(AggregationTemporality.CUMULATIVE); + assertThat( + configureAggregationTemporality( + ImmutableMap.of("otel.exporter.otlp.metrics.temporality", "cumulative"))) + .isEqualTo(AggregationTemporality.CUMULATIVE); + assertThat( + configureAggregationTemporality( + ImmutableMap.of("otel.exporter.otlp.metrics.temporality", "DELTA"))) + .isEqualTo(AggregationTemporality.DELTA); + assertThat( + configureAggregationTemporality( + ImmutableMap.of("otel.exporter.otlp.metrics.temporality", "delta"))) + .isEqualTo(AggregationTemporality.DELTA); + } + + /** Configure and return the aggregation temporality using the given properties. */ + private static AggregationTemporality configureAggregationTemporality( + Map properties) { + AtomicReference temporalityRef = new AtomicReference<>(); + OtlpConfigUtil.configureOtlpAggregationTemporality( + DefaultConfigProperties.createForTest(properties), temporalityRef::set); + return temporalityRef.get(); + } } diff --git a/sdk-extensions/autoconfigure/src/testOtlpGrpc/java/io/opentelemetry/sdk/autoconfigure/OtlpGrpcConfigTest.java b/sdk-extensions/autoconfigure/src/testOtlpGrpc/java/io/opentelemetry/sdk/autoconfigure/OtlpGrpcConfigTest.java index c5e9c8f61a..35f018cfb7 100644 --- a/sdk-extensions/autoconfigure/src/testOtlpGrpc/java/io/opentelemetry/sdk/autoconfigure/OtlpGrpcConfigTest.java +++ b/sdk-extensions/autoconfigure/src/testOtlpGrpc/java/io/opentelemetry/sdk/autoconfigure/OtlpGrpcConfigTest.java @@ -184,6 +184,7 @@ class OtlpGrpcConfigTest { props.put("otel.exporter.otlp.metrics.headers", "header-key=header-value"); props.put("otel.exporter.otlp.metrics.compression", "gzip"); props.put("otel.exporter.otlp.metrics.timeout", "15s"); + props.put("otel.exporter.otlp.metrics.temporality", "DELTA"); MetricExporter metricExporter = MetricExporterConfiguration.configureOtlpMetrics( DefaultConfigProperties.createForTest(props), SdkMeterProvider.builder()); @@ -191,6 +192,7 @@ class OtlpGrpcConfigTest { assertThat(metricExporter) .extracting("delegate.timeoutNanos") .isEqualTo(TimeUnit.SECONDS.toNanos(15)); + assertThat(metricExporter.getPreferredTemporality()).isEqualTo(AggregationTemporality.DELTA); assertThat(metricExporter.export(METRIC_DATA).join(15, TimeUnit.SECONDS).isSuccess()).isTrue(); assertThat(server.metricRequests).hasSize(1); assertThat(server.requestHeaders) diff --git a/sdk-extensions/autoconfigure/src/testOtlpHttp/java/io/opentelemetry/sdk/autoconfigure/OtlpHttpConfigTest.java b/sdk-extensions/autoconfigure/src/testOtlpHttp/java/io/opentelemetry/sdk/autoconfigure/OtlpHttpConfigTest.java index 271ba6eb9b..80ab35bc4b 100644 --- a/sdk-extensions/autoconfigure/src/testOtlpHttp/java/io/opentelemetry/sdk/autoconfigure/OtlpHttpConfigTest.java +++ b/sdk-extensions/autoconfigure/src/testOtlpHttp/java/io/opentelemetry/sdk/autoconfigure/OtlpHttpConfigTest.java @@ -281,6 +281,7 @@ class OtlpHttpConfigTest { props.put("otel.exporter.otlp.metrics.headers", "header-key=header-value"); props.put("otel.exporter.otlp.metrics.compression", "gzip"); props.put("otel.exporter.otlp.metrics.timeout", "15s"); + props.put("otel.exporter.otlp.metrics.temporality", "DELTA"); MetricExporter metricExporter = MetricExporterConfiguration.configureOtlpMetrics( DefaultConfigProperties.createForTest(props), SdkMeterProvider.builder()); @@ -289,6 +290,7 @@ class OtlpHttpConfigTest { .extracting("delegate.client", as(InstanceOfAssertFactories.type(OkHttpClient.class))) .extracting(OkHttpClient::callTimeoutMillis) .isEqualTo((int) TimeUnit.SECONDS.toMillis(15)); + assertThat(metricExporter.getPreferredTemporality()).isEqualTo(AggregationTemporality.DELTA); assertThat( metricExporter .export(Lists.newArrayList(generateFakeMetric()))