Add metrics & micrometer support to spring-boot-autoconfigure (#6270)

* Add metrics & micrometer support to spring-boot-autoconfigure

* Apply suggestions from code review

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>

* code review comments

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
This commit is contained in:
Mateusz Rzeszutek 2022-07-07 16:51:25 +02:00 committed by GitHub
parent 646a724a38
commit 9058ad6f40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 597 additions and 71 deletions

View File

@ -14,7 +14,10 @@ muzzle {
dependencies { dependencies {
library("io.micrometer:micrometer-core:1.5.0") library("io.micrometer:micrometer-core:1.5.0")
implementation("io.opentelemetry:opentelemetry-micrometer1-shim") implementation("io.opentelemetry:opentelemetry-micrometer1-shim") {
// just get the instrumentation, without micrometer itself
exclude("io.micrometer", "micrometer-core")
}
} }
tasks { tasks {

View File

@ -6,21 +6,27 @@ plugins {
group = "io.opentelemetry.instrumentation" group = "io.opentelemetry.instrumentation"
val versions: Map<String, String> by project val versions: Map<String, String> by project
val springBootVersion = versions["org.springframework.boot"]
dependencies { dependencies {
implementation(project(":instrumentation-api-annotation-support")) implementation(project(":instrumentation-api-annotation-support"))
implementation("org.springframework.boot:spring-boot-autoconfigure:${versions["org.springframework.boot"]}") implementation("org.springframework.boot:spring-boot-autoconfigure:$springBootVersion")
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor:${versions["org.springframework.boot"]}") annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor:$springBootVersion")
implementation("javax.validation:validation-api:2.0.1.Final") implementation("javax.validation:validation-api:2.0.1.Final")
implementation(project(":instrumentation:spring:spring-web-3.1:library")) implementation(project(":instrumentation:spring:spring-web-3.1:library"))
implementation(project(":instrumentation:spring:spring-webmvc-3.1:library")) implementation(project(":instrumentation:spring:spring-webmvc-3.1:library"))
implementation(project(":instrumentation:spring:spring-webflux-5.0:library")) implementation(project(":instrumentation:spring:spring-webflux-5.0:library"))
implementation("io.opentelemetry:opentelemetry-micrometer1-shim") {
// just get the instrumentation, without micrometer itself
exclude("io.micrometer", "micrometer-core")
}
compileOnly("org.springframework.boot:spring-boot-starter-aop:${versions["org.springframework.boot"]}") compileOnly("org.springframework.boot:spring-boot-starter-actuator:$springBootVersion")
compileOnly("org.springframework.boot:spring-boot-starter-web:${versions["org.springframework.boot"]}") compileOnly("org.springframework.boot:spring-boot-starter-aop:$springBootVersion")
compileOnly("org.springframework.boot:spring-boot-starter-webflux:${versions["org.springframework.boot"]}") compileOnly("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
compileOnly("org.springframework.boot:spring-boot-starter-webflux:$springBootVersion")
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
compileOnly("io.opentelemetry:opentelemetry-extension-annotations") compileOnly("io.opentelemetry:opentelemetry-extension-annotations")
@ -32,10 +38,11 @@ dependencies {
compileOnly("io.opentelemetry:opentelemetry-exporter-otlp") compileOnly("io.opentelemetry:opentelemetry-exporter-otlp")
compileOnly("io.opentelemetry:opentelemetry-exporter-zipkin") compileOnly("io.opentelemetry:opentelemetry-exporter-zipkin")
testImplementation("org.springframework.boot:spring-boot-starter-aop:${versions["org.springframework.boot"]}") testImplementation("org.springframework.boot:spring-boot-starter-actuator:$springBootVersion")
testImplementation("org.springframework.boot:spring-boot-starter-webflux:${versions["org.springframework.boot"]}") testImplementation("org.springframework.boot:spring-boot-starter-aop:$springBootVersion")
testImplementation("org.springframework.boot:spring-boot-starter-web:${versions["org.springframework.boot"]}") testImplementation("org.springframework.boot:spring-boot-starter-webflux:$springBootVersion")
testImplementation("org.springframework.boot:spring-boot-starter-test:${versions["org.springframework.boot"]}") { testImplementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
testImplementation("org.springframework.boot:spring-boot-starter-test:$springBootVersion") {
exclude("org.junit.vintage", "junit-vintage-engine") exclude("org.junit.vintage", "junit-vintage-engine")
} }

View File

@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure;
import java.time.Duration;
import javax.annotation.Nullable;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "otel.metric.export")
public class MetricExportProperties {
@Nullable private Duration interval;
@Nullable
public Duration getInterval() {
return interval;
}
public void setInterval(@Nullable Duration interval) {
this.interval = interval;
}
}

View File

@ -12,6 +12,11 @@ import io.opentelemetry.instrumentation.spring.autoconfigure.resources.SpringRes
import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider; import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder;
import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
@ -36,7 +41,7 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
* <p>Updates the sampler probability for the configured {@link TracerProvider}. * <p>Updates the sampler probability for the configured {@link TracerProvider}.
*/ */
@Configuration @Configuration
@EnableConfigurationProperties(SamplerProperties.class) @EnableConfigurationProperties({MetricExportProperties.class, SamplerProperties.class})
public class OpenTelemetryAutoConfiguration { public class OpenTelemetryAutoConfiguration {
@Configuration @Configuration
@ -61,6 +66,32 @@ public class OpenTelemetryAutoConfiguration {
.build(); .build();
} }
@Bean
@ConditionalOnMissingBean
public SdkMeterProvider sdkMeterProvider(
MetricExportProperties properties,
ObjectProvider<List<MetricExporter>> metricExportersProvider,
Resource otelResource) {
SdkMeterProviderBuilder meterProviderBuilder = SdkMeterProvider.builder();
metricExportersProvider.getIfAvailable(Collections::emptyList).stream()
.map(metricExporter -> createPeriodicMetricReader(properties, metricExporter))
.forEach(meterProviderBuilder::registerMetricReader);
return meterProviderBuilder.setResource(otelResource).build();
}
private static PeriodicMetricReader createPeriodicMetricReader(
MetricExportProperties properties, MetricExporter metricExporter) {
PeriodicMetricReaderBuilder metricReaderBuilder =
PeriodicMetricReader.builder(metricExporter);
if (properties.getInterval() != null) {
metricReaderBuilder.setInterval(properties.getInterval());
}
return metricReaderBuilder.build();
}
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public Resource otelResource( public Resource otelResource(
@ -76,12 +107,15 @@ public class OpenTelemetryAutoConfiguration {
@Bean @Bean
public OpenTelemetry openTelemetry( public OpenTelemetry openTelemetry(
ObjectProvider<ContextPropagators> propagatorsProvider, SdkTracerProvider tracerProvider) { ObjectProvider<ContextPropagators> propagatorsProvider,
SdkTracerProvider tracerProvider,
SdkMeterProvider meterProvider) {
ContextPropagators propagators = propagatorsProvider.getIfAvailable(ContextPropagators::noop); ContextPropagators propagators = propagatorsProvider.getIfAvailable(ContextPropagators::noop);
return OpenTelemetrySdk.builder() return OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider) .setTracerProvider(tracerProvider)
.setMeterProvider(meterProvider)
.setPropagators(propagators) .setPropagators(propagators)
.build(); .build();
} }

View File

@ -0,0 +1,49 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration for {@link io.opentelemetry.exporter.logging.LoggingSpanExporter} and {@link
* io.opentelemetry.exporter.logging.LoggingMetricExporter}.
*/
@ConfigurationProperties(prefix = "otel.exporter.logging")
public final class LoggingExporterProperties {
private boolean enabled = true;
private final SignalProperties traces = new SignalProperties();
private final SignalProperties metrics = new SignalProperties();
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public SignalProperties getTraces() {
return traces;
}
public SignalProperties getMetrics() {
return metrics;
}
public static class SignalProperties {
private boolean enabled = true;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging;
import io.opentelemetry.exporter.logging.LoggingMetricExporter;
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/** Configures {@link LoggingSpanExporter} bean for tracing. */
@Configuration
@EnableConfigurationProperties(LoggingExporterProperties.class)
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
@ConditionalOnProperty(
prefix = "otel.exporter.logging",
name = {"enabled", "metrics.enabled"},
matchIfMissing = true)
@ConditionalOnClass(LoggingMetricExporter.class)
public class LoggingMetricExporterAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public LoggingMetricExporter otelLoggingMetricExporter() {
return LoggingMetricExporter.create();
}
}

View File

@ -17,9 +17,12 @@ import org.springframework.context.annotation.Configuration;
/** Configures {@link LoggingSpanExporter} bean for tracing. */ /** Configures {@link LoggingSpanExporter} bean for tracing. */
@Configuration @Configuration
@EnableConfigurationProperties(LoggingSpanExporterProperties.class) @EnableConfigurationProperties(LoggingExporterProperties.class)
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class) @AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
@ConditionalOnProperty(prefix = "otel.exporter.logging", name = "enabled", matchIfMissing = true) @ConditionalOnProperty(
prefix = "otel.exporter.logging",
name = {"enabled", "traces.enabled"},
matchIfMissing = true)
@ConditionalOnClass(LoggingSpanExporter.class) @ConditionalOnClass(LoggingSpanExporter.class)
public class LoggingSpanExporterAutoConfiguration { public class LoggingSpanExporterAutoConfiguration {

View File

@ -10,7 +10,8 @@ import javax.annotation.Nullable;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
* Configuration for {@link io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter}. * Configuration for {@link io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter} and {@link
* io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter}.
* *
* <p>Get Exporter Service Name * <p>Get Exporter Service Name
* *
@ -19,11 +20,13 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* <p>Get max wait time for Collector to process Span Batches * <p>Get max wait time for Collector to process Span Batches
*/ */
@ConfigurationProperties(prefix = "otel.exporter.otlp") @ConfigurationProperties(prefix = "otel.exporter.otlp")
public final class OtlpGrpcSpanExporterProperties { public final class OtlpExporterProperties {
private boolean enabled = true; private boolean enabled = true;
@Nullable private String endpoint; @Nullable private String endpoint;
@Nullable private Duration timeout; @Nullable private Duration timeout;
private final SignalProperties traces = new SignalProperties();
private final SignalProperties metrics = new SignalProperties();
public boolean isEnabled() { public boolean isEnabled() {
return enabled; return enabled;
@ -50,4 +53,45 @@ public final class OtlpGrpcSpanExporterProperties {
public void setTimeout(Duration timeout) { public void setTimeout(Duration timeout) {
this.timeout = timeout; this.timeout = timeout;
} }
public SignalProperties getTraces() {
return traces;
}
public SignalProperties getMetrics() {
return metrics;
}
public static class SignalProperties {
private boolean enabled = true;
@Nullable private String endpoint;
@Nullable private Duration timeout;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Nullable
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(@Nullable String endpoint) {
this.endpoint = endpoint;
}
@Nullable
public Duration getTimeout() {
return timeout;
}
public void setTimeout(@Nullable Duration timeout) {
this.timeout = timeout;
}
}
} }

View File

@ -0,0 +1,53 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import java.time.Duration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
@EnableConfigurationProperties(OtlpExporterProperties.class)
@ConditionalOnProperty(
prefix = "otel.exporter.otlp",
name = {"enabled", "metrics.enabled"},
matchIfMissing = true)
@ConditionalOnClass(OtlpGrpcMetricExporter.class)
public class OtlpMetricExporterAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public OtlpGrpcMetricExporter otelOtlpGrpcMetricExporter(OtlpExporterProperties properties) {
OtlpGrpcMetricExporterBuilder builder = OtlpGrpcMetricExporter.builder();
String endpoint = properties.getMetrics().getEndpoint();
if (endpoint == null) {
endpoint = properties.getEndpoint();
}
if (endpoint != null) {
builder.setEndpoint(endpoint);
}
Duration timeout = properties.getMetrics().getTimeout();
if (timeout == null) {
timeout = properties.getTimeout();
}
if (timeout != null) {
builder.setTimeout(timeout);
}
return builder.build();
}
}

View File

@ -8,6 +8,7 @@ package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import java.time.Duration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -23,22 +24,35 @@ import org.springframework.context.annotation.Configuration;
*/ */
@Configuration @Configuration
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class) @AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
@EnableConfigurationProperties(OtlpGrpcSpanExporterProperties.class) @EnableConfigurationProperties(OtlpExporterProperties.class)
@ConditionalOnProperty(prefix = "otel.exporter.otlp", name = "enabled", matchIfMissing = true) @ConditionalOnProperty(
prefix = "otel.exporter.otlp",
name = {"enabled", "traces.enabled"},
matchIfMissing = true)
@ConditionalOnClass(OtlpGrpcSpanExporter.class) @ConditionalOnClass(OtlpGrpcSpanExporter.class)
public class OtlpGrpcSpanExporterAutoConfiguration { public class OtlpSpanExporterAutoConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public OtlpGrpcSpanExporter otelOtlpGrpcSpanExporter(OtlpGrpcSpanExporterProperties properties) { public OtlpGrpcSpanExporter otelOtlpGrpcSpanExporter(OtlpExporterProperties properties) {
OtlpGrpcSpanExporterBuilder builder = OtlpGrpcSpanExporter.builder(); OtlpGrpcSpanExporterBuilder builder = OtlpGrpcSpanExporter.builder();
if (properties.getEndpoint() != null) {
builder.setEndpoint(properties.getEndpoint()); String endpoint = properties.getTraces().getEndpoint();
if (endpoint == null) {
endpoint = properties.getEndpoint();
} }
if (properties.getTimeout() != null) { if (endpoint != null) {
builder.setTimeout(properties.getTimeout()); builder.setEndpoint(endpoint);
} }
Duration timeout = properties.getTraces().getTimeout();
if (timeout == null) {
timeout = properties.getTimeout();
}
if (timeout != null) {
builder.setTimeout(timeout);
}
return builder.build(); return builder.build();
} }
} }

View File

@ -0,0 +1,36 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure.metrics;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.MeterRegistry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.micrometer1shim.OpenTelemetryMeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(MicrometerShimProperties.class)
@ConditionalOnProperty(name = "otel.springboot.micrometer.enabled", matchIfMissing = true)
@AutoConfigureAfter(MetricsAutoConfiguration.class)
@AutoConfigureBefore(CompositeMeterRegistryAutoConfiguration.class)
@ConditionalOnBean(Clock.class)
@ConditionalOnClass(MeterRegistry.class)
public class MicrometerShimAutoConfiguration {
@Bean
public MeterRegistry micrometerShim(OpenTelemetry openTelemetry, Clock micrometerClock) {
return OpenTelemetryMeterRegistry.builder(openTelemetry).setClock(micrometerClock).build();
}
}

View File

@ -3,13 +3,13 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging; package io.opentelemetry.instrumentation.spring.autoconfigure.metrics;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
/** Configuration for {@link io.opentelemetry.exporter.logging.LoggingSpanExporter}. */ @ConfigurationProperties(prefix = "otel.springboot.micrometer")
@ConfigurationProperties(prefix = "otel.exporter.logging") public class MicrometerShimProperties {
public final class LoggingSpanExporterProperties {
private boolean enabled = true; private boolean enabled = true;
public boolean isEnabled() { public boolean isEnabled() {

View File

@ -1,12 +1,14 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.jaeger.JaegerSpanExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpGrpcSpanExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.zipkin.ZipkinSpanExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging.LoggingSpanExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.propagators.PropagationAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration,\ io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.aspects.TraceAspectAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.jaeger.JaegerSpanExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging.LoggingSpanExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpMetricExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpSpanExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.exporters.zipkin.ZipkinSpanExporterAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.httpclients.resttemplate.RestTemplateAutoConfiguration,\ io.opentelemetry.instrumentation.spring.autoconfigure.httpclients.resttemplate.RestTemplateAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.httpclients.webclient.WebClientAutoConfiguration,\ io.opentelemetry.instrumentation.spring.autoconfigure.httpclients.webclient.WebClientAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.webmvc.WebMvcFilterAutoConfiguration,\ io.opentelemetry.instrumentation.spring.autoconfigure.metrics.MicrometerShimAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.aspects.TraceAspectAutoConfiguration,\ io.opentelemetry.instrumentation.spring.autoconfigure.propagators.PropagationAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration,\
io.opentelemetry.instrumentation.spring.autoconfigure.webmvc.WebMvcFilterAutoConfiguration

View File

@ -11,6 +11,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration; import io.opentelemetry.instrumentation.spring.autoconfigure.resources.OtelResourceAutoConfiguration;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SdkTracerProvider;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
@ -44,34 +45,44 @@ class OpenTelemetryAutoConfigurationTest {
assertThat(context) assertThat(context)
.hasBean("customOpenTelemetry") .hasBean("customOpenTelemetry")
.doesNotHaveBean("openTelemetry") .doesNotHaveBean("openTelemetry")
.doesNotHaveBean("sdkTracerProvider")); .doesNotHaveBean("sdkTracerProvider")
.doesNotHaveBean("sdkMeterProvider"));
} }
@Test @Test
@DisplayName( @DisplayName(
"when Application Context DOES NOT contain OpenTelemetry bean should initialize openTelemetry") "when Application Context DOES NOT contain OpenTelemetry bean should initialize openTelemetry")
void initializeTracerProviderAndOpenTelemetry() { void initializeProvidersAndOpenTelemetry() {
this.contextRunner this.contextRunner
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class)) .withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class))
.run(context -> assertThat(context).hasBean("openTelemetry").hasBean("sdkTracerProvider")); .run(
context ->
assertThat(context)
.hasBean("openTelemetry")
.hasBean("sdkTracerProvider")
.hasBean("sdkMeterProvider"));
} }
@Test @Test
@DisplayName( @DisplayName(
"when Application Context DOES NOT contain OpenTelemetry bean but TracerProvider should initialize openTelemetry") "when Application Context DOES NOT contain OpenTelemetry bean but TracerProvider should initialize openTelemetry")
void initializeOpenTelemetry() { void initializeOpenTelemetryWithCustomProviders() {
this.contextRunner this.contextRunner
.withBean( .withBean(
"customTracerProvider", "customTracerProvider",
SdkTracerProvider.class, SdkTracerProvider.class,
() -> SdkTracerProvider.builder().build()) () -> SdkTracerProvider.builder().build())
.withBean(
"customMeterProvider", SdkMeterProvider.class, () -> SdkMeterProvider.builder().build())
.withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class)) .withConfiguration(AutoConfigurations.of(OpenTelemetryAutoConfiguration.class))
.run( .run(
context -> context ->
assertThat(context) assertThat(context)
.hasBean("openTelemetry") .hasBean("openTelemetry")
.hasBean("customTracerProvider") .hasBean("customTracerProvider")
.doesNotHaveBean("sdkTracerProvider")); .doesNotHaveBean("sdkTracerProvider")
.hasBean("customMeterProvider")
.doesNotHaveBean("sdkMeterProvider"));
} }
@Test @Test

View File

@ -0,0 +1,68 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging;
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.exporter.logging.LoggingMetricExporter;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
class LoggingMetricExporterAutoConfigurationTest {
private final ApplicationContextRunner runner =
new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(
OpenTelemetryAutoConfiguration.class,
LoggingMetricExporterAutoConfiguration.class));
@Test
void loggingEnabled() {
runner
.withPropertyValues("otel.exporter.logging.enabled=true")
.run(
context ->
assertThat(
context.getBean("otelLoggingMetricExporter", LoggingMetricExporter.class))
.isNotNull());
}
@Test
void loggingMetricsEnabled() {
runner
.withPropertyValues("otel.exporter.logging.metrics.enabled=true")
.run(
context ->
assertThat(
context.getBean("otelLoggingMetricExporter", LoggingMetricExporter.class))
.isNotNull());
}
@Test
void loggingDisabled() {
runner
.withPropertyValues("otel.exporter.logging.enabled=false")
.run(context -> assertThat(context.containsBean("otelLoggingMetricExporter")).isFalse());
}
@Test
void loggingMetricsDisabled() {
runner
.withPropertyValues("otel.exporter.logging.metrics.enabled=false")
.run(context -> assertThat(context.containsBean("otelLoggingMetricExporter")).isFalse());
}
@Test
void noProperties() {
runner.run(
context ->
assertThat(context.getBean("otelLoggingMetricExporter", LoggingMetricExporter.class))
.isNotNull());
}
}

View File

@ -3,13 +3,12 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters; package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.exporter.logging.LoggingSpanExporter; import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging.LoggingSpanExporterAutoConfiguration;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
@ -27,7 +26,7 @@ class LoggingSpanExporterAutoConfigurationTest {
@Test @Test
@DisplayName("when exporters are ENABLED should initialize LoggingSpanExporter bean") @DisplayName("when exporters are ENABLED should initialize LoggingSpanExporter bean")
void exportersEnabled() { void loggingEnabled() {
this.contextRunner this.contextRunner
.withPropertyValues("otel.exporter.logging.enabled=true") .withPropertyValues("otel.exporter.logging.enabled=true")
.run( .run(
@ -36,18 +35,36 @@ class LoggingSpanExporterAutoConfigurationTest {
.isNotNull()); .isNotNull());
} }
@Test
void loggingTracesEnabled() {
this.contextRunner
.withPropertyValues("otel.exporter.logging.traces.enabled=true")
.run(
context ->
assertThat(context.getBean("otelLoggingSpanExporter", LoggingSpanExporter.class))
.isNotNull());
}
@Test @Test
@DisplayName("when exporters are DISABLED should NOT initialize LoggingSpanExporter bean") @DisplayName("when exporters are DISABLED should NOT initialize LoggingSpanExporter bean")
void disabledProperty() { void loggingDisabled() {
this.contextRunner this.contextRunner
.withPropertyValues("otel.exporter.logging.enabled=false") .withPropertyValues("otel.exporter.logging.enabled=false")
.run(context -> assertThat(context.containsBean("otelLoggingSpanExporter")).isFalse()); .run(context -> assertThat(context.containsBean("otelLoggingSpanExporter")).isFalse());
} }
@Test
@DisplayName("when exporters are DISABLED should NOT initialize LoggingSpanExporter bean")
void loggingTracesDisabled() {
this.contextRunner
.withPropertyValues("otel.exporter.logging.traces.enabled=false")
.run(context -> assertThat(context.containsBean("otelLoggingSpanExporter")).isFalse());
}
@Test @Test
@DisplayName( @DisplayName(
"when exporter enabled property is MISSING should initialize LoggingSpanExporter bean") "when exporter enabled property is MISSING should initialize LoggingSpanExporter bean")
void noProperty() { void exporterPresentByDefault() {
this.contextRunner.run( this.contextRunner.run(
context -> context ->
assertThat(context.getBean("otelLoggingSpanExporter", LoggingSpanExporter.class)) assertThat(context.getBean("otelLoggingSpanExporter", LoggingSpanExporter.class))

View File

@ -0,0 +1,67 @@
/*
* 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 io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
class OtlpMetricExporterAutoConfigurationTest {
private final ApplicationContextRunner runner =
new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(
OpenTelemetryAutoConfiguration.class, OtlpMetricExporterAutoConfiguration.class));
@Test
void otlpEnabled() {
runner
.withPropertyValues("otel.exporter.otlp.enabled=true")
.run(
context ->
assertThat(
context.getBean("otelOtlpGrpcMetricExporter", OtlpGrpcMetricExporter.class))
.isNotNull());
}
@Test
void otlpMetricsEnabled() {
runner
.withPropertyValues("otel.exporter.otlp.metrics.enabled=true")
.run(
context ->
assertThat(
context.getBean("otelOtlpGrpcMetricExporter", OtlpGrpcMetricExporter.class))
.isNotNull());
}
@Test
void otlpDisabled() {
runner
.withPropertyValues("otel.exporter.otlp.enabled=false")
.run(context -> assertThat(context.containsBean("otelOtlpGrpcMetricExporter")).isFalse());
}
@Test
void otlpMetricsDisabled() {
runner
.withPropertyValues("otel.exporter.otlp.metrics.enabled=false")
.run(context -> assertThat(context.containsBean("otelOtlpGrpcMetricExporter")).isFalse());
}
@Test
void exporterPresentByDefault() {
runner.run(
context ->
assertThat(context.getBean("otelOtlpGrpcMetricExporter", OtlpGrpcMetricExporter.class))
.isNotNull());
}
}

View File

@ -3,32 +3,29 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package io.opentelemetry.instrumentation.spring.autoconfigure.exporters; package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration; import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpGrpcSpanExporterAutoConfiguration;
import io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp.OtlpGrpcSpanExporterProperties;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
/** Spring Boot auto configuration test for {@link OtlpGrpcSpanExporterAutoConfiguration}. */ /** Spring Boot auto configuration test for {@link OtlpSpanExporterAutoConfiguration}. */
class OtlpGrpcSpanExporterAutoConfigurationTest { class OtlpSpanExporterAutoConfigurationTest {
private final ApplicationContextRunner contextRunner = private final ApplicationContextRunner contextRunner =
new ApplicationContextRunner() new ApplicationContextRunner()
.withConfiguration( .withConfiguration(
AutoConfigurations.of( AutoConfigurations.of(
OpenTelemetryAutoConfiguration.class, OpenTelemetryAutoConfiguration.class, OtlpSpanExporterAutoConfiguration.class));
OtlpGrpcSpanExporterAutoConfiguration.class));
@Test @Test
@DisplayName("when exporters are ENABLED should initialize OtlpGrpcSpanExporter bean") @DisplayName("when exporters are ENABLED should initialize OtlpGrpcSpanExporter bean")
void exportersEnabled() { void otlpEnabled() {
this.contextRunner this.contextRunner
.withPropertyValues("otel.exporter.otlp.enabled=true") .withPropertyValues("otel.exporter.otlp.enabled=true")
.run( .run(
@ -38,35 +35,33 @@ class OtlpGrpcSpanExporterAutoConfigurationTest {
} }
@Test @Test
@DisplayName( void otlpTracesEnabled() {
"when otel.exporter.otlp properties are set should initialize OtlpGrpcSpanExporterProperties")
void handlesProperties() {
this.contextRunner this.contextRunner
.withPropertyValues( .withPropertyValues("otel.exporter.otlp.traces.enabled=true")
"otel.exporter.otlp.enabled=true",
"otel.exporter.otlp.endpoint=http://localhost:8080/test",
"otel.exporter.otlp.timeout=69ms")
.run( .run(
context -> { context ->
OtlpGrpcSpanExporterProperties otlpSpanExporterProperties = assertThat(context.getBean("otelOtlpGrpcSpanExporter", OtlpGrpcSpanExporter.class))
context.getBean(OtlpGrpcSpanExporterProperties.class); .isNotNull());
assertThat(otlpSpanExporterProperties.getEndpoint())
.isEqualTo("http://localhost:8080/test");
assertThat(otlpSpanExporterProperties.getTimeout()).hasMillis(69);
});
} }
@Test @Test
@DisplayName("when exporters are DISABLED should NOT initialize OtlpGrpcSpanExporter bean") @DisplayName("when exporters are DISABLED should NOT initialize OtlpGrpcSpanExporter bean")
void disabledProperty() { void otlpDisabled() {
this.contextRunner this.contextRunner
.withPropertyValues("otel.exporter.otlp.enabled=false") .withPropertyValues("otel.exporter.otlp.enabled=false")
.run(context -> assertThat(context.containsBean("otelOtlpGrpcSpanExporter")).isFalse()); .run(context -> assertThat(context.containsBean("otelOtlpGrpcSpanExporter")).isFalse());
} }
@Test
void otlpTracesDisabled() {
this.contextRunner
.withPropertyValues("otel.exporter.otlp.traces.enabled=false")
.run(context -> assertThat(context.containsBean("otelOtlpGrpcSpanExporter")).isFalse());
}
@Test @Test
@DisplayName("when otlp enabled property is MISSING should initialize OtlpGrpcSpanExporter bean") @DisplayName("when otlp enabled property is MISSING should initialize OtlpGrpcSpanExporter bean")
void noProperty() { void exporterPresentByDefault() {
this.contextRunner.run( this.contextRunner.run(
context -> context ->
assertThat(context.getBean("otelOtlpGrpcSpanExporter", OtlpGrpcSpanExporter.class)) assertThat(context.getBean("otelOtlpGrpcSpanExporter", OtlpGrpcSpanExporter.class))

View File

@ -0,0 +1,63 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.spring.autoconfigure.metrics;
import static org.assertj.core.api.Assertions.assertThat;
import io.micrometer.core.instrument.MeterRegistry;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import io.opentelemetry.micrometer1shim.OpenTelemetryMeterRegistry;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
class MicrometerShimAutoConfigurationTest {
private final ApplicationContextRunner runner =
new ApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(
OpenTelemetryAutoConfiguration.class, MicrometerShimAutoConfiguration.class));
@Test
void metricsEnabled() {
runner
.withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class))
.withPropertyValues("otel.springboot.micrometer.enabled = true")
.run(
context ->
assertThat(context.getBean("micrometerShim", MeterRegistry.class))
.isNotNull()
.isInstanceOf(OpenTelemetryMeterRegistry.class));
}
@Test
void metricsEnabledByDefault() {
runner
.withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class))
.run(
context ->
assertThat(context.getBean("micrometerShim", MeterRegistry.class))
.isNotNull()
.isInstanceOf(OpenTelemetryMeterRegistry.class));
}
@Test
void metricsDisabled() {
runner
.withConfiguration(AutoConfigurations.of(MetricsAutoConfiguration.class))
.withPropertyValues("otel.springboot.micrometer.enabled = false")
.run(context -> assertThat(context.containsBean("micrometerShim")).isFalse());
}
@Test
void noActuatorAutoConfiguration() {
runner
.withPropertyValues("otel.springboot.micrometer.enabled = true")
.run(context -> assertThat(context.containsBean("micrometerShim")).isFalse());
}
}