Browse Source

Don't override the OpenTelemetry bean in the OTel starter reactive tests (#11256)

Gregor Zeitlinger 10 months ago
parent
commit
5cc3362ed0

+ 1 - 0
settings.gradle.kts

@@ -138,6 +138,7 @@ include(":smoke-tests:images:servlet:servlet-3.0")
 include(":smoke-tests:images:servlet:servlet-5.0")
 include(":smoke-tests:images:spring-boot")
 
+include(":smoke-tests-otel-starter:spring-smoke-testing")
 include(":smoke-tests-otel-starter:spring-boot-3")
 include(":smoke-tests-otel-starter:spring-boot-3-reactive")
 

+ 1 - 1
smoke-tests-otel-starter/spring-boot-3-reactive/build.gradle.kts

@@ -18,7 +18,7 @@ dependencies {
 
   testImplementation("org.springframework.boot:spring-boot-starter-test")
   testImplementation("io.projectreactor:reactor-test")
-  testImplementation(project(":testing-common"))
+  testImplementation(project(":smoke-tests-otel-starter:spring-smoke-testing"))
 }
 
 tasks {

+ 4 - 28
smoke-tests-otel-starter/spring-boot-3-reactive/src/test/java/io/opentelemetry/smoketest/OtelReactiveSpringStarterSmokeTest.java

@@ -7,62 +7,38 @@ package io.opentelemetry.smoketest;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
-import io.opentelemetry.api.OpenTelemetry;
 import io.opentelemetry.api.trace.SpanKind;
-import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
 import io.opentelemetry.semconv.HttpAttributes;
 import io.opentelemetry.semconv.UrlAttributes;
+import io.opentelemetry.spring.smoketest.AbstractSpringStarterSmokeTest;
 import io.opentelemetry.spring.smoketest.OtelReactiveSpringStarterSmokeTestApplication;
 import io.opentelemetry.spring.smoketest.OtelReactiveSpringStarterSmokeTestController;
-import org.junit.jupiter.api.AfterEach;
+import io.opentelemetry.spring.smoketest.SpringSmokeOtelConfiguration;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.system.CapturedOutput;
-import org.springframework.boot.test.system.OutputCaptureExtension;
 import org.springframework.boot.test.web.server.LocalServerPort;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.web.reactive.function.client.WebClient;
 
-@ExtendWith(OutputCaptureExtension.class)
 @SpringBootTest(
     classes = {
       OtelReactiveSpringStarterSmokeTestApplication.class,
-      OtelReactiveSpringStarterSmokeTest.TestConfiguration.class
+      SpringSmokeOtelConfiguration.class
     },
     webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
-class OtelReactiveSpringStarterSmokeTest {
-
-  @RegisterExtension
-  static final LibraryInstrumentationExtension testing = LibraryInstrumentationExtension.create();
+class OtelReactiveSpringStarterSmokeTest extends AbstractSpringStarterSmokeTest {
 
   @LocalServerPort int serverPort;
 
   @Autowired WebClient.Builder webClientBuilder;
   private WebClient webClient;
 
-  @Configuration(proxyBeanMethods = false)
-  static class TestConfiguration {
-    @Bean
-    OpenTelemetry openTelemetry() {
-      return testing.getOpenTelemetry();
-    }
-  }
-
   @BeforeEach
   void setUp() {
     webClient = webClientBuilder.baseUrl("http://localhost:" + serverPort).build();
   }
 
-  @AfterEach
-  void tearDown(CapturedOutput output) {
-    assertThat(output).doesNotContain("WARN").doesNotContain("ERROR");
-  }
-
   @Test
   void webClientAndWebFluxAndR2dbc() {
     webClient

+ 1 - 1
smoke-tests-otel-starter/spring-boot-3/build.gradle.kts

@@ -22,7 +22,7 @@ dependencies {
   implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES))
 
   testImplementation("org.springframework.boot:spring-boot-starter-test")
-  testImplementation(project(":testing-common"))
+  testImplementation(project(":smoke-tests-otel-starter:spring-smoke-testing"))
 }
 
 tasks {

+ 3 - 1
smoke-tests-otel-starter/spring-boot-3/src/main/java/io/opentelemetry/spring/smoketest/OtelSpringStarterSmokeTestController.java

@@ -23,6 +23,8 @@ public class OtelSpringStarterSmokeTestController {
   public static final String REST_CLIENT = "/rest-client";
   public static final String REST_TEMPLATE = "/rest-template";
   public static final String TEST_HISTOGRAM = "histogram-test-otel-spring-starter";
+  public static final String METER_SCOPE_NAME =
+      OtelSpringStarterSmokeTestApplication.class.getName();
   private final LongHistogram histogram;
   private final Optional<RestTemplate> restTemplate;
   private final Optional<RestClient> restClient;
@@ -32,7 +34,7 @@ public class OtelSpringStarterSmokeTestController {
       RestClient.Builder restClientBuilder,
       RestTemplateBuilder restTemplateBuilder,
       Optional<ServletWebServerApplicationContext> server) {
-    Meter meter = openTelemetry.getMeter(OtelSpringStarterSmokeTestApplication.class.getName());
+    Meter meter = openTelemetry.getMeter(METER_SCOPE_NAME);
     histogram = meter.histogramBuilder(TEST_HISTOGRAM).ofLongs().build();
     Optional<String> rootUri = server.map(s -> "http://localhost:" + s.getWebServer().getPort());
     restClient = rootUri.map(uri -> restClientBuilder.baseUrl(uri).build());

+ 76 - 186
smoke-tests-otel-starter/spring-boot-3/src/test/java/io/opentelemetry/smoketest/OtelSpringStarterSmokeTest.java

@@ -6,8 +6,8 @@
 package io.opentelemetry.smoketest;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.awaitility.Awaitility.with;
 
+import io.opentelemetry.api.OpenTelemetry;
 import io.opentelemetry.api.common.AttributeKey;
 import io.opentelemetry.api.common.Attributes;
 import io.opentelemetry.api.trace.SpanKind;
@@ -18,47 +18,28 @@ import io.opentelemetry.instrumentation.spring.autoconfigure.properties.SpringCo
 import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
 import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
 import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
-import io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider;
-import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider;
-import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider;
 import io.opentelemetry.sdk.logs.data.LogRecordData;
-import io.opentelemetry.sdk.logs.export.LogRecordExporter;
-import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
-import io.opentelemetry.sdk.metrics.data.MetricData;
-import io.opentelemetry.sdk.metrics.export.MetricExporter;
 import io.opentelemetry.sdk.resources.Resource;
 import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions;
-import io.opentelemetry.sdk.testing.assertj.TracesAssert;
-import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
-import io.opentelemetry.sdk.testing.exporter.InMemoryMetricExporter;
-import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
-import io.opentelemetry.sdk.trace.data.SpanData;
-import io.opentelemetry.sdk.trace.export.SpanExporter;
 import io.opentelemetry.semconv.HttpAttributes;
 import io.opentelemetry.semconv.UrlAttributes;
 import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes;
 import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
 import io.opentelemetry.semconv.incubating.ServiceIncubatingAttributes;
+import io.opentelemetry.spring.smoketest.AbstractSpringStarterSmokeTest;
 import io.opentelemetry.spring.smoketest.OtelSpringStarterSmokeTestApplication;
 import io.opentelemetry.spring.smoketest.OtelSpringStarterSmokeTestController;
-import java.time.Duration;
+import io.opentelemetry.spring.smoketest.SpringSmokeOtelConfiguration;
+import io.opentelemetry.spring.smoketest.SpringSmokeTestRunner;
 import java.util.Collections;
-import java.util.List;
 import org.assertj.core.api.AbstractCharSequenceAssert;
-import org.awaitility.core.ConditionEvaluationLogger;
-import org.awaitility.core.EvaluatedCondition;
-import org.awaitility.core.TimeoutEvent;
-import org.junit.jupiter.api.AfterEach;
+import org.assertj.core.api.AbstractIterableAssert;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.MethodOrderer;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestMethodOrder;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.system.CapturedOutput;
-import org.springframework.boot.test.system.OutputCaptureExtension;
 import org.springframework.boot.test.web.client.TestRestTemplate;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -72,35 +53,19 @@ import org.springframework.core.env.Environment;
  * <p>The exporters are not reset using {@link org.junit.jupiter.api.BeforeEach}, because it would
  * prevent the telemetry data from the application startup to be asserted.
  */
-@ExtendWith(OutputCaptureExtension.class)
 @SpringBootTest(
     classes = {
       OtelSpringStarterSmokeTestApplication.class,
-      OtelSpringStarterSmokeTest.TestConfiguration.class
+      OtelSpringStarterSmokeTest.TestConfiguration.class,
+      SpringSmokeOtelConfiguration.class
     },
     webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
     properties = {
-      // We set the export interval of the metrics to 100 ms. The default value is 1 minute.
-      "otel.metric.export.interval=100",
-      // We set the export interval of the spans to 100 ms. The default value is 5 seconds.
-      "otel.bsp.schedule.delay=100",
-      // We set the export interval of the logs to 100 ms. The default value is 1 second.
-      "otel.blrp.schedule.delay=100",
       // The headers are simply set here to make sure that headers can be parsed
-      "otel.exporter.otlp.headers.c=3",
-      "otel.traces.exporter=memory",
-      "otel.metrics.exporter=memory",
-      "otel.logs.exporter=memory"
+      "otel.exporter.otlp.headers.c=3"
     })
 @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
-class OtelSpringStarterSmokeTest {
-
-  private static final InMemoryMetricExporter METRIC_EXPORTER =
-      InMemoryMetricExporter.create(AggregationTemporality.DELTA);
-  private static final InMemoryLogRecordExporter LOG_RECORD_EXPORTER =
-      InMemoryLogRecordExporter.create();
-  private static final InMemorySpanExporter SPAN_EXPORTER = InMemorySpanExporter.create();
-  private static final Logger logger = LoggerFactory.getLogger(OtelSpringStarterSmokeTest.class);
+class OtelSpringStarterSmokeTest extends AbstractSpringStarterSmokeTest {
 
   @Autowired private TestRestTemplate testRestTemplate;
 
@@ -109,53 +74,15 @@ class OtelSpringStarterSmokeTest {
   @Autowired private OtelResourceProperties otelResourceProperties;
   @Autowired private OtlpExporterProperties otlpExporterProperties;
 
-  @Configuration(proxyBeanMethods = false)
-  static class TestConfiguration {
-
-    @Bean
-    ConfigurableMetricExporterProvider otlpMetricExporterProvider() {
-      return new ConfigurableMetricExporterProvider() {
-        @Override
-        public MetricExporter createExporter(ConfigProperties configProperties) {
-          return METRIC_EXPORTER;
-        }
-
-        @Override
-        public String getName() {
-          return "memory";
-        }
-      };
-    }
-
-    @Bean
-    ConfigurableSpanExporterProvider otlpSpanExporterProvider() {
-      return new ConfigurableSpanExporterProvider() {
-        @Override
-        public SpanExporter createExporter(ConfigProperties configProperties) {
-          return SPAN_EXPORTER;
-        }
+  @Autowired OpenTelemetry openTelemetry;
 
-        @Override
-        public String getName() {
-          return "memory";
-        }
-      };
-    }
-
-    @Bean
-    ConfigurableLogRecordExporterProvider otlpLogRecordExporterProvider() {
-      return new ConfigurableLogRecordExporterProvider() {
-        @Override
-        public LogRecordExporter createExporter(ConfigProperties configProperties) {
-          return LOG_RECORD_EXPORTER;
-        }
+  @BeforeEach
+  void initOpenTelemetry() {
+    testing = new SpringSmokeTestRunner(openTelemetry);
+  }
 
-        @Override
-        public String getName() {
-          return "memory";
-        }
-      };
-    }
+  @Configuration(proxyBeanMethods = false)
+  static class TestConfiguration {
 
     @Bean
     @Order(1)
@@ -182,17 +109,6 @@ class OtelSpringStarterSmokeTest {
     }
   }
 
-  private static void resetExporters() {
-    SPAN_EXPORTER.reset();
-    METRIC_EXPORTER.reset();
-    LOG_RECORD_EXPORTER.reset();
-  }
-
-  @AfterEach
-  void tearDown(CapturedOutput output) {
-    assertThat(output).doesNotContain("WARN").doesNotContain("ERROR");
-  }
-
   @Test
   void propertyConversion() {
     ConfigProperties configProperties =
@@ -216,53 +132,47 @@ class OtelSpringStarterSmokeTest {
     testRestTemplate.getForObject(OtelSpringStarterSmokeTestController.PING, String.class);
 
     // Span
-    TracesAssert.assertThat(expectSpans(3))
-        .hasTracesSatisfyingExactly(
-            traceAssert ->
-                traceAssert.hasSpansSatisfyingExactly(
-                    spanDataAssert ->
-                        spanDataAssert
-                            .hasKind(SpanKind.CLIENT)
-                            .hasAttribute(
-                                DbIncubatingAttributes.DB_STATEMENT,
-                                "create table test_table (id bigint not null, primary key (id))")),
-            traceAssert ->
-                traceAssert.hasSpansSatisfyingExactly(
-                    clientSpan ->
-                        clientSpan
-                            .hasKind(SpanKind.CLIENT)
-                            .hasAttributesSatisfying(
-                                a -> assertThat(a.get(UrlAttributes.URL_FULL)).endsWith("/ping")),
-                    serverSpan ->
-                        serverSpan
-                            .hasKind(SpanKind.SERVER)
-                            .hasResourceSatisfying(
-                                r ->
-                                    r.hasAttribute(
-                                            AttributeKey.booleanKey("keyFromResourceCustomizer"),
-                                            true)
-                                        .hasAttribute(
-                                            AttributeKey.stringKey("attributeFromYaml"), "true")
-                                        .hasAttribute(
-                                            OpenTelemetryAssertions.satisfies(
-                                                ServiceIncubatingAttributes.SERVICE_INSTANCE_ID,
-                                                AbstractCharSequenceAssert::isNotBlank)))
-                            .hasAttribute(HttpAttributes.HTTP_REQUEST_METHOD, "GET")
-                            .hasAttribute(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200L)
-                            .hasAttribute(HttpAttributes.HTTP_ROUTE, "/ping")));
+    testing.waitAndAssertTraces(
+        traceAssert ->
+            traceAssert.hasSpansSatisfyingExactly(
+                spanDataAssert ->
+                    spanDataAssert
+                        .hasKind(SpanKind.CLIENT)
+                        .hasAttribute(
+                            DbIncubatingAttributes.DB_STATEMENT,
+                            "create table test_table (id bigint not null, primary key (id))")),
+        traceAssert ->
+            traceAssert.hasSpansSatisfyingExactly(
+                clientSpan ->
+                    clientSpan
+                        .hasKind(SpanKind.CLIENT)
+                        .hasAttributesSatisfying(
+                            a -> assertThat(a.get(UrlAttributes.URL_FULL)).endsWith("/ping")),
+                serverSpan ->
+                    serverSpan
+                        .hasKind(SpanKind.SERVER)
+                        .hasResourceSatisfying(
+                            r ->
+                                r.hasAttribute(
+                                        AttributeKey.booleanKey("keyFromResourceCustomizer"), true)
+                                    .hasAttribute(
+                                        AttributeKey.stringKey("attributeFromYaml"), "true")
+                                    .hasAttribute(
+                                        OpenTelemetryAssertions.satisfies(
+                                            ServiceIncubatingAttributes.SERVICE_INSTANCE_ID,
+                                            AbstractCharSequenceAssert::isNotBlank)))
+                        .hasAttribute(HttpAttributes.HTTP_REQUEST_METHOD, "GET")
+                        .hasAttribute(HttpAttributes.HTTP_RESPONSE_STATUS_CODE, 200L)
+                        .hasAttribute(HttpAttributes.HTTP_ROUTE, "/ping")));
 
     // Metric
-    List<MetricData> exportedMetrics = METRIC_EXPORTER.getFinishedMetricItems();
-    assertThat(exportedMetrics)
-        .as("Should contain " + OtelSpringStarterSmokeTestController.TEST_HISTOGRAM + " metric.")
-        .anySatisfy(
-            metric -> {
-              String metricName = metric.getName();
-              assertThat(metricName).isEqualTo(OtelSpringStarterSmokeTestController.TEST_HISTOGRAM);
-            });
+    testing.waitAndAssertMetrics(
+        OtelSpringStarterSmokeTestController.METER_SCOPE_NAME,
+        OtelSpringStarterSmokeTestController.TEST_HISTOGRAM,
+        AbstractIterableAssert::isNotEmpty);
 
     // Log
-    LogRecordData firstLog = LOG_RECORD_EXPORTER.getFinishedLogRecordItems().get(0);
+    LogRecordData firstLog = testing.getExportedLogRecords().get(0);
     assertThat(firstLog.getBody().asString())
         .as("Should instrument logs")
         .startsWith("Starting ")
@@ -284,50 +194,30 @@ class OtelSpringStarterSmokeTest {
   }
 
   private void assertClient(String url) {
-    resetExporters();
+    testing.clearAllExportedData();
 
     testRestTemplate.getForObject(url, String.class);
 
-    TracesAssert.assertThat(expectSpans(4))
-        .hasTracesSatisfyingExactly(
-            traceAssert ->
-                traceAssert.hasSpansSatisfyingExactly(
-                    clientSpan ->
-                        clientSpan
-                            .hasKind(SpanKind.CLIENT)
-                            .hasAttributesSatisfying(
-                                a -> assertThat(a.get(UrlAttributes.URL_FULL)).endsWith(url)),
-                    serverSpan ->
-                        serverSpan
-                            .hasKind(SpanKind.SERVER)
-                            .hasAttribute(HttpAttributes.HTTP_ROUTE, url),
-                    nestedClientSpan ->
-                        nestedClientSpan
-                            .hasKind(SpanKind.CLIENT)
-                            .hasAttributesSatisfying(
-                                a -> assertThat(a.get(UrlAttributes.URL_FULL)).endsWith("/ping")),
-                    nestedServerSpan ->
-                        nestedServerSpan
-                            .hasKind(SpanKind.SERVER)
-                            .hasAttribute(HttpAttributes.HTTP_ROUTE, "/ping")));
-  }
-
-  private static List<SpanData> expectSpans(int spans) {
-    with()
-        .conditionEvaluationListener(
-            new ConditionEvaluationLogger() {
-              @Override
-              public void conditionEvaluated(EvaluatedCondition<Object> condition) {}
-
-              @Override
-              public void onTimeout(TimeoutEvent timeoutEvent) {
-                logger.info("Spans: {}", SPAN_EXPORTER.getFinishedSpanItems());
-              }
-            })
-        .await()
-        .atMost(Duration.ofSeconds(30))
-        .until(() -> SPAN_EXPORTER.getFinishedSpanItems().size() == spans);
-
-    return SPAN_EXPORTER.getFinishedSpanItems();
+    testing.waitAndAssertTraces(
+        traceAssert ->
+            traceAssert.hasSpansSatisfyingExactly(
+                clientSpan ->
+                    clientSpan
+                        .hasKind(SpanKind.CLIENT)
+                        .hasAttributesSatisfying(
+                            a -> assertThat(a.get(UrlAttributes.URL_FULL)).endsWith(url)),
+                serverSpan ->
+                    serverSpan
+                        .hasKind(SpanKind.SERVER)
+                        .hasAttribute(HttpAttributes.HTTP_ROUTE, url),
+                nestedClientSpan ->
+                    nestedClientSpan
+                        .hasKind(SpanKind.CLIENT)
+                        .hasAttributesSatisfying(
+                            a -> assertThat(a.get(UrlAttributes.URL_FULL)).endsWith("/ping")),
+                nestedServerSpan ->
+                    nestedServerSpan
+                        .hasKind(SpanKind.SERVER)
+                        .hasAttribute(HttpAttributes.HTTP_ROUTE, "/ping")));
   }
 }

+ 19 - 0
smoke-tests-otel-starter/spring-smoke-testing/build.gradle.kts

@@ -0,0 +1,19 @@
+plugins {
+  id("otel.java-conventions")
+}
+
+otelJava {
+  minJavaVersionSupported.set(JavaVersion.VERSION_17)
+}
+
+val versions: Map<String, String> by project
+val springBootVersion = versions["org.springframework.boot"]
+
+dependencies {
+  // spring depdenencies are compile only to enable testing against different versions of spring
+  compileOnly("org.springframework.boot:spring-boot-starter:$springBootVersion")
+  compileOnly("org.springframework.boot:spring-boot-starter-test:$springBootVersion")
+  api(project(":testing-common"))
+  api(project(":instrumentation:spring:spring-boot-autoconfigure"))
+  api("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
+}

+ 35 - 0
smoke-tests-otel-starter/spring-smoke-testing/src/main/java/io/opentelemetry/spring/smoketest/AbstractSpringStarterSmokeTest.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.spring.smoketest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.opentelemetry.api.OpenTelemetry;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.system.CapturedOutput;
+import org.springframework.boot.test.system.OutputCaptureExtension;
+
+@ExtendWith(OutputCaptureExtension.class)
+public abstract class AbstractSpringStarterSmokeTest {
+
+  @Autowired OpenTelemetry openTelemetry;
+
+  protected SpringSmokeTestRunner testing;
+
+  @BeforeEach
+  void initOpenTelemetry() {
+    testing = new SpringSmokeTestRunner(openTelemetry);
+  }
+
+  @AfterEach
+  void checkSpringLogs(CapturedOutput output) {
+    // warnings are emitted if the auto-configuration have non-fatal problems
+    assertThat(output).doesNotContain("WARN").doesNotContain("ERROR");
+  }
+}

+ 93 - 0
smoke-tests-otel-starter/spring-smoke-testing/src/main/java/io/opentelemetry/spring/smoketest/SpringSmokeOtelConfiguration.java

@@ -0,0 +1,93 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.spring.smoketest;
+
+import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
+import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
+import io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider;
+import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider;
+import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider;
+import io.opentelemetry.sdk.logs.export.LogRecordExporter;
+import io.opentelemetry.sdk.metrics.export.MetricExporter;
+import io.opentelemetry.sdk.trace.export.SpanExporter;
+import java.util.Map;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration(proxyBeanMethods = false)
+public class SpringSmokeOtelConfiguration {
+
+  private static final String MEMORY_EXPORTER = "memory";
+
+  @Bean
+  AutoConfigurationCustomizerProvider autoConfigurationCustomizerProvider() {
+    return provider -> provider.addPropertiesSupplier(SpringSmokeOtelConfiguration::getProperties);
+  }
+
+  private static Map<String, String> getProperties() {
+    return Map.of(
+        // We set the export interval of the metrics to 100 ms. The default value is 1 minute.
+        "otel.metric.export.interval",
+        "100",
+        // We set the export interval of the spans to 100 ms. The default value is 5 seconds.
+        "otel.bsp.schedule.delay",
+        "100",
+        // We set the export interval of the logs to 100 ms. The default value is 1 second.
+        "otel.blrp.schedule.delay",
+        "100",
+        "otel.traces.exporter",
+        MEMORY_EXPORTER,
+        "otel.metrics.exporter",
+        MEMORY_EXPORTER,
+        "otel.logs.exporter",
+        MEMORY_EXPORTER);
+  }
+
+  @Bean
+  ConfigurableMetricExporterProvider otlpMetricExporterProvider() {
+    return new ConfigurableMetricExporterProvider() {
+      @Override
+      public MetricExporter createExporter(ConfigProperties configProperties) {
+        return SpringSmokeTestRunner.testMetricExporter;
+      }
+
+      @Override
+      public String getName() {
+        return MEMORY_EXPORTER;
+      }
+    };
+  }
+
+  @Bean
+  ConfigurableSpanExporterProvider otlpSpanExporterProvider() {
+    return new ConfigurableSpanExporterProvider() {
+      @Override
+      public SpanExporter createExporter(ConfigProperties configProperties) {
+        return SpringSmokeTestRunner.testSpanExporter;
+      }
+
+      @Override
+      public String getName() {
+        return MEMORY_EXPORTER;
+      }
+    };
+  }
+
+  @Bean
+  ConfigurableLogRecordExporterProvider otlpLogRecordExporterProvider() {
+    return new ConfigurableLogRecordExporterProvider() {
+      @Override
+      public LogRecordExporter createExporter(ConfigProperties configProperties) {
+        return SpringSmokeTestRunner.testLogRecordExporter;
+      }
+
+      @Override
+      public String getName() {
+        return MEMORY_EXPORTER;
+      }
+    };
+  }
+}

+ 72 - 0
smoke-tests-otel-starter/spring-smoke-testing/src/main/java/io/opentelemetry/spring/smoketest/SpringSmokeTestRunner.java

@@ -0,0 +1,72 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.spring.smoketest;
+
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.instrumentation.testing.InstrumentationTestRunner;
+import io.opentelemetry.sdk.logs.data.LogRecordData;
+import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
+import io.opentelemetry.sdk.metrics.data.MetricData;
+import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
+import io.opentelemetry.sdk.testing.exporter.InMemoryMetricExporter;
+import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
+import io.opentelemetry.sdk.trace.data.SpanData;
+import java.util.List;
+
+/**
+ * An implementation of {@link InstrumentationTestRunner} that initializes OpenTelemetry SDK and
+ * uses in-memory exporter to collect traces and metrics.
+ */
+public final class SpringSmokeTestRunner extends InstrumentationTestRunner {
+
+  static final InMemorySpanExporter testSpanExporter = InMemorySpanExporter.create();
+  static final InMemoryMetricExporter testMetricExporter =
+      InMemoryMetricExporter.create(AggregationTemporality.DELTA);
+  static final InMemoryLogRecordExporter testLogRecordExporter = InMemoryLogRecordExporter.create();
+  static OpenTelemetry openTelemetry;
+
+  public SpringSmokeTestRunner(OpenTelemetry openTelemetry) {
+    super(openTelemetry);
+  }
+
+  @Override
+  public void beforeTestClass() {}
+
+  @Override
+  public void afterTestClass() {}
+
+  @Override
+  public void clearAllExportedData() {
+    testSpanExporter.reset();
+    testMetricExporter.reset();
+    testLogRecordExporter.reset();
+  }
+
+  @Override
+  public OpenTelemetry getOpenTelemetry() {
+    return openTelemetry;
+  }
+
+  @Override
+  public List<SpanData> getExportedSpans() {
+    return testSpanExporter.getFinishedSpanItems();
+  }
+
+  @Override
+  public List<MetricData> getExportedMetrics() {
+    return testMetricExporter.getFinishedMetricItems();
+  }
+
+  @Override
+  public List<LogRecordData> getExportedLogRecords() {
+    return testLogRecordExporter.getFinishedLogRecordItems();
+  }
+
+  @Override
+  public boolean forceFlushCalled() {
+    throw new UnsupportedOperationException();
+  }
+}