OtelSpringStarterSmokeTest.java 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /*
  2. * Copyright The OpenTelemetry Authors
  3. * SPDX-License-Identifier: Apache-2.0
  4. */
  5. package io.opentelemetry.smoketest;
  6. import static org.assertj.core.api.Assertions.assertThat;
  7. import io.opentelemetry.api.common.AttributeKey;
  8. import io.opentelemetry.api.common.Attributes;
  9. import io.opentelemetry.api.trace.SpanKind;
  10. import io.opentelemetry.instrumentation.spring.autoconfigure.properties.OtelResourceProperties;
  11. import io.opentelemetry.instrumentation.spring.autoconfigure.properties.OtlpExporterProperties;
  12. import io.opentelemetry.instrumentation.spring.autoconfigure.properties.PropagationProperties;
  13. import io.opentelemetry.instrumentation.spring.autoconfigure.properties.SpringConfigProperties;
  14. import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider;
  15. import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
  16. import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
  17. import io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider;
  18. import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider;
  19. import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider;
  20. import io.opentelemetry.sdk.logs.data.LogRecordData;
  21. import io.opentelemetry.sdk.logs.export.LogRecordExporter;
  22. import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
  23. import io.opentelemetry.sdk.metrics.data.MetricData;
  24. import io.opentelemetry.sdk.metrics.export.MetricExporter;
  25. import io.opentelemetry.sdk.resources.Resource;
  26. import io.opentelemetry.sdk.testing.assertj.TracesAssert;
  27. import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
  28. import io.opentelemetry.sdk.testing.exporter.InMemoryMetricExporter;
  29. import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
  30. import io.opentelemetry.sdk.trace.data.SpanData;
  31. import io.opentelemetry.sdk.trace.export.SpanExporter;
  32. import io.opentelemetry.semconv.SemanticAttributes;
  33. import io.opentelemetry.spring.smoketest.OtelSpringStarterSmokeTestApplication;
  34. import io.opentelemetry.spring.smoketest.OtelSpringStarterSmokeTestController;
  35. import java.util.Collections;
  36. import java.util.List;
  37. import org.junit.jupiter.api.Test;
  38. import org.springframework.beans.factory.annotation.Autowired;
  39. import org.springframework.boot.test.context.SpringBootTest;
  40. import org.springframework.boot.test.web.client.TestRestTemplate;
  41. import org.springframework.context.annotation.Bean;
  42. import org.springframework.context.annotation.Configuration;
  43. import org.springframework.core.annotation.Order;
  44. import org.springframework.core.env.Environment;
  45. @SpringBootTest(
  46. classes = {
  47. OtelSpringStarterSmokeTestApplication.class,
  48. OtelSpringStarterSmokeTest.TestConfiguration.class
  49. },
  50. webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
  51. properties = {
  52. "otel.traces.exporter=none",
  53. "otel.metrics.exporter=none",
  54. "otel.logs.exporter=none",
  55. "otel.metric.export.interval=100",
  56. "otel.exporter.otlp.headers=a=1,b=2",
  57. "otel.traces.exporter=memory",
  58. "otel.metrics.exporter=memory",
  59. "otel.logs.exporter=memory"
  60. // We set the export interval of the metrics to 100 ms. The default value is 1 minute.
  61. // the headers are simply set here to make sure that headers can be parsed
  62. })
  63. class OtelSpringStarterSmokeTest {
  64. public static final InMemoryMetricExporter METRIC_EXPORTER =
  65. InMemoryMetricExporter.create(AggregationTemporality.DELTA);
  66. private static final InMemoryLogRecordExporter LOG_RECORD_EXPORTER =
  67. InMemoryLogRecordExporter.create();
  68. public static final InMemorySpanExporter SPAN_EXPORTER = InMemorySpanExporter.create();
  69. @Autowired private TestRestTemplate testRestTemplate;
  70. @Autowired private Environment environment;
  71. @Autowired private PropagationProperties propagationProperties;
  72. @Autowired private OtelResourceProperties otelResourceProperties;
  73. @Autowired private OtlpExporterProperties otlpExporterProperties;
  74. @Configuration(proxyBeanMethods = false)
  75. static class TestConfiguration {
  76. @Bean
  77. ConfigurableMetricExporterProvider otlpMetricExporterProvider() {
  78. return new ConfigurableMetricExporterProvider() {
  79. @Override
  80. public MetricExporter createExporter(ConfigProperties configProperties) {
  81. return METRIC_EXPORTER;
  82. }
  83. @Override
  84. public String getName() {
  85. return "memory";
  86. }
  87. };
  88. }
  89. @Bean
  90. ConfigurableSpanExporterProvider otlpSpanExporterProvider() {
  91. return new ConfigurableSpanExporterProvider() {
  92. @Override
  93. public SpanExporter createExporter(ConfigProperties configProperties) {
  94. return SPAN_EXPORTER;
  95. }
  96. @Override
  97. public String getName() {
  98. return "memory";
  99. }
  100. };
  101. }
  102. @Bean
  103. ConfigurableLogRecordExporterProvider otlpLogRecordExporterProvider() {
  104. return new ConfigurableLogRecordExporterProvider() {
  105. @Override
  106. public LogRecordExporter createExporter(ConfigProperties configProperties) {
  107. return LOG_RECORD_EXPORTER;
  108. }
  109. @Override
  110. public String getName() {
  111. return "memory";
  112. }
  113. };
  114. }
  115. @Bean
  116. @Order(1)
  117. AutoConfigurationCustomizerProvider hiddenPropagatorCustomizer() {
  118. return customizer ->
  119. customizer.addResourceCustomizer(
  120. (resource, config) ->
  121. resource.merge(
  122. Resource.create(
  123. Attributes.of(
  124. AttributeKey.booleanKey("keyFromResourceCustomizer"), false))));
  125. }
  126. @Bean
  127. @Order(2)
  128. AutoConfigurationCustomizerProvider propagatorCustomizer() {
  129. return customizer ->
  130. customizer.addResourceCustomizer(
  131. (resource, config) ->
  132. resource.merge(
  133. Resource.create(
  134. Attributes.of(
  135. AttributeKey.booleanKey("keyFromResourceCustomizer"), true))));
  136. }
  137. }
  138. @Test
  139. void propertyConversion() {
  140. ConfigProperties configProperties =
  141. SpringConfigProperties.create(
  142. environment,
  143. otlpExporterProperties,
  144. otelResourceProperties,
  145. propagationProperties,
  146. DefaultConfigProperties.createFromMap(Collections.emptyMap()));
  147. assertThat(configProperties.getMap("otel.exporter.otlp.headers"))
  148. .containsEntry("a", "1")
  149. .containsEntry("b", "2");
  150. assertThat(configProperties.getList("otel.propagators")).containsExactly("b3");
  151. }
  152. @Test
  153. void shouldSendTelemetry() throws InterruptedException {
  154. testRestTemplate.getForObject(OtelSpringStarterSmokeTestController.URL, String.class);
  155. Thread.sleep(5_000); // Sleep time could be potentially reduced and perhaps removed with
  156. // https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/8962
  157. // and https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/8963
  158. List<SpanData> exportedSpans = SPAN_EXPORTER.getFinishedSpanItems();
  159. // Span
  160. TracesAssert.assertThat(exportedSpans)
  161. .hasTracesSatisfyingExactly(
  162. traceAssert ->
  163. traceAssert.hasSpansSatisfyingExactly(
  164. spanDataAssert ->
  165. spanDataAssert
  166. .hasKind(SpanKind.CLIENT)
  167. .hasAttribute(
  168. SemanticAttributes.DB_STATEMENT,
  169. "create table test_table (id bigint not null, primary key (id))")),
  170. traceAssert ->
  171. traceAssert.hasSpansSatisfyingExactly(
  172. spanDataAssert ->
  173. spanDataAssert
  174. .hasKind(SpanKind.SERVER)
  175. .hasResourceSatisfying(
  176. r ->
  177. r.hasAttribute(
  178. AttributeKey.booleanKey("keyFromResourceCustomizer"),
  179. true)
  180. .hasAttribute(
  181. AttributeKey.stringKey("attributeFromYaml"), "true"))
  182. .hasAttribute(SemanticAttributes.HTTP_REQUEST_METHOD, "GET")
  183. .hasAttribute(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE, 200L)
  184. .hasAttribute(SemanticAttributes.HTTP_ROUTE, "/ping")));
  185. // Metric
  186. List<MetricData> exportedMetrics = METRIC_EXPORTER.getFinishedMetricItems();
  187. assertThat(exportedMetrics)
  188. .as("Should contain " + OtelSpringStarterSmokeTestController.TEST_HISTOGRAM + " metric.")
  189. .anySatisfy(
  190. metric -> {
  191. String metricName = metric.getName();
  192. assertThat(metricName).isEqualTo(OtelSpringStarterSmokeTestController.TEST_HISTOGRAM);
  193. });
  194. // Log
  195. List<LogRecordData> logs = LOG_RECORD_EXPORTER.getFinishedLogRecordItems();
  196. LogRecordData firstLog = logs.get(0);
  197. assertThat(firstLog.getBody().asString())
  198. .as("Should instrument logs")
  199. .startsWith("Starting ")
  200. .contains(this.getClass().getSimpleName());
  201. assertThat(firstLog.getAttributes().asMap())
  202. .as("Should capture code attributes")
  203. .containsEntry(
  204. SemanticAttributes.CODE_NAMESPACE, "org.springframework.boot.StartupInfoLogger");
  205. }
  206. }