diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpGrpcSpanExporterBuilderCustomizer.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpGrpcSpanExporterBuilderCustomizer.java new file mode 100644 index 000000000000..3a82e97f6746 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpGrpcSpanExporterBuilderCustomizer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure.tracing.otlp; + +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder; + +/** + * Callback interface that can be implemented by beans wishing to customize the + * {@link OtlpGrpcSpanExporterBuilder} whilst retaining default auto-configuration. + * + * @author Dmytro Nosan + * @since 3.5.0 + */ +@FunctionalInterface +public interface OtlpGrpcSpanExporterBuilderCustomizer { + + /** + * Customize the {@link OtlpGrpcSpanExporterBuilder}. + * @param builder the builder to customize + */ + void customize(OtlpGrpcSpanExporterBuilder builder); + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpHttpSpanExporterBuilderCustomizer.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpHttpSpanExporterBuilderCustomizer.java new file mode 100644 index 000000000000..45ef2b3ac902 --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpHttpSpanExporterBuilderCustomizer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure.tracing.otlp; + +import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder; + +/** + * Callback interface that can be implemented by beans wishing to customize the + * {@link OtlpHttpSpanExporterBuilder} whilst retaining default auto-configuration. + * + * @author Dmytro Nosan + * @since 3.5.0 + */ +@FunctionalInterface +public interface OtlpHttpSpanExporterBuilderCustomizer { + + /** + * Customize the {@link OtlpHttpSpanExporterBuilder}. + * @param builder the builder to customize + */ + void customize(OtlpHttpSpanExporterBuilder builder); + +} diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java index 892f544cec38..deeaddecd2fa 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingConfigurations.java @@ -83,7 +83,8 @@ static class Exporters { @Bean @ConditionalOnProperty(name = "management.otlp.tracing.transport", havingValue = "http", matchIfMissing = true) OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpTracingProperties properties, - OtlpTracingConnectionDetails connectionDetails, ObjectProvider meterProvider) { + OtlpTracingConnectionDetails connectionDetails, ObjectProvider meterProvider, + ObjectProvider customizers) { OtlpHttpSpanExporterBuilder builder = OtlpHttpSpanExporter.builder() .setEndpoint(connectionDetails.getUrl(Transport.HTTP)) .setTimeout(properties.getTimeout()) @@ -91,13 +92,15 @@ OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpTracingProperties properties, .setCompression(properties.getCompression().name().toLowerCase(Locale.ROOT)); properties.getHeaders().forEach(builder::addHeader); meterProvider.ifAvailable(builder::setMeterProvider); + customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder.build(); } @Bean @ConditionalOnProperty(name = "management.otlp.tracing.transport", havingValue = "grpc") OtlpGrpcSpanExporter otlpGrpcSpanExporter(OtlpTracingProperties properties, - OtlpTracingConnectionDetails connectionDetails, ObjectProvider meterProvider) { + OtlpTracingConnectionDetails connectionDetails, ObjectProvider meterProvider, + ObjectProvider customizers) { OtlpGrpcSpanExporterBuilder builder = OtlpGrpcSpanExporter.builder() .setEndpoint(connectionDetails.getUrl(Transport.GRPC)) .setTimeout(properties.getTimeout()) @@ -105,6 +108,7 @@ OtlpGrpcSpanExporter otlpGrpcSpanExporter(OtlpTracingProperties properties, .setCompression(properties.getCompression().name().toLowerCase(Locale.ROOT)); properties.getHeaders().forEach(builder::addHeader); meterProvider.ifAvailable(builder::setMeterProvider); + customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder.build(); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingAutoConfigurationTests.java index 3d62d48aa709..00a4ad8b15c5 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/tracing/otlp/OtlpTracingAutoConfigurationTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.autoconfigure.tracing.otlp; +import java.time.Duration; import java.util.List; import java.util.function.Supplier; @@ -230,6 +231,45 @@ void grpcShouldUseMeterProviderIfSet() { }); } + @Test + void shouldCustomizeHttpTransportWithOtlpHttpSpanExporterBuilderCustomizer() { + Duration connectTimeout = Duration.ofMinutes(20); + Duration timeout = Duration.ofMinutes(10); + this.contextRunner + .withBean("httpCustomizer1", OtlpHttpSpanExporterBuilderCustomizer.class, + () -> (builder) -> builder.setConnectTimeout(connectTimeout)) + .withBean("httpCustomizer2", OtlpHttpSpanExporterBuilderCustomizer.class, + () -> (builder) -> builder.setTimeout(timeout)) + .withPropertyValues("management.otlp.tracing.endpoint=http://localhost:4317/v1/traces") + .run((context) -> { + assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class).hasSingleBean(SpanExporter.class); + OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); + assertThat(exporter).extracting("delegate.httpSender.client") + .hasFieldOrPropertyWithValue("connectTimeoutMillis", (int) connectTimeout.toMillis()) + .hasFieldOrPropertyWithValue("callTimeoutMillis", (int) timeout.toMillis()); + }); + } + + @Test + void shouldCustomizeGrpcTransportWhenEnabledWithOtlpGrpcSpanExporterBuilderCustomizer() { + Duration timeout = Duration.ofMinutes(10); + Duration connectTimeout = Duration.ofMinutes(20); + this.contextRunner + .withBean("grpcCustomizer1", OtlpGrpcSpanExporterBuilderCustomizer.class, + () -> (builder) -> builder.setConnectTimeout(connectTimeout)) + .withBean("grpcCustomizer2", OtlpGrpcSpanExporterBuilderCustomizer.class, + () -> (builder) -> builder.setTimeout(timeout)) + .withPropertyValues("management.otlp.tracing.endpoint=http://localhost:4317/v1/traces", + "management.otlp.tracing.transport=grpc") + .run((context) -> { + assertThat(context).hasSingleBean(OtlpGrpcSpanExporter.class).hasSingleBean(SpanExporter.class); + OtlpGrpcSpanExporter exporter = context.getBean(OtlpGrpcSpanExporter.class); + assertThat(exporter).extracting("delegate.grpcSender.client") + .hasFieldOrPropertyWithValue("connectTimeoutMillis", (int) connectTimeout.toMillis()) + .hasFieldOrPropertyWithValue("callTimeoutMillis", (int) timeout.toMillis()); + }); + } + @Configuration(proxyBeanMethods = false) private static final class MeterProviderConfiguration { diff --git a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc index c66a07beabeb..73eeff7c7b0d 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/antora/modules/reference/pages/actuator/tracing.adoc @@ -150,6 +150,8 @@ Tracing with OpenTelemetry and reporting using OTLP requires the following depen Use the `management.otlp.tracing.*` configuration properties to configure reporting using OTLP. +NOTE: If you need to apply advanced customizations to OTLP span exporters, consider registering javadoc:org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpHttpSpanExporterBuilderCustomizer[] or javadoc:org.springframework.boot.actuate.autoconfigure.tracing.otlp.OtlpGrpcSpanExporterBuilderCustomizer[] beans. These will be invoked **before** the creation of the `OtlpHttpSpanExporter` or `OtlpGrpcSpanExporter`. The customizers take precedence over anything applied by the auto-configuration. + [[actuator.micrometer-tracing.tracer-implementations.brave-zipkin]]