From 27fef3f212c777e98921d6186b645335471d0271 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 19 May 2026 01:22:36 +0530 Subject: [PATCH 1/9] Align OTLP signal-specific properties with common fallback configuration Signed-off-by: somiljain2006 --- .idea/inspectionProfiles/Project_Default.xml | 5 +- ...itional-spring-configuration-metadata.json | 20 +++ .../OtlpMetricsExportAutoConfiguration.java | 35 +++-- .../OtlpMetricsPropertiesConfigAdapter.java | 29 +++- ...lpMetricsExportAutoConfigurationTests.java | 11 ++ ...lpMetricsPropertiesConfigAdapterTests.java | 39 ++++- .../otlp/OtlpTracingAutoConfiguration.java | 3 +- .../otlp/OtlpTracingConfigurations.java | 139 ++++++++++++++--- ...elemetryTracingAutoConfigurationTests.java | 98 ++++++++++++ .../autoconfigure/OtlpProperties.java | 138 +++++++++++++++++ .../otlp/OtlpLoggingAutoConfiguration.java | 3 +- .../otlp/OtlpLoggingConfigurations.java | 143 +++++++++++++++--- ...elemetryLoggingAutoConfigurationTests.java | 111 ++++++++++++++ 13 files changed, 710 insertions(+), 64 deletions(-) create mode 100644 module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpProperties.java diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index a18b995b1a07..cef7567b0b3b 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,6 +1,9 @@ - + \ No newline at end of file diff --git a/module/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/module/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index b539371484c4..d6d1662d7cdc 100644 --- a/module/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/module/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -151,6 +151,26 @@ "description": "Whether auto-configuration of logging is enabled to export logs.", "defaultValue": true }, + { + "name": "management.opentelemetry.otlp.connect-timeout", + "type": "java.time.Duration", + "description": "Connection timeout for the OTLP exporters." + }, + { + "name": "management.opentelemetry.otlp.endpoint", + "type": "java.lang.String", + "description": "OTLP target endpoint URL." + }, + { + "name": "management.opentelemetry.otlp.headers", + "type": "java.util.Map", + "description": "Custom headers to be appended to OTLP requests." + }, + { + "name": "management.opentelemetry.otlp.timeout", + "type": "java.time.Duration", + "description": "Read timeout for the OTLP exporters." + }, { "name": "management.server.add-application-context-header", "type": "java.lang.Boolean", diff --git a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsExportAutoConfiguration.java b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsExportAutoConfiguration.java index 5c218497e65c..591eac0d2eef 100644 --- a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsExportAutoConfiguration.java +++ b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsExportAutoConfiguration.java @@ -39,6 +39,7 @@ import org.springframework.boot.micrometer.metrics.autoconfigure.export.ConditionalOnEnabledMetricsExport; import org.springframework.boot.micrometer.metrics.autoconfigure.export.simple.SimpleMetricsExportAutoConfiguration; import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetryProperties; +import org.springframework.boot.opentelemetry.autoconfigure.OtlpProperties; import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.ssl.SslBundles; import org.springframework.boot.thread.Threading; @@ -61,7 +62,7 @@ @ConditionalOnBean(Clock.class) @ConditionalOnClass({ OtlpMeterRegistry.class, OpenTelemetryProperties.class }) @ConditionalOnEnabledMetricsExport("otlp") -@EnableConfigurationProperties({ OtlpMetricsProperties.class, OpenTelemetryProperties.class }) +@EnableConfigurationProperties({ OtlpMetricsProperties.class, OpenTelemetryProperties.class, OtlpProperties.class }) public final class OtlpMetricsExportAutoConfiguration { private final OtlpMetricsProperties properties; @@ -72,23 +73,26 @@ public final class OtlpMetricsExportAutoConfiguration { @Bean @ConditionalOnMissingBean - OtlpMetricsConnectionDetails otlpMetricsConnectionDetails(ObjectProvider sslBundles) { - return new PropertiesOtlpMetricsConnectionDetails(this.properties, sslBundles.getIfAvailable()); + OtlpMetricsConnectionDetails otlpMetricsConnectionDetails(OtlpProperties otlpProperties, + ObjectProvider sslBundles) { + return new PropertiesOtlpMetricsConnectionDetails(this.properties, otlpProperties, sslBundles.getIfAvailable()); } @Bean @ConditionalOnMissingBean - OtlpConfig otlpConfig(OpenTelemetryProperties openTelemetryProperties, + OtlpConfig otlpConfig(OtlpProperties otlpProperties, OpenTelemetryProperties openTelemetryProperties, OtlpMetricsConnectionDetails connectionDetails, Environment environment) { - return new OtlpMetricsPropertiesConfigAdapter(this.properties, openTelemetryProperties, connectionDetails, - environment); + return new OtlpMetricsPropertiesConfigAdapter(this.properties, otlpProperties, openTelemetryProperties, + connectionDetails, environment); } @Bean @ConditionalOnMissingBean(OtlpMetricsSender.class) - OtlpHttpMetricsSender otlpMetricsSender(OtlpMetricsConnectionDetails connectionDetails) { + OtlpHttpMetricsSender otlpMetricsSender(OtlpMetricsConnectionDetails connectionDetails, + OtlpProperties otlpProperties) { Duration connectTimeout = this.properties.getConnectTimeout(); - Duration timeout = connectTimeout.plus(this.properties.getReadTimeout()); + Duration readTimeout = this.properties.getReadTimeout(); + Duration timeout = connectTimeout.plus(readTimeout); JdkClientHttpSender httpSender = new JdkClientHttpSender(connectTimeout, timeout, connectionDetails.getSslBundle()); return new OtlpHttpMetricsSender(httpSender); @@ -129,16 +133,27 @@ static class PropertiesOtlpMetricsConnectionDetails implements OtlpMetricsConnec private final OtlpMetricsProperties properties; + private final OtlpProperties otlpProperties; + private final @Nullable SslBundles sslBundles; - PropertiesOtlpMetricsConnectionDetails(OtlpMetricsProperties properties, @Nullable SslBundles sslBundles) { + PropertiesOtlpMetricsConnectionDetails(OtlpMetricsProperties properties, OtlpProperties otlpProperties, + @Nullable SslBundles sslBundles) { this.properties = properties; + this.otlpProperties = otlpProperties; this.sslBundles = sslBundles; } @Override public @Nullable String getUrl() { - return this.properties.getUrl(); + if (StringUtils.hasLength(this.properties.getUrl())) { + return this.properties.getUrl(); + } + String endpoint = this.otlpProperties.getEndpoint(); + if (StringUtils.hasLength(endpoint)) { + return endpoint.endsWith("/") ? endpoint + "v1/metrics" : endpoint + "/v1/metrics"; + } + return null; } @Override diff --git a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapter.java b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapter.java index 4cc7073c0090..09976bc5166d 100644 --- a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapter.java +++ b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapter.java @@ -16,6 +16,7 @@ package org.springframework.boot.micrometer.metrics.autoconfigure.export.otlp; +import java.time.Duration; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -30,6 +31,7 @@ import org.springframework.boot.micrometer.metrics.autoconfigure.export.properties.StepRegistryPropertiesConfigAdapter; import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetryProperties; import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetryResourceAttributes; +import org.springframework.boot.opentelemetry.autoconfigure.OtlpProperties; import org.springframework.core.env.Environment; import org.springframework.util.CollectionUtils; @@ -43,16 +45,19 @@ class OtlpMetricsPropertiesConfigAdapter extends StepRegistryPropertiesConfigAdapter implements OtlpConfig { + private final OtlpProperties otlpProperties; + private final OpenTelemetryProperties openTelemetryProperties; private final OtlpMetricsConnectionDetails connectionDetails; private final Environment environment; - OtlpMetricsPropertiesConfigAdapter(OtlpMetricsProperties properties, + OtlpMetricsPropertiesConfigAdapter(OtlpMetricsProperties properties, OtlpProperties otlpProperties, OpenTelemetryProperties openTelemetryProperties, OtlpMetricsConnectionDetails connectionDetails, Environment environment) { super(properties); + this.otlpProperties = otlpProperties; this.connectionDetails = connectionDetails; this.openTelemetryProperties = openTelemetryProperties; this.environment = environment; @@ -88,7 +93,27 @@ public Map resourceAttributes() { @Override public Map headers() { - return obtain(OtlpMetricsProperties::getHeaders, OtlpConfig.super::headers); + Map headers = new LinkedHashMap<>(this.otlpProperties.getHeaders()); + headers.putAll(obtain(OtlpMetricsProperties::getHeaders, OtlpConfig.super::headers)); + return Collections.unmodifiableMap(headers); + } + + @Override + @SuppressWarnings("deprecation") + public Duration connectTimeout() { + return obtain(OtlpMetricsProperties::getConnectTimeout, () -> { + Duration commonConnectTimeout = this.otlpProperties.getConnectTimeout(); + return (commonConnectTimeout != null) ? commonConnectTimeout : OtlpConfig.super.connectTimeout(); + }); + } + + @Override + @SuppressWarnings("deprecation") + public Duration readTimeout() { + return obtain(OtlpMetricsProperties::getReadTimeout, () -> { + Duration commonTimeout = this.otlpProperties.getTimeout(); + return (commonTimeout != null) ? commonTimeout : OtlpConfig.super.readTimeout(); + }); } @Override diff --git a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsExportAutoConfigurationTests.java b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsExportAutoConfigurationTests.java index f4c267b8f19e..d4f3e62d748c 100644 --- a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsExportAutoConfigurationTests.java +++ b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsExportAutoConfigurationTests.java @@ -266,6 +266,17 @@ public SslBundle getSslBundle() { }); } + @Test + void testUrlFallbackToCommonOtlpEndpoint() { + this.contextRunner.withUserConfiguration(BaseConfiguration.class) + .withPropertyValues("management.opentelemetry.otlp.endpoint=http://common-host:4318") + .run((context) -> { + assertThat(context).hasSingleBean(OtlpConfig.class); + OtlpConfig config = context.getBean(OtlpConfig.class); + assertThat(config.url()).isEqualTo("http://common-host:4318/v1/metrics"); + }); + } + private HttpClient extractHttpClient(OtlpHttpMetricsSender metricsSender) { Object field = ReflectionTestUtils.getField(metricsSender, "httpSender"); assertThat(field).isNotNull(); diff --git a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java index e78e7ddbd45b..3a97ab693360 100644 --- a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java +++ b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.micrometer.metrics.autoconfigure.export.otlp; +import java.time.Duration; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -28,6 +29,7 @@ import org.springframework.boot.micrometer.metrics.autoconfigure.export.otlp.OtlpMetricsExportAutoConfiguration.PropertiesOtlpMetricsConnectionDetails; import org.springframework.boot.micrometer.metrics.autoconfigure.export.otlp.OtlpMetricsProperties.Meter; import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetryProperties; +import org.springframework.boot.opentelemetry.autoconfigure.OtlpProperties; import org.springframework.mock.env.MockEnvironment; import static org.assertj.core.api.Assertions.assertThat; @@ -44,6 +46,8 @@ class OtlpMetricsPropertiesConfigAdapterTests { private OtlpMetricsProperties properties; + private OtlpProperties otlpProperties; + private OpenTelemetryProperties openTelemetryProperties; private MockEnvironment environment; @@ -53,9 +57,10 @@ class OtlpMetricsPropertiesConfigAdapterTests { @BeforeEach void setUp() { this.properties = new OtlpMetricsProperties(); + this.otlpProperties = new OtlpProperties(); this.openTelemetryProperties = new OpenTelemetryProperties(); this.environment = new MockEnvironment(); - this.connectionDetails = new PropertiesOtlpMetricsConnectionDetails(this.properties, null); + this.connectionDetails = new PropertiesOtlpMetricsConnectionDetails(this.properties, this.otlpProperties, null); } @Test @@ -238,8 +243,36 @@ void shouldUseDefaultApplicationGroupIfApplicationGroupIsNotSet() { } private OtlpMetricsPropertiesConfigAdapter createAdapter() { - return new OtlpMetricsPropertiesConfigAdapter(this.properties, this.openTelemetryProperties, - this.connectionDetails, this.environment); + return new OtlpMetricsPropertiesConfigAdapter(this.properties, this.otlpProperties, + this.openTelemetryProperties, this.connectionDetails, this.environment); + } + + @Test + void whenPropertiesHeadersIsNotSetThenUseOtlpPropertiesHeadersAsFallback() { + this.otlpProperties.getHeaders().put("common-header", "common-value"); + assertThat(createAdapter().headers()).containsEntry("common-header", "common-value"); + } + + @Test + void whenPropertiesHeadersIsSetThenMergeWithOtlpPropertiesHeaders() { + this.otlpProperties.getHeaders().put("common-header", "common-value"); + this.properties.setHeaders(Map.of("signal-header", "signal-value")); + assertThat(createAdapter().headers()).containsEntry("common-header", "common-value") + .containsEntry("signal-header", "signal-value"); + } + + @Test + void whenPropertiesTimeoutIsSetItOverridesOtlpPropertiesTimeout() { + this.otlpProperties.setTimeout(Duration.ofSeconds(10)); + this.properties.setReadTimeout(Duration.ofSeconds(3)); + assertThat(createAdapter().readTimeout()).isEqualTo(Duration.ofSeconds(3)); + } + + @Test + void whenPropertiesConnectTimeoutIsSetItOverridesOtlpPropertiesConnectTimeout() { + this.otlpProperties.setConnectTimeout(Duration.ofSeconds(10)); + this.properties.setConnectTimeout(Duration.ofSeconds(3)); + assertThat(createAdapter().connectTimeout()).isEqualTo(Duration.ofSeconds(3)); } } diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingAutoConfiguration.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingAutoConfiguration.java index 2138ee908c2e..ce6cd6cb5bba 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingAutoConfiguration.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingAutoConfiguration.java @@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.opentelemetry.autoconfigure.OtlpProperties; import org.springframework.context.annotation.Import; /** @@ -48,7 +49,7 @@ */ @AutoConfiguration @ConditionalOnClass({ OtelTracer.class, SdkTracerProvider.class, OpenTelemetry.class, OtlpHttpSpanExporter.class }) -@EnableConfigurationProperties(OtlpTracingProperties.class) +@EnableConfigurationProperties({ OtlpTracingProperties.class, OtlpProperties.class }) @Import({ OtlpTracingConfigurations.ConnectionDetails.class, OtlpTracingConfigurations.Exporters.class }) public final class OtlpTracingAutoConfiguration { diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingConfigurations.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingConfigurations.java index 4cb6a7c41d15..35dab24d45a3 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingConfigurations.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingConfigurations.java @@ -16,7 +16,10 @@ package org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.otlp; +import java.time.Duration; +import java.util.LinkedHashMap; import java.util.Locale; +import java.util.Map; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; @@ -30,14 +33,21 @@ import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.micrometer.tracing.autoconfigure.ConditionalOnEnabledTracingExport; +import org.springframework.boot.opentelemetry.autoconfigure.OtlpProperties; import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.ssl.SslBundles; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -54,32 +64,60 @@ static class ConnectionDetails { @Bean @ConditionalOnMissingBean - @ConditionalOnProperty("management.opentelemetry.tracing.export.otlp.endpoint") + @Conditional(OtlpEndpointCondition.class) OtlpTracingConnectionDetails otlpTracingConnectionDetails(OtlpTracingProperties properties, - ObjectProvider sslBundles) { - return new PropertiesOtlpTracingConnectionDetails(properties, sslBundles.getIfAvailable()); + OtlpProperties otlpProperties, ObjectProvider sslBundles) { + return new PropertiesOtlpTracingConnectionDetails(properties, otlpProperties, sslBundles.getIfAvailable()); } /** - * Adapts {@link OtlpTracingProperties} to {@link OtlpTracingConnectionDetails}. + * Condition to check if either the tracing-specific endpoint or the common OTLP + * endpoint is set. + */ + @SuppressWarnings("unused") + static class OtlpEndpointCondition extends AnyNestedCondition { + + OtlpEndpointCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @ConditionalOnProperty("management.opentelemetry.tracing.export.otlp.endpoint") + static class TracingEndpoint { + + } + + @ConditionalOnProperty("management.opentelemetry.otlp.endpoint") + static class CommonEndpoint { + + } + + } + + /** + * Adapts {@link OtlpTracingProperties} and {@link OtlpProperties} to + * {@link OtlpTracingConnectionDetails}. */ static class PropertiesOtlpTracingConnectionDetails implements OtlpTracingConnectionDetails { private final OtlpTracingProperties properties; + private final OtlpProperties otlpProperties; + private final @Nullable SslBundles sslBundles; - PropertiesOtlpTracingConnectionDetails(OtlpTracingProperties properties, @Nullable SslBundles sslBundles) { + PropertiesOtlpTracingConnectionDetails(OtlpTracingProperties properties, OtlpProperties otlpProperties, + @Nullable SslBundles sslBundles) { this.properties = properties; + this.otlpProperties = otlpProperties; this.sslBundles = sslBundles; } @Override public String getUrl(Transport transport) { - Assert.state(transport == this.properties.getTransport(), - "Requested transport %s doesn't match configured transport %s".formatted(transport, - this.properties.getTransport())); String endpoint = this.properties.getEndpoint(); + if (!StringUtils.hasLength(endpoint)) { + endpoint = this.otlpProperties.getEndpoint(); + } Assert.state(endpoint != null, "'endpoint' must not be null"); return endpoint; } @@ -105,17 +143,28 @@ public String getUrl(Transport transport) { static class Exporters { @Bean - @ConditionalOnProperty(name = "management.opentelemetry.tracing.export.otlp.transport", havingValue = "http", - matchIfMissing = true) - OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpTracingProperties properties, + @Conditional(HttpTransportCondition.class) + OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpTracingProperties properties, OtlpProperties otlpProperties, OtlpTracingConnectionDetails connectionDetails, ObjectProvider meterProvider, ObjectProvider customizers) { OtlpHttpSpanExporterBuilder builder = OtlpHttpSpanExporter.builder() - .setEndpoint(connectionDetails.getUrl(Transport.HTTP)) - .setTimeout(properties.getTimeout()) - .setConnectTimeout(properties.getConnectTimeout()) - .setCompression(properties.getCompression().name().toLowerCase(Locale.ROOT)); - properties.getHeaders().forEach(builder::addHeader); + .setEndpoint(connectionDetails.getUrl(Transport.HTTP)); + + Duration timeout = properties.getTimeout(); + builder.setTimeout(timeout); + + Duration connectTimeout = properties.getConnectTimeout(); + builder.setConnectTimeout(connectTimeout); + + String compression = properties.getCompression().name().toLowerCase(Locale.ROOT); + if (StringUtils.hasLength(compression)) { + builder.setCompression(compression); + } + + Map headers = new LinkedHashMap<>(otlpProperties.getHeaders()); + headers.putAll(properties.getHeaders()); + headers.forEach(builder::addHeader); + meterProvider.ifAvailable(builder::setMeterProvider); configureSsl(connectionDetails, builder::setSslContext); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); @@ -123,16 +172,28 @@ OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpTracingProperties properties, } @Bean - @ConditionalOnProperty(name = "management.opentelemetry.tracing.export.otlp.transport", havingValue = "grpc") - OtlpGrpcSpanExporter otlpGrpcSpanExporter(OtlpTracingProperties properties, + @Conditional(GrpcTransportCondition.class) + OtlpGrpcSpanExporter otlpGrpcSpanExporter(OtlpTracingProperties properties, OtlpProperties otlpProperties, OtlpTracingConnectionDetails connectionDetails, ObjectProvider meterProvider, ObjectProvider customizers) { OtlpGrpcSpanExporterBuilder builder = OtlpGrpcSpanExporter.builder() - .setEndpoint(connectionDetails.getUrl(Transport.GRPC)) - .setTimeout(properties.getTimeout()) - .setConnectTimeout(properties.getConnectTimeout()) - .setCompression(properties.getCompression().name().toLowerCase(Locale.ROOT)); - properties.getHeaders().forEach(builder::addHeader); + .setEndpoint(connectionDetails.getUrl(Transport.GRPC)); + + Duration timeout = properties.getTimeout(); + builder.setTimeout(timeout); + + Duration connectTimeout = properties.getConnectTimeout(); + builder.setConnectTimeout(connectTimeout); + + String compression = properties.getCompression().name().toLowerCase(Locale.ROOT); + if (StringUtils.hasLength(compression)) { + builder.setCompression(compression); + } + + Map headers = new LinkedHashMap<>(otlpProperties.getHeaders()); + headers.putAll(properties.getHeaders()); + headers.forEach(builder::addHeader); + meterProvider.ifAvailable(builder::setMeterProvider); configureSsl(connectionDetails, builder::setSslContext); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); @@ -164,6 +225,38 @@ private interface SslContextConfigurer { } + static class HttpTransportCondition extends SpringBootCondition { + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + String tracingTransport = context.getEnvironment() + .getProperty("management.opentelemetry.tracing.export.otlp.transport"); + String commonTransport = context.getEnvironment() + .getProperty("management.opentelemetry.otlp.transport"); + String activeTransport = (tracingTransport != null) ? tracingTransport + : (commonTransport != null) ? commonTransport : "http"; + return new ConditionOutcome("http".equalsIgnoreCase(activeTransport), + "Transport is " + activeTransport); + } + + } + + static class GrpcTransportCondition extends SpringBootCondition { + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + String tracingTransport = context.getEnvironment() + .getProperty("management.opentelemetry.tracing.export.otlp.transport"); + String commonTransport = context.getEnvironment() + .getProperty("management.opentelemetry.otlp.transport"); + String activeTransport = (tracingTransport != null) ? tracingTransport + : (commonTransport != null) ? commonTransport : "http"; + return new ConditionOutcome("grpc".equalsIgnoreCase(activeTransport), + "Transport is " + activeTransport); + } + + } + } } diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java index 50f47d4286a6..50fb4726327a 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java @@ -48,6 +48,8 @@ import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; import io.opentelemetry.extension.trace.propagation.B3Propagator; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.resources.Resource; @@ -67,6 +69,7 @@ import org.springframework.boot.micrometer.observation.autoconfigure.ObservationAutoConfiguration; import org.springframework.boot.micrometer.tracing.autoconfigure.MicrometerTracingAutoConfiguration; import org.springframework.boot.micrometer.tracing.brave.autoconfigure.BraveAutoConfiguration; +import org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.otlp.OtlpTracingAutoConfiguration; import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -528,6 +531,101 @@ void shouldPublishEventsWhenContextStorageIsInitializedEarly() { }); } + @Test + void shouldFallbackToCommonTransportForTracing() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.transport=grpc", + "management.opentelemetry.tracing.export.otlp.endpoint=http://localhost:4317") + .run((context) -> { + assertThat(context).hasSingleBean(OtlpGrpcSpanExporter.class); + assertThat(context).doesNotHaveBean(OtlpHttpSpanExporter.class); + }); + } + + @Test + void shouldFallbackToCommonTimeoutForTracingExporter() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.timeout=10s", + "management.opentelemetry.tracing.export.otlp.endpoint=http://localhost:4317") + .run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); + OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); + assertThat(exporter).isNotNull(); + }); + } + + @Test + void shouldFallbackToCommonCompressionForTracingExporter() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.compression=gzip", + "management.opentelemetry.tracing.export.otlp.endpoint=http://localhost:4317") + .run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); + OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); + assertThat(exporter).isNotNull(); + }); + } + + @Test + void shouldPreferSignalSpecificTimeoutOverCommonTimeoutForTracing() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.timeout=10s", + "management.opentelemetry.tracing.export.otlp.timeout=3s", + "management.opentelemetry.tracing.export.otlp.endpoint=http://localhost:4317") + .run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); + OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); + assertThat(exporter).isNotNull(); + }); + } + + @Test + void shouldPreferSignalSpecificCompressionOverCommonCompressionForTracing() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.compression=gzip", + "management.opentelemetry.tracing.export.otlp.compression=none", + "management.opentelemetry.tracing.export.otlp.endpoint=http://localhost:4317") + .run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); + OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); + assertThat(exporter).isNotNull(); + }); + } + + @Test + void shouldPreferSignalSpecificConnectTimeoutOverCommonConnectTimeoutForTracing() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.connect-timeout=10s", + "management.opentelemetry.tracing.export.otlp.connect-timeout=3s", + "management.opentelemetry.tracing.export.otlp.endpoint=http://localhost:4317") + .run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); + OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); + assertThat(exporter).isNotNull(); + }); + } + + @Test + void shouldMergeCommonAndSignalSpecificHeadersForTracing() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.endpoint=http://localhost:4318", + "management.opentelemetry.otlp.headers.common-header=common-value", + "management.opentelemetry.otlp.headers.shared-header=common-wins", + "management.opentelemetry.tracing.export.otlp.headers.tracing-header=tracing-value", + "management.opentelemetry.tracing.export.otlp.headers.shared-header=tracing-wins") + .run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); + OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); + assertThat(exporter).isNotNull(); + }); + } + private void initializeOpenTelemetry(ConfigurableApplicationContext context) { context.addApplicationListener(new OpenTelemetryEventPublisherBeansApplicationListener()); Span.current(); diff --git a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpProperties.java b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpProperties.java new file mode 100644 index 000000000000..0892bf9cdd96 --- /dev/null +++ b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpProperties.java @@ -0,0 +1,138 @@ +/* + * Copyright 2012-present 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.opentelemetry.autoconfigure; + +import java.time.Duration; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.jspecify.annotations.Nullable; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Common configuration properties for OpenTelemetry Protocol (OTLP) exporters. + * + * @author Somil Jain + * @since 4.0.0 + */ +@ConfigurationProperties("management.opentelemetry.otlp") +public class OtlpProperties { + + /** + * OTLP endpoint to connect to. + */ + private @Nullable String endpoint; + + /** + * Additional headers to be passed with every request. + */ + private final Map headers = new LinkedHashMap<>(); + + /** + * Timeout for executing requests. + */ + private @Nullable Duration timeout; + + /** + * Connection timeout. + */ + private @Nullable Duration connectTimeout; + + /** + * Transport to use. + */ + private @Nullable Transport transport; + + /** + * Compression to use. + */ + private @Nullable Compression compression; + + public @Nullable String getEndpoint() { + return this.endpoint; + } + + public void setEndpoint(@Nullable String endpoint) { + this.endpoint = endpoint; + } + + public Map getHeaders() { + return this.headers; + } + + public @Nullable Duration getTimeout() { + return this.timeout; + } + + public void setTimeout(@Nullable Duration timeout) { + this.timeout = timeout; + } + + public @Nullable Duration getConnectTimeout() { + return this.connectTimeout; + } + + public void setConnectTimeout(@Nullable Duration connectTimeout) { + this.connectTimeout = connectTimeout; + } + + public @Nullable Transport getTransport() { + return this.transport; + } + + public void setTransport(@Nullable Transport transport) { + this.transport = transport; + } + + public @Nullable Compression getCompression() { + return this.compression; + } + + public void setCompression(@Nullable Compression compression) { + this.compression = compression; + } + + public enum Transport { + + /** + * HTTP transport. + */ + HTTP, + + /** + * gRPC transport. + */ + GRPC + + } + + public enum Compression { + + /** + * No compression. + */ + NONE, + + /** + * GZIP compression. + */ + GZIP + + } + +} diff --git a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfiguration.java b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfiguration.java index 571632b0544d..622bee40ac09 100644 --- a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfiguration.java +++ b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfiguration.java @@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.opentelemetry.autoconfigure.OtlpProperties; import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.OtlpLoggingConfigurations.ConnectionDetails; import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.OtlpLoggingConfigurations.Exporters; import org.springframework.context.annotation.Import; @@ -35,7 +36,7 @@ */ @AutoConfiguration @ConditionalOnClass({ OpenTelemetry.class, SdkLoggerProvider.class }) -@EnableConfigurationProperties(OtlpLoggingProperties.class) +@EnableConfigurationProperties({ OtlpLoggingProperties.class, OtlpProperties.class }) @Import({ ConnectionDetails.class, Exporters.class }) public final class OtlpLoggingAutoConfiguration { diff --git a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java index 0722701f2a45..4a8e31d44694 100644 --- a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java +++ b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java @@ -16,7 +16,10 @@ package org.springframework.boot.opentelemetry.autoconfigure.logging.otlp; +import java.time.Duration; +import java.util.LinkedHashMap; import java.util.Locale; +import java.util.Map; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; @@ -30,15 +33,22 @@ import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 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.autoconfigure.condition.SpringBootCondition; +import org.springframework.boot.opentelemetry.autoconfigure.OtlpProperties; import org.springframework.boot.opentelemetry.autoconfigure.logging.ConditionalOnEnabledLoggingExport; import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.ssl.SslBundles; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -55,32 +65,61 @@ static class ConnectionDetails { @Bean @ConditionalOnMissingBean(OtlpLoggingConnectionDetails.class) - @ConditionalOnProperty("management.opentelemetry.logging.export.otlp.endpoint") + @Conditional(OtlpEndpointCondition.class) PropertiesOtlpLoggingConnectionDetails openTelemetryLoggingConnectionDetails(OtlpLoggingProperties properties, - ObjectProvider sslBundles) { - return new PropertiesOtlpLoggingConnectionDetails(properties, sslBundles.getIfAvailable()); + OtlpProperties otlpProperties, ObjectProvider sslBundles) { + return new PropertiesOtlpLoggingConnectionDetails(properties, otlpProperties, sslBundles.getIfAvailable()); } /** - * Adapts {@link OtlpLoggingProperties} to {@link OtlpLoggingConnectionDetails}. + * Condition to check if either the logging-specific endpoint or the common OTLP + * endpoint is set. + */ + static class OtlpEndpointCondition extends AnyNestedCondition { + + OtlpEndpointCondition() { + super(ConfigurationPhase.REGISTER_BEAN); + } + + @ConditionalOnProperty("management.opentelemetry.logging.export.otlp.endpoint") + @SuppressWarnings("unused") + static class LoggingEndpoint { + + } + + @ConditionalOnProperty("management.opentelemetry.otlp.endpoint") + @SuppressWarnings("unused") + static class CommonEndpoint { + + } + + } + + /** + * Adapts {@link OtlpLoggingProperties} and {@link OtlpProperties} to + * {@link OtlpLoggingConnectionDetails}. */ static class PropertiesOtlpLoggingConnectionDetails implements OtlpLoggingConnectionDetails { private final OtlpLoggingProperties properties; + private final OtlpProperties otlpProperties; + private final @Nullable SslBundles sslBundles; - PropertiesOtlpLoggingConnectionDetails(OtlpLoggingProperties properties, @Nullable SslBundles sslBundles) { + PropertiesOtlpLoggingConnectionDetails(OtlpLoggingProperties properties, OtlpProperties otlpProperties, + @Nullable SslBundles sslBundles) { this.properties = properties; + this.otlpProperties = otlpProperties; this.sslBundles = sslBundles; } @Override public String getUrl(Transport transport) { - Assert.state(transport == this.properties.getTransport(), - "Requested transport %s doesn't match configured transport %s".formatted(transport, - this.properties.getTransport())); String endpoint = this.properties.getEndpoint(); + if (!StringUtils.hasLength(endpoint)) { + endpoint = this.otlpProperties.getEndpoint(); + } Assert.state(endpoint != null, "'endpoint' must not be null"); return endpoint; } @@ -107,17 +146,30 @@ public String getUrl(Transport transport) { static class Exporters { @Bean - @ConditionalOnProperty(name = "management.opentelemetry.logging.export.otlp.transport", havingValue = "http", - matchIfMissing = true) + @Conditional(HttpTransportCondition.class) OtlpHttpLogRecordExporter otlpHttpLogRecordExporter(OtlpLoggingProperties properties, - OtlpLoggingConnectionDetails connectionDetails, ObjectProvider meterProvider, + OtlpProperties otlpProperties, OtlpLoggingConnectionDetails connectionDetails, + ObjectProvider meterProvider, ObjectProvider customizers) { OtlpHttpLogRecordExporterBuilder builder = OtlpHttpLogRecordExporter.builder() - .setEndpoint(connectionDetails.getUrl(Transport.HTTP)) - .setTimeout(properties.getTimeout()) - .setConnectTimeout(properties.getConnectTimeout()) - .setCompression(properties.getCompression().name().toLowerCase(Locale.US)); - properties.getHeaders().forEach(builder::addHeader); + .setEndpoint(connectionDetails.getUrl(Transport.HTTP)); + + Duration timeout = properties.getTimeout(); + builder.setTimeout(timeout); + + Duration connectTimeout = properties.getConnectTimeout(); + builder.setConnectTimeout(connectTimeout); + + String compression = properties.getCompression().name().toLowerCase(Locale.ROOT); + + if (StringUtils.hasLength(compression)) { + builder.setCompression(compression); + } + + Map headers = new LinkedHashMap<>(otlpProperties.getHeaders()); + headers.putAll(properties.getHeaders()); + headers.forEach(builder::addHeader); + meterProvider.ifAvailable(builder::setMeterProvider); configureSsl(connectionDetails, builder::setSslContext); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); @@ -125,16 +177,29 @@ OtlpHttpLogRecordExporter otlpHttpLogRecordExporter(OtlpLoggingProperties proper } @Bean - @ConditionalOnProperty(name = "management.opentelemetry.logging.export.otlp.transport", havingValue = "grpc") + @Conditional(GrpcTransportCondition.class) OtlpGrpcLogRecordExporter otlpGrpcLogRecordExporter(OtlpLoggingProperties properties, - OtlpLoggingConnectionDetails connectionDetails, ObjectProvider meterProvider, + OtlpProperties otlpProperties, OtlpLoggingConnectionDetails connectionDetails, + ObjectProvider meterProvider, ObjectProvider customizers) { OtlpGrpcLogRecordExporterBuilder builder = OtlpGrpcLogRecordExporter.builder() - .setEndpoint(connectionDetails.getUrl(Transport.GRPC)) - .setTimeout(properties.getTimeout()) - .setConnectTimeout(properties.getConnectTimeout()) - .setCompression(properties.getCompression().name().toLowerCase(Locale.US)); - properties.getHeaders().forEach(builder::addHeader); + .setEndpoint(connectionDetails.getUrl(Transport.GRPC)); + + Duration timeout = properties.getTimeout(); + builder.setTimeout(timeout); + + Duration connectTimeout = properties.getConnectTimeout(); + builder.setConnectTimeout(connectTimeout); + + String compression = properties.getCompression().name().toLowerCase(Locale.US); + if (StringUtils.hasLength(compression)) { + builder.setCompression(compression); + } + + Map headers = new LinkedHashMap<>(otlpProperties.getHeaders()); + headers.putAll(properties.getHeaders()); + headers.forEach(builder::addHeader); + meterProvider.ifAvailable(builder::setMeterProvider); configureSsl(connectionDetails, builder::setSslContext); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); @@ -166,6 +231,38 @@ private interface SslContextConfigurer { } + static class HttpTransportCondition extends SpringBootCondition { + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + String loggingTransport = context.getEnvironment() + .getProperty("management.opentelemetry.logging.export.otlp.transport"); + String commonTransport = context.getEnvironment() + .getProperty("management.opentelemetry.otlp.transport"); + String activeTransport = (loggingTransport != null) ? loggingTransport + : (commonTransport != null) ? commonTransport : "http"; + return new ConditionOutcome("http".equalsIgnoreCase(activeTransport), + "Transport is " + activeTransport); + } + + } + + static class GrpcTransportCondition extends SpringBootCondition { + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + String loggingTransport = context.getEnvironment() + .getProperty("management.opentelemetry.logging.export.otlp.transport"); + String commonTransport = context.getEnvironment() + .getProperty("management.opentelemetry.otlp.transport"); + String activeTransport = (loggingTransport != null) ? loggingTransport + : (commonTransport != null) ? commonTransport : "http"; + return new ConditionOutcome("grpc".equalsIgnoreCase(activeTransport), + "Transport is " + activeTransport); + } + + } + } } diff --git a/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OpenTelemetryLoggingAutoConfigurationTests.java b/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OpenTelemetryLoggingAutoConfigurationTests.java index 6df0b728035d..ba11c2babb83 100644 --- a/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OpenTelemetryLoggingAutoConfigurationTests.java +++ b/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OpenTelemetryLoggingAutoConfigurationTests.java @@ -22,6 +22,8 @@ import java.util.concurrent.atomic.AtomicInteger; import io.opentelemetry.context.Context; +import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; +import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.logs.LogLimits; import io.opentelemetry.sdk.logs.LogRecordProcessor; @@ -37,6 +39,9 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.context.annotation.ImportCandidates; import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration; +import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.OtlpLoggingAutoConfiguration; +import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.OtlpLoggingConnectionDetails; +import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.Transport; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; @@ -176,6 +181,112 @@ void shouldConfigureBatchLogRecordProcessorWithProperties() { }); } + @Test + void shouldFallbackToCommonTransportForLogging() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.transport=grpc", + "management.opentelemetry.logging.export.otlp.endpoint=http://localhost:4317") + .run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpGrpcLogRecordExporter.class); + assertThat(context).doesNotHaveBean(OtlpHttpLogRecordExporter.class); + }); + } + + @Test + void shouldFallbackToCommonTimeoutForLoggingExporter() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.timeout=5s", + "management.opentelemetry.logging.export.otlp.endpoint=http://localhost:4317") + .run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class); + OtlpHttpLogRecordExporter exporter = context.getBean(OtlpHttpLogRecordExporter.class); + assertThat(exporter).isNotNull(); + }); + } + + @Test + void shouldPreferSignalSpecificTransportOverCommonTransportForLogging() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.transport=grpc", + "management.opentelemetry.logging.export.otlp.transport=http", + "management.opentelemetry.logging.export.otlp.endpoint=http://localhost:4317") + .run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class); + assertThat(context).doesNotHaveBean(OtlpGrpcLogRecordExporter.class); + }); + } + + @Test + void shouldPreferSignalSpecificTimeoutOverCommonTimeoutForLogging() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.timeout=10s", + "management.opentelemetry.logging.export.otlp.timeout=3s", + "management.opentelemetry.logging.export.otlp.endpoint=http://localhost:4317") + .run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class); + OtlpHttpLogRecordExporter exporter = context.getBean(OtlpHttpLogRecordExporter.class); + assertThat(exporter).isNotNull(); + }); + } + + @Test + void shouldPreferSignalSpecificCompressionOverCommonCompressionForLogging() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.compression=gzip", + "management.opentelemetry.logging.export.otlp.compression=none", + "management.opentelemetry.logging.export.otlp.endpoint=http://localhost:4317") + .run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class); + OtlpHttpLogRecordExporter exporter = context.getBean(OtlpHttpLogRecordExporter.class); + assertThat(exporter).isNotNull(); + }); + } + + @Test + void shouldPreferSignalSpecificConnectTimeoutOverCommonConnectTimeoutForLogging() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.connect-timeout=10s", + "management.opentelemetry.logging.export.otlp.connect-timeout=3s", + "management.opentelemetry.logging.export.otlp.endpoint=http://localhost:4317") + .run((context) -> { + assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class); + OtlpHttpLogRecordExporter exporter = context.getBean(OtlpHttpLogRecordExporter.class); + assertThat(exporter).isNotNull(); + }); + } + + @Test + void shouldPreferSignalSpecificEndpointOverCommonEndpointForLogging() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.endpoint=http://common-host:4318", + "management.opentelemetry.logging.export.otlp.endpoint=http://logging-host:4318") + .run((context) -> { + assertThat(context).hasSingleBean(OtlpLoggingConnectionDetails.class); + OtlpLoggingConnectionDetails connectionDetails = context.getBean(OtlpLoggingConnectionDetails.class); + assertThat(connectionDetails.getUrl(Transport.HTTP)).isEqualTo("http://logging-host:4318"); + }); + } + + @Test + void shouldMergeCommonAndSignalSpecificHeadersForLogging() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.endpoint=http://localhost:4318", + "management.opentelemetry.otlp.headers.common-header=common-value", + "management.opentelemetry.otlp.headers.shared-header=common-wins", + "management.opentelemetry.logging.export.otlp.headers.logging-header=logging-value", + "management.opentelemetry.logging.export.otlp.headers.shared-header=logging-wins") + .run((context) -> { + assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class); + OtlpHttpLogRecordExporter exporter = context.getBean(OtlpHttpLogRecordExporter.class); + assertThat(exporter).isNotNull(); + }); + } + @Configuration(proxyBeanMethods = false) static class UserConfiguration { From ebb495146ae314f03e432ec77e49204d1244076e Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 19 May 2026 01:33:11 +0530 Subject: [PATCH 2/9] Reverting one file Signed-off-by: somiljain2006 --- .idea/inspectionProfiles/Project_Default.xml | 35 +++++++++----------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index cef7567b0b3b..6af2b4715b17 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,21 +1,18 @@ - - + + \ No newline at end of file From 1f4b129b89be02febef41018f0c525fa3d8257e1 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 19 May 2026 01:35:39 +0530 Subject: [PATCH 3/9] Formatting changes Signed-off-by: somiljain2006 --- .idea/inspectionProfiles/Project_Default.xml | 32 ++++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 6af2b4715b17..897f5384311f 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,18 +1,18 @@ - - \ No newline at end of file + From 3a911d1893ba0dcfdcd40eb2182290f91b0fbe97 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 19 May 2026 01:37:20 +0530 Subject: [PATCH 4/9] Formatting changes Signed-off-by: somiljain2006 --- .idea/inspectionProfiles/Project_Default.xml | 30 ++++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 897f5384311f..b8b9ad4ab5b3 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,18 +1,18 @@ - + From 42262b19204d5c507c5f78a04a311544f3dcb786 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Wed, 15 Jul 2026 18:51:31 +0530 Subject: [PATCH 5/9] Allow OTLP endpoint and headers to be shared across exporters Signed-off-by: somiljain2006 --- .../OtlpMetricsPropertiesConfigAdapter.java | 10 +- ...lpMetricsPropertiesConfigAdapterTests.java | 15 -- .../otlp/OtlpTracingConfigurations.java | 128 +++++++++++++----- ...elemetryTracingAutoConfigurationTests.java | 80 ----------- .../autoconfigure/OtlpProperties.java | 81 ----------- .../otlp/OtlpLoggingConfigurations.java | 10 +- ...elemetryLoggingAutoConfigurationTests.java | 80 ----------- 7 files changed, 96 insertions(+), 308 deletions(-) diff --git a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapter.java b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapter.java index 09976bc5166d..dabbeb2fa365 100644 --- a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapter.java +++ b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapter.java @@ -101,19 +101,13 @@ public Map headers() { @Override @SuppressWarnings("deprecation") public Duration connectTimeout() { - return obtain(OtlpMetricsProperties::getConnectTimeout, () -> { - Duration commonConnectTimeout = this.otlpProperties.getConnectTimeout(); - return (commonConnectTimeout != null) ? commonConnectTimeout : OtlpConfig.super.connectTimeout(); - }); + return obtain(OtlpMetricsProperties::getConnectTimeout, OtlpConfig.super::connectTimeout); } @Override @SuppressWarnings("deprecation") public Duration readTimeout() { - return obtain(OtlpMetricsProperties::getReadTimeout, () -> { - Duration commonTimeout = this.otlpProperties.getTimeout(); - return (commonTimeout != null) ? commonTimeout : OtlpConfig.super.readTimeout(); - }); + return obtain(OtlpMetricsProperties::getReadTimeout, OtlpConfig.super::readTimeout); } @Override diff --git a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java index 3a97ab693360..0d771396c7fe 100644 --- a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java +++ b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java @@ -16,7 +16,6 @@ package org.springframework.boot.micrometer.metrics.autoconfigure.export.otlp; -import java.time.Duration; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -261,18 +260,4 @@ void whenPropertiesHeadersIsSetThenMergeWithOtlpPropertiesHeaders() { .containsEntry("signal-header", "signal-value"); } - @Test - void whenPropertiesTimeoutIsSetItOverridesOtlpPropertiesTimeout() { - this.otlpProperties.setTimeout(Duration.ofSeconds(10)); - this.properties.setReadTimeout(Duration.ofSeconds(3)); - assertThat(createAdapter().readTimeout()).isEqualTo(Duration.ofSeconds(3)); - } - - @Test - void whenPropertiesConnectTimeoutIsSetItOverridesOtlpPropertiesConnectTimeout() { - this.otlpProperties.setConnectTimeout(Duration.ofSeconds(10)); - this.properties.setConnectTimeout(Duration.ofSeconds(3)); - assertThat(createAdapter().connectTimeout()).isEqualTo(Duration.ofSeconds(3)); - } - } diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingConfigurations.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingConfigurations.java index 35dab24d45a3..ad0970cc2d51 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingConfigurations.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingConfigurations.java @@ -150,23 +150,39 @@ OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpTracingProperties properties, Otlp OtlpHttpSpanExporterBuilder builder = OtlpHttpSpanExporter.builder() .setEndpoint(connectionDetails.getUrl(Transport.HTTP)); - Duration timeout = properties.getTimeout(); - builder.setTimeout(timeout); + applyCommonConfiguration(properties, otlpProperties, connectionDetails, meterProvider, + new ExporterBuilderDelegate() { + @Override + public void setTimeout(Duration timeout) { + builder.setTimeout(timeout); + } + + @Override + public void setConnectTimeout(Duration connectTimeout) { + builder.setConnectTimeout(connectTimeout); + } + + @Override + public void setCompression(String compression) { + builder.setCompression(compression); + } + + @Override + public void addHeader(String key, String value) { + builder.addHeader(key, value); + } + + @Override + public void setMeterProvider(MeterProvider meterProvider) { + builder.setMeterProvider(meterProvider); + } + + @Override + public void configure(SSLContext sslContext, X509TrustManager trustManager) { + builder.setSslContext(sslContext, trustManager); + } + }); - Duration connectTimeout = properties.getConnectTimeout(); - builder.setConnectTimeout(connectTimeout); - - String compression = properties.getCompression().name().toLowerCase(Locale.ROOT); - if (StringUtils.hasLength(compression)) { - builder.setCompression(compression); - } - - Map headers = new LinkedHashMap<>(otlpProperties.getHeaders()); - headers.putAll(properties.getHeaders()); - headers.forEach(builder::addHeader); - - meterProvider.ifAvailable(builder::setMeterProvider); - configureSsl(connectionDetails, builder::setSslContext); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder.build(); } @@ -179,25 +195,57 @@ OtlpGrpcSpanExporter otlpGrpcSpanExporter(OtlpTracingProperties properties, Otlp OtlpGrpcSpanExporterBuilder builder = OtlpGrpcSpanExporter.builder() .setEndpoint(connectionDetails.getUrl(Transport.GRPC)); - Duration timeout = properties.getTimeout(); - builder.setTimeout(timeout); + applyCommonConfiguration(properties, otlpProperties, connectionDetails, meterProvider, + new ExporterBuilderDelegate() { + @Override + public void setTimeout(Duration timeout) { + builder.setTimeout(timeout); + } + + @Override + public void setConnectTimeout(Duration connectTimeout) { + builder.setConnectTimeout(connectTimeout); + } + + @Override + public void setCompression(String compression) { + builder.setCompression(compression); + } + + @Override + public void addHeader(String key, String value) { + builder.addHeader(key, value); + } + + @Override + public void setMeterProvider(MeterProvider meterProvider) { + builder.setMeterProvider(meterProvider); + } + + @Override + public void configure(SSLContext sslContext, X509TrustManager trustManager) { + builder.setSslContext(sslContext, trustManager); + } + }); - Duration connectTimeout = properties.getConnectTimeout(); - builder.setConnectTimeout(connectTimeout); + customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); + return builder.build(); + } + private void applyCommonConfiguration(OtlpTracingProperties properties, OtlpProperties otlpProperties, + OtlpTracingConnectionDetails connectionDetails, ObjectProvider meterProvider, + ExporterBuilderDelegate delegate) { + delegate.setTimeout(properties.getTimeout()); + delegate.setConnectTimeout(properties.getConnectTimeout()); String compression = properties.getCompression().name().toLowerCase(Locale.ROOT); if (StringUtils.hasLength(compression)) { - builder.setCompression(compression); + delegate.setCompression(compression); } - Map headers = new LinkedHashMap<>(otlpProperties.getHeaders()); headers.putAll(properties.getHeaders()); - headers.forEach(builder::addHeader); - - meterProvider.ifAvailable(builder::setMeterProvider); - configureSsl(connectionDetails, builder::setSslContext); - customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); - return builder.build(); + headers.forEach(delegate::addHeader); + meterProvider.ifAvailable(delegate::setMeterProvider); + configureSsl(connectionDetails, delegate); } private void configureSsl(OtlpTracingConnectionDetails connectionDetails, @@ -225,16 +273,27 @@ private interface SslContextConfigurer { } + private interface ExporterBuilderDelegate extends SslContextConfigurer { + + void setTimeout(Duration timeout); + + void setConnectTimeout(Duration connectTimeout); + + void setCompression(String compression); + + void addHeader(String key, String value); + + void setMeterProvider(MeterProvider meterProvider); + + } + static class HttpTransportCondition extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { String tracingTransport = context.getEnvironment() .getProperty("management.opentelemetry.tracing.export.otlp.transport"); - String commonTransport = context.getEnvironment() - .getProperty("management.opentelemetry.otlp.transport"); - String activeTransport = (tracingTransport != null) ? tracingTransport - : (commonTransport != null) ? commonTransport : "http"; + String activeTransport = (tracingTransport != null) ? tracingTransport : "http"; return new ConditionOutcome("http".equalsIgnoreCase(activeTransport), "Transport is " + activeTransport); } @@ -247,10 +306,7 @@ static class GrpcTransportCondition extends SpringBootCondition { public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { String tracingTransport = context.getEnvironment() .getProperty("management.opentelemetry.tracing.export.otlp.transport"); - String commonTransport = context.getEnvironment() - .getProperty("management.opentelemetry.otlp.transport"); - String activeTransport = (tracingTransport != null) ? tracingTransport - : (commonTransport != null) ? commonTransport : "http"; + String activeTransport = (tracingTransport != null) ? tracingTransport : "http"; return new ConditionOutcome("grpc".equalsIgnoreCase(activeTransport), "Transport is " + activeTransport); } diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java index 50fb4726327a..9c25c178b009 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java @@ -49,7 +49,6 @@ import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; -import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; import io.opentelemetry.extension.trace.propagation.B3Propagator; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.resources.Resource; @@ -531,85 +530,6 @@ void shouldPublishEventsWhenContextStorageIsInitializedEarly() { }); } - @Test - void shouldFallbackToCommonTransportForTracing() { - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) - .withPropertyValues("management.opentelemetry.otlp.transport=grpc", - "management.opentelemetry.tracing.export.otlp.endpoint=http://localhost:4317") - .run((context) -> { - assertThat(context).hasSingleBean(OtlpGrpcSpanExporter.class); - assertThat(context).doesNotHaveBean(OtlpHttpSpanExporter.class); - }); - } - - @Test - void shouldFallbackToCommonTimeoutForTracingExporter() { - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) - .withPropertyValues("management.opentelemetry.otlp.timeout=10s", - "management.opentelemetry.tracing.export.otlp.endpoint=http://localhost:4317") - .run((context) -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); - OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); - assertThat(exporter).isNotNull(); - }); - } - - @Test - void shouldFallbackToCommonCompressionForTracingExporter() { - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) - .withPropertyValues("management.opentelemetry.otlp.compression=gzip", - "management.opentelemetry.tracing.export.otlp.endpoint=http://localhost:4317") - .run((context) -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); - OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); - assertThat(exporter).isNotNull(); - }); - } - - @Test - void shouldPreferSignalSpecificTimeoutOverCommonTimeoutForTracing() { - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) - .withPropertyValues("management.opentelemetry.otlp.timeout=10s", - "management.opentelemetry.tracing.export.otlp.timeout=3s", - "management.opentelemetry.tracing.export.otlp.endpoint=http://localhost:4317") - .run((context) -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); - OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); - assertThat(exporter).isNotNull(); - }); - } - - @Test - void shouldPreferSignalSpecificCompressionOverCommonCompressionForTracing() { - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) - .withPropertyValues("management.opentelemetry.otlp.compression=gzip", - "management.opentelemetry.tracing.export.otlp.compression=none", - "management.opentelemetry.tracing.export.otlp.endpoint=http://localhost:4317") - .run((context) -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); - OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); - assertThat(exporter).isNotNull(); - }); - } - - @Test - void shouldPreferSignalSpecificConnectTimeoutOverCommonConnectTimeoutForTracing() { - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) - .withPropertyValues("management.opentelemetry.otlp.connect-timeout=10s", - "management.opentelemetry.tracing.export.otlp.connect-timeout=3s", - "management.opentelemetry.tracing.export.otlp.endpoint=http://localhost:4317") - .run((context) -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); - OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); - assertThat(exporter).isNotNull(); - }); - } - @Test void shouldMergeCommonAndSignalSpecificHeadersForTracing() { new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) diff --git a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpProperties.java b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpProperties.java index 0892bf9cdd96..830c7b175f2a 100644 --- a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpProperties.java +++ b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpProperties.java @@ -16,7 +16,6 @@ package org.springframework.boot.opentelemetry.autoconfigure; -import java.time.Duration; import java.util.LinkedHashMap; import java.util.Map; @@ -43,26 +42,6 @@ public class OtlpProperties { */ private final Map headers = new LinkedHashMap<>(); - /** - * Timeout for executing requests. - */ - private @Nullable Duration timeout; - - /** - * Connection timeout. - */ - private @Nullable Duration connectTimeout; - - /** - * Transport to use. - */ - private @Nullable Transport transport; - - /** - * Compression to use. - */ - private @Nullable Compression compression; - public @Nullable String getEndpoint() { return this.endpoint; } @@ -75,64 +54,4 @@ public Map getHeaders() { return this.headers; } - public @Nullable Duration getTimeout() { - return this.timeout; - } - - public void setTimeout(@Nullable Duration timeout) { - this.timeout = timeout; - } - - public @Nullable Duration getConnectTimeout() { - return this.connectTimeout; - } - - public void setConnectTimeout(@Nullable Duration connectTimeout) { - this.connectTimeout = connectTimeout; - } - - public @Nullable Transport getTransport() { - return this.transport; - } - - public void setTransport(@Nullable Transport transport) { - this.transport = transport; - } - - public @Nullable Compression getCompression() { - return this.compression; - } - - public void setCompression(@Nullable Compression compression) { - this.compression = compression; - } - - public enum Transport { - - /** - * HTTP transport. - */ - HTTP, - - /** - * gRPC transport. - */ - GRPC - - } - - public enum Compression { - - /** - * No compression. - */ - NONE, - - /** - * GZIP compression. - */ - GZIP - - } - } diff --git a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java index 4a8e31d44694..61b5e2775757 100644 --- a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java +++ b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java @@ -237,10 +237,7 @@ static class HttpTransportCondition extends SpringBootCondition { public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { String loggingTransport = context.getEnvironment() .getProperty("management.opentelemetry.logging.export.otlp.transport"); - String commonTransport = context.getEnvironment() - .getProperty("management.opentelemetry.otlp.transport"); - String activeTransport = (loggingTransport != null) ? loggingTransport - : (commonTransport != null) ? commonTransport : "http"; + String activeTransport = (loggingTransport != null) ? loggingTransport : "http"; return new ConditionOutcome("http".equalsIgnoreCase(activeTransport), "Transport is " + activeTransport); } @@ -253,10 +250,7 @@ static class GrpcTransportCondition extends SpringBootCondition { public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { String loggingTransport = context.getEnvironment() .getProperty("management.opentelemetry.logging.export.otlp.transport"); - String commonTransport = context.getEnvironment() - .getProperty("management.opentelemetry.otlp.transport"); - String activeTransport = (loggingTransport != null) ? loggingTransport - : (commonTransport != null) ? commonTransport : "http"; + String activeTransport = (loggingTransport != null) ? loggingTransport : "http"; return new ConditionOutcome("grpc".equalsIgnoreCase(activeTransport), "Transport is " + activeTransport); } diff --git a/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OpenTelemetryLoggingAutoConfigurationTests.java b/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OpenTelemetryLoggingAutoConfigurationTests.java index ba11c2babb83..3bcc90153a62 100644 --- a/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OpenTelemetryLoggingAutoConfigurationTests.java +++ b/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OpenTelemetryLoggingAutoConfigurationTests.java @@ -23,7 +23,6 @@ import io.opentelemetry.context.Context; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; -import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.logs.LogLimits; import io.opentelemetry.sdk.logs.LogRecordProcessor; @@ -181,85 +180,6 @@ void shouldConfigureBatchLogRecordProcessorWithProperties() { }); } - @Test - void shouldFallbackToCommonTransportForLogging() { - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) - .withPropertyValues("management.opentelemetry.otlp.transport=grpc", - "management.opentelemetry.logging.export.otlp.endpoint=http://localhost:4317") - .run((context) -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(OtlpGrpcLogRecordExporter.class); - assertThat(context).doesNotHaveBean(OtlpHttpLogRecordExporter.class); - }); - } - - @Test - void shouldFallbackToCommonTimeoutForLoggingExporter() { - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) - .withPropertyValues("management.opentelemetry.otlp.timeout=5s", - "management.opentelemetry.logging.export.otlp.endpoint=http://localhost:4317") - .run((context) -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class); - OtlpHttpLogRecordExporter exporter = context.getBean(OtlpHttpLogRecordExporter.class); - assertThat(exporter).isNotNull(); - }); - } - - @Test - void shouldPreferSignalSpecificTransportOverCommonTransportForLogging() { - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) - .withPropertyValues("management.opentelemetry.otlp.transport=grpc", - "management.opentelemetry.logging.export.otlp.transport=http", - "management.opentelemetry.logging.export.otlp.endpoint=http://localhost:4317") - .run((context) -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class); - assertThat(context).doesNotHaveBean(OtlpGrpcLogRecordExporter.class); - }); - } - - @Test - void shouldPreferSignalSpecificTimeoutOverCommonTimeoutForLogging() { - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) - .withPropertyValues("management.opentelemetry.otlp.timeout=10s", - "management.opentelemetry.logging.export.otlp.timeout=3s", - "management.opentelemetry.logging.export.otlp.endpoint=http://localhost:4317") - .run((context) -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class); - OtlpHttpLogRecordExporter exporter = context.getBean(OtlpHttpLogRecordExporter.class); - assertThat(exporter).isNotNull(); - }); - } - - @Test - void shouldPreferSignalSpecificCompressionOverCommonCompressionForLogging() { - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) - .withPropertyValues("management.opentelemetry.otlp.compression=gzip", - "management.opentelemetry.logging.export.otlp.compression=none", - "management.opentelemetry.logging.export.otlp.endpoint=http://localhost:4317") - .run((context) -> { - assertThat(context).hasNotFailed(); - assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class); - OtlpHttpLogRecordExporter exporter = context.getBean(OtlpHttpLogRecordExporter.class); - assertThat(exporter).isNotNull(); - }); - } - - @Test - void shouldPreferSignalSpecificConnectTimeoutOverCommonConnectTimeoutForLogging() { - new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) - .withPropertyValues("management.opentelemetry.otlp.connect-timeout=10s", - "management.opentelemetry.logging.export.otlp.connect-timeout=3s", - "management.opentelemetry.logging.export.otlp.endpoint=http://localhost:4317") - .run((context) -> { - assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class); - OtlpHttpLogRecordExporter exporter = context.getBean(OtlpHttpLogRecordExporter.class); - assertThat(exporter).isNotNull(); - }); - } - @Test void shouldPreferSignalSpecificEndpointOverCommonEndpointForLogging() { new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) From b2e7a7f7cebdd5e001ebb0164510fc15a1d94dab Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 21 Jul 2026 21:10:43 +0530 Subject: [PATCH 6/9] Refine OTLP common configuration metadata and tests Signed-off-by: somiljain2006 --- ...itional-spring-configuration-metadata.json | 10 -- .../OtlpMetricsExportAutoConfiguration.java | 6 +- .../OtlpMetricsPropertiesConfigAdapter.java | 13 -- ...lpMetricsPropertiesConfigAdapterTests.java | 19 +++ .../otlp/OtlpTracingConfigurations.java | 129 +++++------------- ...elemetryTracingAutoConfigurationTests.java | 37 ++++- .../autoconfigure/OtlpProperties.java | 2 +- .../otlp/OtlpLoggingConfigurations.java | 12 +- ...elemetryLoggingAutoConfigurationTests.java | 22 ++- .../OtlpLoggingAutoConfigurationTests.java | 38 ++++++ 10 files changed, 162 insertions(+), 126 deletions(-) diff --git a/module/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/module/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index d6d1662d7cdc..789d8e694392 100644 --- a/module/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/module/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -151,11 +151,6 @@ "description": "Whether auto-configuration of logging is enabled to export logs.", "defaultValue": true }, - { - "name": "management.opentelemetry.otlp.connect-timeout", - "type": "java.time.Duration", - "description": "Connection timeout for the OTLP exporters." - }, { "name": "management.opentelemetry.otlp.endpoint", "type": "java.lang.String", @@ -166,11 +161,6 @@ "type": "java.util.Map", "description": "Custom headers to be appended to OTLP requests." }, - { - "name": "management.opentelemetry.otlp.timeout", - "type": "java.time.Duration", - "description": "Read timeout for the OTLP exporters." - }, { "name": "management.server.add-application-context-header", "type": "java.lang.Boolean", diff --git a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsExportAutoConfiguration.java b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsExportAutoConfiguration.java index 591eac0d2eef..1933816b85a0 100644 --- a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsExportAutoConfiguration.java +++ b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsExportAutoConfiguration.java @@ -88,11 +88,9 @@ OtlpConfig otlpConfig(OtlpProperties otlpProperties, OpenTelemetryProperties ope @Bean @ConditionalOnMissingBean(OtlpMetricsSender.class) - OtlpHttpMetricsSender otlpMetricsSender(OtlpMetricsConnectionDetails connectionDetails, - OtlpProperties otlpProperties) { + OtlpHttpMetricsSender otlpMetricsSender(OtlpMetricsConnectionDetails connectionDetails) { Duration connectTimeout = this.properties.getConnectTimeout(); - Duration readTimeout = this.properties.getReadTimeout(); - Duration timeout = connectTimeout.plus(readTimeout); + Duration timeout = connectTimeout.plus(this.properties.getReadTimeout()); JdkClientHttpSender httpSender = new JdkClientHttpSender(connectTimeout, timeout, connectionDetails.getSslBundle()); return new OtlpHttpMetricsSender(httpSender); diff --git a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapter.java b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapter.java index dabbeb2fa365..2255b2be8b89 100644 --- a/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapter.java +++ b/module/spring-boot-micrometer-metrics/src/main/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapter.java @@ -16,7 +16,6 @@ package org.springframework.boot.micrometer.metrics.autoconfigure.export.otlp; -import java.time.Duration; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -98,18 +97,6 @@ public Map headers() { return Collections.unmodifiableMap(headers); } - @Override - @SuppressWarnings("deprecation") - public Duration connectTimeout() { - return obtain(OtlpMetricsProperties::getConnectTimeout, OtlpConfig.super::connectTimeout); - } - - @Override - @SuppressWarnings("deprecation") - public Duration readTimeout() { - return obtain(OtlpMetricsProperties::getReadTimeout, OtlpConfig.super::readTimeout); - } - @Override public HistogramFlavor histogramFlavor() { return obtain(OtlpMetricsProperties::getHistogramFlavor, OtlpConfig.super::histogramFlavor); diff --git a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java index 0d771396c7fe..39be71c518d0 100644 --- a/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java +++ b/module/spring-boot-micrometer-metrics/src/test/java/org/springframework/boot/micrometer/metrics/autoconfigure/export/otlp/OtlpMetricsPropertiesConfigAdapterTests.java @@ -260,4 +260,23 @@ void whenPropertiesHeadersIsSetThenMergeWithOtlpPropertiesHeaders() { .containsEntry("signal-header", "signal-value"); } + @Test + void whenPropertiesUrlIsNotSetThenUseOtlpPropertiesEndpointAsFallbackWithAppendix() { + this.otlpProperties.setEndpoint("http://common-endpoint:4318"); + assertThat(createAdapter().url()).isEqualTo("http://common-endpoint:4318/v1/metrics"); + } + + @Test + void whenPropertiesUrlIsNotSetThenUseOtlpPropertiesEndpointAsFallbackWithAppendixAndTrailingSlash() { + this.otlpProperties.setEndpoint("http://common-endpoint:4318/"); + assertThat(createAdapter().url()).isEqualTo("http://common-endpoint:4318/v1/metrics"); + } + + @Test + void whenPropertiesUrlIsSetThenItOverridesOtlpPropertiesEndpoint() { + this.otlpProperties.setEndpoint("http://common-endpoint:4318"); + this.properties.setUrl("http://signal-endpoint:4318/custom/metrics"); + assertThat(createAdapter().url()).isEqualTo("http://signal-endpoint:4318/custom/metrics"); + } + } diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingConfigurations.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingConfigurations.java index ad0970cc2d51..a33a0c9f6c8e 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingConfigurations.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingConfigurations.java @@ -74,7 +74,6 @@ OtlpTracingConnectionDetails otlpTracingConnectionDetails(OtlpTracingProperties * Condition to check if either the tracing-specific endpoint or the common OTLP * endpoint is set. */ - @SuppressWarnings("unused") static class OtlpEndpointCondition extends AnyNestedCondition { OtlpEndpointCondition() { @@ -82,11 +81,13 @@ static class OtlpEndpointCondition extends AnyNestedCondition { } @ConditionalOnProperty("management.opentelemetry.tracing.export.otlp.endpoint") + @SuppressWarnings("unused") static class TracingEndpoint { } @ConditionalOnProperty("management.opentelemetry.otlp.endpoint") + @SuppressWarnings("unused") static class CommonEndpoint { } @@ -117,6 +118,9 @@ public String getUrl(Transport transport) { String endpoint = this.properties.getEndpoint(); if (!StringUtils.hasLength(endpoint)) { endpoint = this.otlpProperties.getEndpoint(); + if (endpoint != null && transport == Transport.HTTP) { + endpoint = endpoint.endsWith("/") ? endpoint + "v1/traces" : endpoint + "/v1/traces"; + } } Assert.state(endpoint != null, "'endpoint' must not be null"); return endpoint; @@ -143,109 +147,62 @@ public String getUrl(Transport transport) { static class Exporters { @Bean - @Conditional(HttpTransportCondition.class) + @ConditionalOnProperty(name = "management.opentelemetry.tracing.export.otlp.transport", havingValue = "http", + matchIfMissing = true) OtlpHttpSpanExporter otlpHttpSpanExporter(OtlpTracingProperties properties, OtlpProperties otlpProperties, OtlpTracingConnectionDetails connectionDetails, ObjectProvider meterProvider, ObjectProvider customizers) { OtlpHttpSpanExporterBuilder builder = OtlpHttpSpanExporter.builder() .setEndpoint(connectionDetails.getUrl(Transport.HTTP)); - applyCommonConfiguration(properties, otlpProperties, connectionDetails, meterProvider, - new ExporterBuilderDelegate() { - @Override - public void setTimeout(Duration timeout) { - builder.setTimeout(timeout); - } - - @Override - public void setConnectTimeout(Duration connectTimeout) { - builder.setConnectTimeout(connectTimeout); - } - - @Override - public void setCompression(String compression) { - builder.setCompression(compression); - } - - @Override - public void addHeader(String key, String value) { - builder.addHeader(key, value); - } - - @Override - public void setMeterProvider(MeterProvider meterProvider) { - builder.setMeterProvider(meterProvider); - } - - @Override - public void configure(SSLContext sslContext, X509TrustManager trustManager) { - builder.setSslContext(sslContext, trustManager); - } - }); + Duration timeout = properties.getTimeout(); + builder.setTimeout(timeout); + Duration connectTimeout = properties.getConnectTimeout(); + builder.setConnectTimeout(connectTimeout); + + String compression = properties.getCompression().name().toLowerCase(Locale.ROOT); + if (StringUtils.hasLength(compression)) { + builder.setCompression(compression); + } + + Map headers = new LinkedHashMap<>(otlpProperties.getHeaders()); + headers.putAll(properties.getHeaders()); + headers.forEach(builder::addHeader); + + meterProvider.ifAvailable(builder::setMeterProvider); + configureSsl(connectionDetails, builder::setSslContext); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return builder.build(); } @Bean - @Conditional(GrpcTransportCondition.class) + @ConditionalOnProperty(name = "management.opentelemetry.tracing.export.otlp.transport", havingValue = "grpc") OtlpGrpcSpanExporter otlpGrpcSpanExporter(OtlpTracingProperties properties, OtlpProperties otlpProperties, OtlpTracingConnectionDetails connectionDetails, ObjectProvider meterProvider, ObjectProvider customizers) { OtlpGrpcSpanExporterBuilder builder = OtlpGrpcSpanExporter.builder() .setEndpoint(connectionDetails.getUrl(Transport.GRPC)); - applyCommonConfiguration(properties, otlpProperties, connectionDetails, meterProvider, - new ExporterBuilderDelegate() { - @Override - public void setTimeout(Duration timeout) { - builder.setTimeout(timeout); - } - - @Override - public void setConnectTimeout(Duration connectTimeout) { - builder.setConnectTimeout(connectTimeout); - } - - @Override - public void setCompression(String compression) { - builder.setCompression(compression); - } - - @Override - public void addHeader(String key, String value) { - builder.addHeader(key, value); - } - - @Override - public void setMeterProvider(MeterProvider meterProvider) { - builder.setMeterProvider(meterProvider); - } - - @Override - public void configure(SSLContext sslContext, X509TrustManager trustManager) { - builder.setSslContext(sslContext, trustManager); - } - }); + Duration timeout = properties.getTimeout(); + builder.setTimeout(timeout); - customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); - return builder.build(); - } + Duration connectTimeout = properties.getConnectTimeout(); + builder.setConnectTimeout(connectTimeout); - private void applyCommonConfiguration(OtlpTracingProperties properties, OtlpProperties otlpProperties, - OtlpTracingConnectionDetails connectionDetails, ObjectProvider meterProvider, - ExporterBuilderDelegate delegate) { - delegate.setTimeout(properties.getTimeout()); - delegate.setConnectTimeout(properties.getConnectTimeout()); String compression = properties.getCompression().name().toLowerCase(Locale.ROOT); if (StringUtils.hasLength(compression)) { - delegate.setCompression(compression); + builder.setCompression(compression); } + Map headers = new LinkedHashMap<>(otlpProperties.getHeaders()); headers.putAll(properties.getHeaders()); - headers.forEach(delegate::addHeader); - meterProvider.ifAvailable(delegate::setMeterProvider); - configureSsl(connectionDetails, delegate); + headers.forEach(builder::addHeader); + + meterProvider.ifAvailable(builder::setMeterProvider); + configureSsl(connectionDetails, builder::setSslContext); + customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); + return builder.build(); } private void configureSsl(OtlpTracingConnectionDetails connectionDetails, @@ -273,20 +230,6 @@ private interface SslContextConfigurer { } - private interface ExporterBuilderDelegate extends SslContextConfigurer { - - void setTimeout(Duration timeout); - - void setConnectTimeout(Duration connectTimeout); - - void setCompression(String compression); - - void addHeader(String key, String value); - - void setMeterProvider(MeterProvider meterProvider); - - } - static class HttpTransportCondition extends SpringBootCondition { @Override diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java index 9c25c178b009..9e86db47034a 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/OpenTelemetryTracingAutoConfigurationTests.java @@ -542,7 +542,42 @@ void shouldMergeCommonAndSignalSpecificHeadersForTracing() { assertThat(context).hasNotFailed(); assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); - assertThat(exporter).isNotNull(); + assertThat(exporter).extracting("delegate.httpSender.headerSupplier") + .asInstanceOf(InstanceOfAssertFactories.type(Supplier.class)) + .satisfies((headerSupplier) -> assertThat(headerSupplier.get()) + .asInstanceOf(InstanceOfAssertFactories.map(String.class, List.class)) + .containsEntry("common-header", List.of("common-value")) + .containsEntry("tracing-header", List.of("tracing-value")) + .containsEntry("shared-header", List.of("tracing-wins"))); + }); + } + + @Test + void shouldAppendTracesPathToCommonEndpoint() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.endpoint=http://localhost:4318") + .run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); + OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); + assertThat(exporter).extracting("delegate.httpSender.url") + .extracting(Object::toString) + .isEqualTo("http://localhost:4318/v1/traces"); + }); + } + + @Test + void shouldNotAppendTracesPathToTracingSpecificEndpoint() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpTracingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.endpoint=http://localhost:4318", + "management.opentelemetry.tracing.export.otlp.endpoint=http://localhost:4318/custom/traces") + .run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context).hasSingleBean(OtlpHttpSpanExporter.class); + OtlpHttpSpanExporter exporter = context.getBean(OtlpHttpSpanExporter.class); + assertThat(exporter).extracting("delegate.httpSender.url") + .extracting(Object::toString) + .isEqualTo("http://localhost:4318/custom/traces"); }); } diff --git a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpProperties.java b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpProperties.java index 830c7b175f2a..1aed3ee1b89b 100644 --- a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpProperties.java +++ b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpProperties.java @@ -27,7 +27,7 @@ * Common configuration properties for OpenTelemetry Protocol (OTLP) exporters. * * @author Somil Jain - * @since 4.0.0 + * @since 4.2.0 */ @ConfigurationProperties("management.opentelemetry.otlp") public class OtlpProperties { diff --git a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java index 61b5e2775757..a3af902fb780 100644 --- a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java +++ b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java @@ -119,6 +119,9 @@ public String getUrl(Transport transport) { String endpoint = this.properties.getEndpoint(); if (!StringUtils.hasLength(endpoint)) { endpoint = this.otlpProperties.getEndpoint(); + if (endpoint != null && transport == Transport.HTTP) { + endpoint = endpoint.endsWith("/") ? endpoint + "v1/logs" : endpoint + "/v1/logs"; + } } Assert.state(endpoint != null, "'endpoint' must not be null"); return endpoint; @@ -146,13 +149,16 @@ public String getUrl(Transport transport) { static class Exporters { @Bean - @Conditional(HttpTransportCondition.class) + @ConditionalOnProperty(name = "management.opentelemetry.logging.export.otlp.transport", havingValue = "http", + matchIfMissing = true) OtlpHttpLogRecordExporter otlpHttpLogRecordExporter(OtlpLoggingProperties properties, OtlpProperties otlpProperties, OtlpLoggingConnectionDetails connectionDetails, ObjectProvider meterProvider, ObjectProvider customizers) { OtlpHttpLogRecordExporterBuilder builder = OtlpHttpLogRecordExporter.builder() - .setEndpoint(connectionDetails.getUrl(Transport.HTTP)); + .setConnectTimeout(properties.getConnectTimeout()) + .setCompression(properties.getCompression().name().toLowerCase(Locale.US)); + properties.getHeaders().forEach(builder::addHeader); Duration timeout = properties.getTimeout(); builder.setTimeout(timeout); @@ -177,7 +183,7 @@ OtlpHttpLogRecordExporter otlpHttpLogRecordExporter(OtlpLoggingProperties proper } @Bean - @Conditional(GrpcTransportCondition.class) + @ConditionalOnProperty(name = "management.opentelemetry.logging.export.otlp.transport", havingValue = "grpc") OtlpGrpcLogRecordExporter otlpGrpcLogRecordExporter(OtlpLoggingProperties properties, OtlpProperties otlpProperties, OtlpLoggingConnectionDetails connectionDetails, ObjectProvider meterProvider, diff --git a/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OpenTelemetryLoggingAutoConfigurationTests.java b/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OpenTelemetryLoggingAutoConfigurationTests.java index 3bcc90153a62..02ba84dc1bfa 100644 --- a/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OpenTelemetryLoggingAutoConfigurationTests.java +++ b/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/OpenTelemetryLoggingAutoConfigurationTests.java @@ -18,8 +18,10 @@ import java.time.Duration; import java.util.Collection; +import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; import io.opentelemetry.context.Context; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; @@ -32,6 +34,7 @@ import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor; import io.opentelemetry.sdk.logs.export.LogRecordExporter; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -203,7 +206,24 @@ void shouldMergeCommonAndSignalSpecificHeadersForLogging() { .run((context) -> { assertThat(context).hasSingleBean(OtlpHttpLogRecordExporter.class); OtlpHttpLogRecordExporter exporter = context.getBean(OtlpHttpLogRecordExporter.class); - assertThat(exporter).isNotNull(); + assertThat(exporter).extracting("delegate.httpSender.headerSupplier") + .asInstanceOf(InstanceOfAssertFactories.type(Supplier.class)) + .satisfies((headerSupplier) -> assertThat(headerSupplier.get()) + .asInstanceOf(InstanceOfAssertFactories.map(String.class, List.class)) + .containsEntry("common-header", List.of("common-value")) + .containsEntry("logging-header", List.of("logging-value")) + .containsEntry("shared-header", List.of("logging-wins"))); + }); + } + + @Test + void shouldFallbackToCommonEndpointAndAppendPathForLogging() { + new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(OtlpLoggingAutoConfiguration.class)) + .withPropertyValues("management.opentelemetry.otlp.endpoint=http://common-host:4318") + .run((context) -> { + assertThat(context).hasSingleBean(OtlpLoggingConnectionDetails.class); + OtlpLoggingConnectionDetails connectionDetails = context.getBean(OtlpLoggingConnectionDetails.class); + assertThat(connectionDetails.getUrl(Transport.HTTP)).isEqualTo("http://common-host:4318/v1/logs"); }); } diff --git a/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfigurationTests.java b/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfigurationTests.java index 2839b3c9b9d1..800f68bd9ef9 100644 --- a/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfigurationTests.java +++ b/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingAutoConfigurationTests.java @@ -38,6 +38,7 @@ import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration; import org.springframework.boot.context.annotation.ImportCandidates; import org.springframework.boot.opentelemetry.autoconfigure.OpenTelemetrySdkAutoConfiguration; +import org.springframework.boot.opentelemetry.autoconfigure.OtlpProperties; import org.springframework.boot.opentelemetry.autoconfigure.logging.OpenTelemetryLoggingAutoConfiguration; import org.springframework.boot.opentelemetry.autoconfigure.logging.SdkLoggerProviderBuilderCustomizer; import org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.OtlpLoggingConfigurations.ConnectionDetails.PropertiesOtlpLoggingConnectionDetails; @@ -321,6 +322,43 @@ void whenCustomConnectionDetailsProvidesSslBundleConfiguresHttpExporter() { }); } + @Test + void shouldAppendLogsPathToCommonEndpoint() { + OtlpLoggingProperties properties = new OtlpLoggingProperties(); + OtlpProperties otlpProperties = new OtlpProperties(); + otlpProperties.setEndpoint("http://localhost:4318"); + PropertiesOtlpLoggingConnectionDetails connectionDetails = new PropertiesOtlpLoggingConnectionDetails( + properties, otlpProperties, null); + + assertThat(connectionDetails.getUrl(Transport.HTTP)).isEqualTo("http://localhost:4318/v1/logs"); + assertThat(connectionDetails.getUrl(Transport.GRPC)).isEqualTo("http://localhost:4318"); + } + + @Test + void shouldNotAppendLogsPathToLoggingSpecificEndpoint() { + OtlpLoggingProperties properties = new OtlpLoggingProperties(); + properties.setEndpoint("http://localhost:4318/custom/logs"); + OtlpProperties otlpProperties = new OtlpProperties(); + otlpProperties.setEndpoint("http://localhost:4318"); + PropertiesOtlpLoggingConnectionDetails connectionDetails = new PropertiesOtlpLoggingConnectionDetails( + properties, otlpProperties, null); + + assertThat(connectionDetails.getUrl(Transport.HTTP)).isEqualTo("http://localhost:4318/custom/logs"); + } + + @Test + void shouldAppendLogsPathToCommonEndpointWithTrailingSlash() { + OtlpLoggingProperties properties = new OtlpLoggingProperties(); + + OtlpProperties otlpProperties = new OtlpProperties(); + otlpProperties.setEndpoint("http://localhost:4318/"); + + PropertiesOtlpLoggingConnectionDetails connectionDetails = new PropertiesOtlpLoggingConnectionDetails( + properties, otlpProperties, null); + + assertThat(connectionDetails.getUrl(Transport.HTTP)).isEqualTo("http://localhost:4318/v1/logs"); + } + @Configuration(proxyBeanMethods = false) public static class MultipleSdkLoggerProviderBuilderCustomizersConfig { From d670c1cb2d7d72da0c110416abec721d758d53dc Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 21 Jul 2026 21:14:04 +0530 Subject: [PATCH 7/9] Reverting xml file Signed-off-by: somiljain2006 --- .idea/inspectionProfiles/Project_Default.xml | 32 ++++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index b8b9ad4ab5b3..f2801506cca9 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,18 +1,18 @@ - - + + From 984bebf54a59d467c9119c8d60509b498bf30014 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 21 Jul 2026 21:15:14 +0530 Subject: [PATCH 8/9] Revert unintended IDE inspection profile changes Signed-off-by: somiljain2006 --- .idea/inspectionProfiles/Project_Default.xml | 32 ++++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index f2801506cca9..a18b995b1a07 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,18 +1,18 @@ - - + + From b0d0247e42a8ef7d84e610e569a570cd1cb4b31d Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Wed, 22 Jul 2026 12:50:11 +0530 Subject: [PATCH 9/9] Fix test failures Signed-off-by: somiljain2006 --- .../otlp/OtlpLoggingConfigurations.java | 43 +------------------ 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java index a3af902fb780..e511d357f798 100644 --- a/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java +++ b/module/spring-boot-opentelemetry/src/main/java/org/springframework/boot/opentelemetry/autoconfigure/logging/otlp/OtlpLoggingConfigurations.java @@ -34,21 +34,17 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; -import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 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.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.opentelemetry.autoconfigure.OtlpProperties; import org.springframework.boot.opentelemetry.autoconfigure.logging.ConditionalOnEnabledLoggingExport; import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.ssl.SslBundles; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; -import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -156,18 +152,12 @@ OtlpHttpLogRecordExporter otlpHttpLogRecordExporter(OtlpLoggingProperties proper ObjectProvider meterProvider, ObjectProvider customizers) { OtlpHttpLogRecordExporterBuilder builder = OtlpHttpLogRecordExporter.builder() - .setConnectTimeout(properties.getConnectTimeout()) - .setCompression(properties.getCompression().name().toLowerCase(Locale.US)); - properties.getHeaders().forEach(builder::addHeader); + .setEndpoint(connectionDetails.getUrl(Transport.HTTP)); Duration timeout = properties.getTimeout(); builder.setTimeout(timeout); - Duration connectTimeout = properties.getConnectTimeout(); - builder.setConnectTimeout(connectTimeout); - String compression = properties.getCompression().name().toLowerCase(Locale.ROOT); - if (StringUtils.hasLength(compression)) { builder.setCompression(compression); } @@ -194,10 +184,7 @@ OtlpGrpcLogRecordExporter otlpGrpcLogRecordExporter(OtlpLoggingProperties proper Duration timeout = properties.getTimeout(); builder.setTimeout(timeout); - Duration connectTimeout = properties.getConnectTimeout(); - builder.setConnectTimeout(connectTimeout); - - String compression = properties.getCompression().name().toLowerCase(Locale.US); + String compression = properties.getCompression().name().toLowerCase(Locale.ROOT); if (StringUtils.hasLength(compression)) { builder.setCompression(compression); } @@ -237,32 +224,6 @@ private interface SslContextConfigurer { } - static class HttpTransportCondition extends SpringBootCondition { - - @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - String loggingTransport = context.getEnvironment() - .getProperty("management.opentelemetry.logging.export.otlp.transport"); - String activeTransport = (loggingTransport != null) ? loggingTransport : "http"; - return new ConditionOutcome("http".equalsIgnoreCase(activeTransport), - "Transport is " + activeTransport); - } - - } - - static class GrpcTransportCondition extends SpringBootCondition { - - @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - String loggingTransport = context.getEnvironment() - .getProperty("management.opentelemetry.logging.export.otlp.transport"); - String activeTransport = (loggingTransport != null) ? loggingTransport : "http"; - return new ConditionOutcome("grpc".equalsIgnoreCase(activeTransport), - "Transport is " + activeTransport); - } - - } - } }