From 9d060a905362f1dd8a752dd12c9e81607390f26a Mon Sep 17 00:00:00 2001 From: Xiaohan Song Date: Thu, 16 Jun 2022 15:54:25 -0700 Subject: [PATCH] Add meaningful tests for OTEL client (#13831) * Add meaningful tests for OTEL client * PR comment fix * code clean ups --- airbyte-metrics/metrics-lib/build.gradle | 2 + .../lib/OpenTelemetryMetricClient.java | 17 +++- .../lib/OpenTelemetryMetricClientTest.java | 94 +++++++++++++++---- deps.toml | 2 + 4 files changed, 95 insertions(+), 20 deletions(-) diff --git a/airbyte-metrics/metrics-lib/build.gradle b/airbyte-metrics/metrics-lib/build.gradle index 3529077216e15..cd69896a6e227 100644 --- a/airbyte-metrics/metrics-lib/build.gradle +++ b/airbyte-metrics/metrics-lib/build.gradle @@ -9,6 +9,8 @@ dependencies { implementation project(':airbyte-db:db-lib') implementation libs.otel.semconv + implementation libs.otel.sdk + implementation libs.otel.sdk.testing implementation platform(libs.otel.bom) implementation("io.opentelemetry:opentelemetry-api") implementation("io.opentelemetry:opentelemetry-sdk") diff --git a/airbyte-metrics/metrics-lib/src/main/java/io/airbyte/metrics/lib/OpenTelemetryMetricClient.java b/airbyte-metrics/metrics-lib/src/main/java/io/airbyte/metrics/lib/OpenTelemetryMetricClient.java index f5e2bba98c111..45d8ed675e7cf 100644 --- a/airbyte-metrics/metrics-lib/src/main/java/io/airbyte/metrics/lib/OpenTelemetryMetricClient.java +++ b/airbyte-metrics/metrics-lib/src/main/java/io/airbyte/metrics/lib/OpenTelemetryMetricClient.java @@ -8,6 +8,7 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME; +import com.google.common.annotations.VisibleForTesting; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; @@ -20,6 +21,7 @@ import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; @@ -28,6 +30,7 @@ public class OpenTelemetryMetricClient implements MetricClient { private Meter meter; + private SdkMeterProvider meterProvider; @Override public void count(MetricsRegistry metric, long val, String... tags) { @@ -75,9 +78,19 @@ public void initialize(MetricEmittingApp metricEmittingApp, String otelEndpoint) .build()) .setResource(resource) .build(); - OtlpGrpcMetricExporter metricExporter = OtlpGrpcMetricExporter.builder() + MetricExporter metricExporter = OtlpGrpcMetricExporter.builder() .setEndpoint(otelEndpoint).build(); - SdkMeterProvider meterProvider = SdkMeterProvider.builder() + initialize(metricEmittingApp, metricExporter, sdkTracerProvider, resource); + } + + @VisibleForTesting + SdkMeterProvider getSdkMeterProvider() { + return meterProvider; + } + + @VisibleForTesting + void initialize(MetricEmittingApp metricEmittingApp, MetricExporter metricExporter, SdkTracerProvider sdkTracerProvider, Resource resource) { + meterProvider = SdkMeterProvider.builder() .registerMetricReader(PeriodicMetricReader.builder(metricExporter).build()) .setResource(resource) .build(); diff --git a/airbyte-metrics/metrics-lib/src/test/java/io/airbyte/metrics/lib/OpenTelemetryMetricClientTest.java b/airbyte-metrics/metrics-lib/src/test/java/io/airbyte/metrics/lib/OpenTelemetryMetricClientTest.java index 0c0062c930cbd..a6f7ad704f077 100644 --- a/airbyte-metrics/metrics-lib/src/test/java/io/airbyte/metrics/lib/OpenTelemetryMetricClientTest.java +++ b/airbyte-metrics/metrics-lib/src/test/java/io/airbyte/metrics/lib/OpenTelemetryMetricClientTest.java @@ -4,8 +4,18 @@ package io.airbyte.metrics.lib; +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import com.google.common.collect.Iterables; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.testing.exporter.InMemoryMetricExporter; +import io.opentelemetry.sdk.trace.SdkTracerProvider; +import java.util.List; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -13,12 +23,24 @@ class OpenTelemetryMetricClientTest { OpenTelemetryMetricClient openTelemetryMetricClient; - private final static String EXPORTER_ENDPOINT = "http://localhost:4322"; + private final static String TAG = "tag1"; + + private final static MetricEmittingApp METRIC_EMITTING_APP = MetricEmittingApps.WORKER; + private InMemoryMetricExporter metricExporter; + private SdkMeterProvider metricProvider; @BeforeEach void setUp() { openTelemetryMetricClient = new OpenTelemetryMetricClient(); - openTelemetryMetricClient.initialize(MetricEmittingApps.WORKER, EXPORTER_ENDPOINT); + + Resource resource = Resource.getDefault().toBuilder().put(SERVICE_NAME, METRIC_EMITTING_APP.getApplicationName()).build(); + metricExporter = InMemoryMetricExporter.create(); + SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() + .setResource(resource) + .build(); + openTelemetryMetricClient.initialize(METRIC_EMITTING_APP, metricExporter, sdkTracerProvider, resource); + + metricProvider = openTelemetryMetricClient.getSdkMeterProvider(); } @AfterEach @@ -27,27 +49,63 @@ void tearDown() { } @Test - @DisplayName("there should be no exception if we attempt to emit metrics while publish is false") - public void testPublishTrueNoEmitError() { - Assertions.assertDoesNotThrow(() -> { - openTelemetryMetricClient.gauge(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS, 1); - }); + @DisplayName("Should send out count metric with correct metric name, description and value") + public void testCountSuccess() { + openTelemetryMetricClient.count(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS, 1); + + metricProvider.forceFlush(); + List metricDataList = metricExporter.getFinishedMetricItems(); + MetricData data = Iterables.getOnlyElement(metricDataList); + + assertThat(data.getName()).isEqualTo(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS.getMetricName()); + assertThat(data.getDescription()).isEqualTo(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS.getMetricDescription()); + assertThat(data.getLongSumData().getPoints().stream().anyMatch(longPointData -> longPointData.getValue() == 1L)); } @Test - @DisplayName("there should be no exception if we attempt to emit metrics while publish is true") - public void testPublishFalseNoEmitError() { - Assertions.assertDoesNotThrow(() -> { - openTelemetryMetricClient.gauge(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS, 1); - }); + @DisplayName("Tags should be passed into metrics") + public void testCountWithTagSuccess() { + openTelemetryMetricClient.count(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS, 1, TAG); + + metricProvider.forceFlush(); + List metricDataList = metricExporter.getFinishedMetricItems(); + MetricData data = Iterables.getOnlyElement(metricDataList); + + assertThat(data.getName()).isEqualTo(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS.getMetricName()); + assertThat(data.getDescription()).isEqualTo(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS.getMetricDescription()); + assertThat(data.getLongSumData().getPoints().stream() + .anyMatch( + longPointData -> longPointData.getValue() == 1L && longPointData.getAttributes().get(AttributeKey.stringKey(TAG)).equals(TAG))); } @Test - @DisplayName("there should be no exception if we attempt to emit metrics without initializing") - public void testNoInitializeNoEmitError() { - Assertions.assertDoesNotThrow(() -> { - openTelemetryMetricClient.gauge(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS, 1); - }); + @DisplayName("Should send out gauge metric with correct metric name, description and value") + public void testGaugeSuccess() throws Exception { + openTelemetryMetricClient.gauge(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS, 1); + + metricProvider.forceFlush(); + List metricDataList = metricExporter.getFinishedMetricItems(); + MetricData data = Iterables.getOnlyElement(metricDataList); + + assertThat(data.getName()).isEqualTo(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS.getMetricName()); + assertThat(data.getDescription()).isEqualTo(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS.getMetricDescription()); + assertThat(data.getDoubleGaugeData().getPoints().stream().anyMatch(doublePointData -> doublePointData.getValue() == 1.0)); + } + + @Test + @DisplayName("Should send out histogram metric with correct metric name, description and value") + public void testHistogramSuccess() { + openTelemetryMetricClient.distribution(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS, 10); + openTelemetryMetricClient.distribution(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS, 30); + + metricProvider.forceFlush(); + List metricDataList = metricExporter.getFinishedMetricItems(); + MetricData data = Iterables.getOnlyElement(metricDataList); + + assertThat(data.getName()).isEqualTo(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS.getMetricName()); + assertThat(data.getDescription()).isEqualTo(OssMetricsRegistry.KUBE_POD_PROCESS_CREATE_TIME_MILLISECS.getMetricDescription()); + assertThat(data.getHistogramData().getPoints().stream().anyMatch(histogramPointData -> histogramPointData.getMax() == 30.0)); + assertThat(data.getHistogramData().getPoints().stream().anyMatch(histogramPointData -> histogramPointData.getMin() == 10.0)); } } diff --git a/deps.toml b/deps.toml index cbfb4525be6c9..07d9c11de1e89 100644 --- a/deps.toml +++ b/deps.toml @@ -84,6 +84,8 @@ findsecbugs-plugin = { module = "com.h3xstream.findsecbugs:findsecbugs-plugin", spotbugs-annotations = { module = "com.github.spotbugs:spotbugs-annotations", version = "4.6.0" } otel-bom = {module = "io.opentelemetry:opentelemetry-bom", version = "1.14.0"} otel-semconv = {module = "io.opentelemetry:opentelemetry-semconv", version = "1.14.0-alpha"} +otel-sdk = {module = "io.opentelemetry:opentelemetry-sdk-metrics", version = "1.14.0"} +otel-sdk-testing = {module = "io.opentelemetry:opentelemetry-sdk-metrics-testing", version = "1.13.0-alpha"} [bundles] jackson = ["jackson-databind", "jackson-annotations", "jackson-dataformat", "jackson-datatype"]