Skip to content

Commit

Permalink
re-use sdk logic for configuring otlp exporters
Browse files Browse the repository at this point in the history
  • Loading branch information
zeitlinger committed Jan 22, 2024
1 parent b6b4ce3 commit e24ba9e
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 200 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;

import io.opentelemetry.instrumentation.spring.autoconfigure.MapConverterTestAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.SpringResourceConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.util.Arrays;
import java.util.stream.Stream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.core.env.Environment;
import org.springframework.expression.spel.standard.SpelExpressionParser;

class OtlpExporterPropertiesTest {

private static final String[] HEADER_KEYS = {
"otel.exporter.otlp.traces.headers",
"otel.exporter.otlp.metrics.headers",
"otel.exporter.otlp.logs.headers",
"otel.exporter.otlp.headers",
};

private final ApplicationContextRunner contextRunner =
new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(
OpenTelemetryAutoConfiguration.class, MapConverterTestAutoConfiguration.class));

public static Stream<Arguments> headerKeys() {
return Arrays.stream(HEADER_KEYS).map(Arguments::of);
}

@Test
@DisplayName("test all property types")
void allTypes() {
this.contextRunner
.withPropertyValues(
"otel.exporter.otlp.enabled=true",
"otel.exporter.otlp.timeout=1s",
"otel.exporter.otlp.compression=gzip")
.run(
context -> {
ConfigProperties config = getConfig(context);
assertThat(config.getString("otel.exporter.otlp.compression")).isEqualTo("gzip");
assertThat(config.getBoolean("otel.exporter.otlp.enabled")).isTrue();
assertThat(config.getDuration("otel.exporter.otlp.timeout"))
.isEqualByComparingTo(java.time.Duration.ofSeconds(1));
});
}

@ParameterizedTest
@MethodSource("headerKeys")
@DisplayName("should map headers from spring properties")
void mapFlatHeaders(String key) {
this.contextRunner
.withSystemProperties(key + "=a=1,b=2")
.run(
context ->
assertThat(getConfig(context).getMap(key))
.containsExactly(entry("a", "1"), entry("b", "2")));
}

@ParameterizedTest
@MethodSource("headerKeys")
@DisplayName("should map headers from spring application.yaml")
void mapObjectHeaders(String key) {
this.contextRunner
.withPropertyValues(key + ".a=1", key + ".b=2")
.run(
context ->
assertThat(getConfig(context).getMap(key))
.containsExactly(entry("a", "1"), entry("b", "2")));
}

private static ConfigProperties getConfig(AssertableApplicationContext context) {
return new SpringResourceConfigProperties(
context.getBean("environment", Environment.class),
new SpelExpressionParser(),
context.getBean(OtlpExporterProperties.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@

import static org.assertj.core.api.Assertions.assertThat;

import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter;
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
Expand Down Expand Up @@ -64,39 +61,4 @@ void otlpLogsDisabled() {
.withPropertyValues("otel.logs.exporter=none")
.run(context -> assertThat(context.containsBean("otelOtlpLogRecordExporter")).isFalse());
}

@Test
void otlpHttpUsedByDefault() {
runner.run(
context ->
assertThat(
context.getBean("otelOtlpLogRecordExporter", OtlpHttpLogRecordExporter.class))
.isNotNull());
}

@Test
@DisplayName("use grpc when protocol set")
void useGrpc() {
runner
.withPropertyValues("otel.exporter.otlp.protocol=grpc")
.run(
context ->
assertThat(
context.getBean(
"otelOtlpLogRecordExporter", OtlpGrpcLogRecordExporter.class))
.isNotNull());
}

@Test
@DisplayName("use http when unknown protocol set")
void useHttpWhenAnUnknownProtocolIsSet() {
runner
.withPropertyValues("otel.exporter.otlp.protocol=unknown")
.run(
context ->
assertThat(
context.getBean(
"otelOtlpLogRecordExporter", OtlpHttpLogRecordExporter.class))
.isNotNull());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,9 @@
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
Expand All @@ -35,32 +31,6 @@ void otlpEnabled() {
.isNotNull());
}

@Test
@DisplayName("use grpc when protocol set")
void useGrpc() {
runner
.withPropertyValues("otel.exporter.otlp.protocol=grpc")
.run(
context ->
assertThat(context.getBean("otelOtlpMetricExporter", OtlpGrpcMetricExporter.class))
.isNotNull());
}

@Test
@DisplayName("use http when unknown protocol set")
void useHttpWhenAnUnknownProtocolIsSet() {
runner
.withPropertyValues("otel.exporter.otlp.protocol=unknown")
.run(
context ->
assertThatThrownBy(
() ->
context.getBean("otelOtlpMetricExporter", OtlpHttpMetricExporter.class))
.rootCause()
.isInstanceOf(ConfigurationException.class)
.hasMessage("Unsupported OTLP metrics protocol: unknown"));
}

@Test
void otlpMetricsEnabled() {
runner
Expand Down Expand Up @@ -91,12 +61,4 @@ void otlpMetricsDisabled() {
.withPropertyValues("otel.metrics.exporter=none")
.run(context -> assertThat(context.containsBean("otelOtlpMetricExporter")).isFalse());
}

@Test
void otlpHttpUsedByDefault() {
runner.run(
context ->
assertThat(context.getBean("otelOtlpMetricExporter", OtlpHttpMetricExporter.class))
.isNotNull());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,21 @@

import static org.assertj.core.api.Assertions.assertThat;

import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.instrumentation.spring.autoconfigure.MapConverterTestAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.util.stream.Collectors;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;

/** Spring Boot auto configuration test for {@link OtlpSpanExporterAutoConfiguration}. */
class OtlpSpanExporterAutoConfigurationTest {

private final OtlpExporterProperties otlpExporterProperties =
Mockito.mock(OtlpExporterProperties.class);

private final ApplicationContextRunner contextRunner =
new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(
OpenTelemetryAutoConfiguration.class,
OtlpSpanExporterAutoConfiguration.class,
MapConverterTestAutoConfiguration.class));

@AfterEach
void tearDown() {
Mockito.verifyNoMoreInteractions(otlpExporterProperties);
}
OpenTelemetryAutoConfiguration.class, OtlpSpanExporterAutoConfiguration.class));

@Test
@DisplayName("when exporters are ENABLED should initialize OtlpHttpSpanExporter bean")
Expand Down Expand Up @@ -75,110 +58,4 @@ void otlpTracesDisabledOld() {
.withPropertyValues("otel.exporter.otlp.traces.enabled=false")
.run(context -> assertThat(context.containsBean("otelOtlpSpanExporter")).isFalse());
}

@Test
void otlpTracesDisabled() {
this.contextRunner
.withPropertyValues("otel.traces.exporter=none")
.run(context -> assertThat(context.containsBean("otelOtlpSpanExporter")).isFalse());
}

@Test
@DisplayName("when otlp enabled property is MISSING should initialize OtlpHttpSpanExporter bean")
void exporterPresentByDefault() {
this.contextRunner.run(
context ->
assertThat(context.getBean("otelOtlpSpanExporter", OtlpHttpSpanExporter.class))
.isNotNull());
}

@Test
@DisplayName("use http/protobuf when protocol set")
void useHttp() {
this.contextRunner
// .withBean(OtlpExporterProperties.class, () -> otlpExporterProperties)
.withPropertyValues(
"otel.exporter.otlp.enabled=true",
"otel.exporter.otlp.protocol=http/protobuf",
"otel.exporter.otlp.endpoint=http://localhost:4317",
"otel.exporter.otlp.headers.x=1",
"otel.exporter.otlp.headers.y=2",
"otel.exporter.otlp.timeout=1s")
.run(
context ->
assertThat(context.getBean("otelOtlpSpanExporter", OtlpHttpSpanExporter.class))
.hasFieldOrProperty("delegate")
.asString()
.contains("x=OBFUSCATED, y=OBFUSCATED"));

// Mockito.verify(otlpHttpSpanExporterBuilder).build();
//
// Mockito.verify(otlpHttpSpanExporterBuilder).setEndpoint("http://localhost:4317/v1/traces");
// Mockito.verify(otlpHttpSpanExporterBuilder).addHeader("x", "1");
// Mockito.verify(otlpHttpSpanExporterBuilder).addHeader("y", "2");
// Mockito.verify(otlpHttpSpanExporterBuilder).setTimeout(java.time.Duration.ofSeconds(1));
// Mockito.verifyNoMoreInteractions(otlpHttpSpanExporterBuilder);
}

@Test
@DisplayName("use http/protobuf with environment variables for headers using the MapConverter")
void useHttpWithEnv() {
this.contextRunner
// .withBean(OtlpHttpSpanExporterBuilder.class, () -> otlpHttpSpanExporterBuilder)
.withPropertyValues(
"otel.exporter.otlp.enabled=true", "otel.exporter.otlp.protocol=http/protobuf")
// are similar to environment variables in that they use the same converters
.withSystemProperties("otel.exporter.otlp.headers=x=1,y=2")
.run(
context ->
assertThat(context.getBean("otelOtlpSpanExporter", OtlpHttpSpanExporter.class))
.hasFieldOrProperty("delegate")
.asString()
.contains("x=OBFUSCATED, y=OBFUSCATED"));

// Mockito.verify(otlpHttpSpanExporterBuilder).build();
// Mockito.verify(otlpHttpSpanExporterBuilder).addHeader("x", "1");
// Mockito.verify(otlpHttpSpanExporterBuilder).addHeader("y", "2");
// Mockito.verifyNoMoreInteractions(otlpHttpSpanExporterBuilder);
}

@Test
@DisplayName("use grpc when protocol set")
void useGrpc() {
this.contextRunner
.withPropertyValues("otel.exporter.otlp.protocol=grpc")
.run(
context ->
assertThat(context.getBean(OtlpGrpcSpanExporter.class))
.as("Should contain the gRPC span exporter when grpc is set")
.isNotNull());
}

@Test
@DisplayName("use http when unknown protocol set")
void useHttpWhenAnUnknownProtocolIsSet() {
this.contextRunner
.withPropertyValues("otel.exporter.otlp.protocol=unknown")
.run(
context ->
assertThat(context.getBean(OtlpHttpSpanExporter.class))
.as("Should contain the http span exporter when an unknown is set")
.isNotNull());
}

@Test
@DisplayName("logging exporter can still be configured")
void loggingExporter() {
this.contextRunner
.withBean(
LoggingSpanExporter.class,
LoggingSpanExporter::create,
bd -> bd.setDestroyMethodName(""))
.run(
context ->
assertThat(
context.getBeanProvider(SpanExporter.class).stream()
.collect(Collectors.toList()))
.hasSize(2));
}
}

0 comments on commit e24ba9e

Please sign in to comment.