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 1933816b85a..0793570fe79 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 @@ -144,14 +144,7 @@ public final class OtlpMetricsExportAutoConfiguration { @Override public @Nullable String 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; + return this.otlpProperties.resolveEndpoint(this.properties.getUrl(), "v1/metrics"); } @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 0842554529f..9f24a6e9936 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 @@ -79,18 +79,11 @@ class OtlpMetricsPropertiesConfigAdapter extends StepRegistryPropertiesConfigAda @Override public CompressionMode compressionMode() { - CompressionMode compressionMode = this.properties.getCompressionMode(); - if (compressionMode != null) { - return compressionMode; - } - OtlpProperties.Compression compressionProperties = this.otlpProperties.getCompression(); - if (compressionProperties != null) { - return switch (compressionProperties) { - case GZIP -> CompressionMode.GZIP; - case NONE -> CompressionMode.NONE; - }; - } - return OtlpConfig.super.compressionMode(); + return this.otlpProperties.resolveCompression(this.properties.getCompressionMode(), + OtlpConfig.super.compressionMode(), (commonCompression) -> switch (commonCompression) { + case GZIP -> CompressionMode.GZIP; + case NONE -> CompressionMode.NONE; + }); } @Override @@ -103,8 +96,8 @@ class OtlpMetricsPropertiesConfigAdapter extends StepRegistryPropertiesConfigAda @Override public Map headers() { - Map headers = new LinkedHashMap<>(this.otlpProperties.getHeaders()); - headers.putAll(obtain(OtlpMetricsProperties::getHeaders, OtlpConfig.super::headers)); + Map headers = this.otlpProperties + .mergeHeaders(obtain(OtlpMetricsProperties::getHeaders, OtlpConfig.super::headers)); return Collections.unmodifiableMap(headers); } diff --git a/module/spring-boot-micrometer-metrics/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/module/spring-boot-micrometer-metrics/src/main/resources/META-INF/additional-spring-configuration-metadata.json index b33b36aab41..edeeb570aa0 100644 --- a/module/spring-boot-micrometer-metrics/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/module/spring-boot-micrometer-metrics/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1821,6 +1821,12 @@ "since": "3.0.0" } }, + { + "name": "management.otlp.metrics.export.compression-mode", + "type": "org.springframework.boot.micrometer.metrics.autoconfigure.export.otlp.OtlpMetricsProperties$CompressionMode", + "description": "Compression mode to use when exporting metrics.", + "defaultValue": "none" + }, { "name": "management.promethus.metrics.export.pushgateway.base-url", "type": "java.lang.String", 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 b26865eda75..074d8cfee9b 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,9 +16,7 @@ package org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.otlp; -import java.util.LinkedHashMap; import java.util.Locale; -import java.util.Map; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; @@ -111,13 +109,8 @@ final class OtlpTracingConfigurations { 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(); - if (endpoint != null && transport == Transport.HTTP) { - endpoint = endpoint.endsWith("/") ? endpoint + "v1/traces" : endpoint + "/v1/traces"; - } - } + String path = (transport == Transport.HTTP) ? "v1/traces" : null; + String endpoint = this.otlpProperties.resolveEndpoint(this.properties.getEndpoint(), path); Assert.state(endpoint != null, "'endpoint' must not be null"); return endpoint; } @@ -153,9 +146,7 @@ final class OtlpTracingConfigurations { .setTimeout(properties.getTimeout()) .setConnectTimeout(properties.getConnectTimeout()) .setCompression(resolveCompression(properties, otlpProperties).name().toLowerCase(Locale.ROOT)); - Map headers = new LinkedHashMap<>(otlpProperties.getHeaders()); - headers.putAll(properties.getHeaders()); - headers.forEach(builder::addHeader); + otlpProperties.mergeHeaders(properties.getHeaders()).forEach(builder::addHeader); meterProvider.ifAvailable(builder::setMeterProvider); configureSsl(connectionDetails, builder::setSslContext); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); @@ -172,9 +163,7 @@ final class OtlpTracingConfigurations { .setTimeout(properties.getTimeout()) .setConnectTimeout(properties.getConnectTimeout()) .setCompression(resolveCompression(properties, otlpProperties).name().toLowerCase(Locale.ROOT)); - Map headers = new LinkedHashMap<>(otlpProperties.getHeaders()); - headers.putAll(properties.getHeaders()); - headers.forEach(builder::addHeader); + otlpProperties.mergeHeaders(properties.getHeaders()).forEach(builder::addHeader); meterProvider.ifAvailable(builder::setMeterProvider); configureSsl(connectionDetails, builder::setSslContext); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); @@ -183,18 +172,11 @@ final class OtlpTracingConfigurations { private OtlpTracingProperties.Compression resolveCompression(OtlpTracingProperties properties, OtlpProperties otlpProperties) { - OtlpTracingProperties.Compression compression = properties.getCompression(); - if (compression != null) { - return compression; - } - OtlpProperties.Compression compressionProperties = otlpProperties.getCompression(); - if (compressionProperties != null) { - return switch (compressionProperties) { - case GZIP -> OtlpTracingProperties.Compression.GZIP; - case NONE -> OtlpTracingProperties.Compression.NONE; - }; - } - return OtlpTracingProperties.Compression.NONE; + return otlpProperties.resolveCompression(properties.getCompression(), + OtlpTracingProperties.Compression.NONE, (commonCompression) -> switch (commonCompression) { + case GZIP -> OtlpTracingProperties.Compression.GZIP; + case NONE -> OtlpTracingProperties.Compression.NONE; + }); } private void configureSsl(OtlpTracingConnectionDetails connectionDetails, diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 1777ce89671..2faf18f4636 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,6 +1,12 @@ { "groups": [], "properties": [ + { + "name": "management.opentelemetry.tracing.export.otlp.compression", + "type": "org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.otlp.OtlpTracingProperties$Compression", + "description": "Method used to compress the payload.", + "defaultValue": "none" + }, { "name": "management.otlp.tracing.compression", "deprecation": { diff --git a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingAutoConfigurationTests.java b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingAutoConfigurationTests.java index 5499a6af591..2eeb63ee4e4 100644 --- a/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingAutoConfigurationTests.java +++ b/module/spring-boot-micrometer-tracing-opentelemetry/src/test/java/org/springframework/boot/micrometer/tracing/opentelemetry/autoconfigure/otlp/OtlpTracingAutoConfigurationTests.java @@ -34,6 +34,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration; import org.springframework.boot.micrometer.tracing.opentelemetry.autoconfigure.otlp.OtlpTracingConfigurations.ConnectionDetails.PropertiesOtlpTracingConnectionDetails; +import org.springframework.boot.opentelemetry.autoconfigure.OtlpProperties; import org.springframework.boot.ssl.SslBundle; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -199,6 +200,49 @@ class OtlpTracingAutoConfigurationTests { .run((context) -> assertThat(context).hasSingleBean(PropertiesOtlpTracingConnectionDetails.class)); } + @Test + void shouldAppendTracesPathToCommonEndpointForHttpTransport() { + OtlpTracingProperties properties = new OtlpTracingProperties(); + properties.setTransport(Transport.HTTP); + OtlpProperties otlpProperties = new OtlpProperties(); + otlpProperties.setEndpoint("http://localhost:4318"); + PropertiesOtlpTracingConnectionDetails connectionDetails = new PropertiesOtlpTracingConnectionDetails( + properties, otlpProperties, null); + assertThat(connectionDetails.getUrl(Transport.HTTP)).isEqualTo("http://localhost:4318/v1/traces"); + } + + @Test + void shouldNotAppendTracesPathToCommonEndpointForGrpcTransport() { + OtlpTracingProperties properties = new OtlpTracingProperties(); + properties.setTransport(Transport.GRPC); + OtlpProperties otlpProperties = new OtlpProperties(); + otlpProperties.setEndpoint("http://localhost:4318"); + PropertiesOtlpTracingConnectionDetails connectionDetails = new PropertiesOtlpTracingConnectionDetails( + properties, otlpProperties, null); + assertThat(connectionDetails.getUrl(Transport.GRPC)).isEqualTo("http://localhost:4318"); + } + + @Test + void shouldNotAppendTracesPathToTracingSpecificEndpoint() { + OtlpTracingProperties properties = new OtlpTracingProperties(); + properties.setEndpoint("http://localhost:4318/custom/traces"); + OtlpProperties otlpProperties = new OtlpProperties(); + otlpProperties.setEndpoint("http://localhost:4318"); + PropertiesOtlpTracingConnectionDetails connectionDetails = new PropertiesOtlpTracingConnectionDetails( + properties, otlpProperties, null); + assertThat(connectionDetails.getUrl(Transport.HTTP)).isEqualTo("http://localhost:4318/custom/traces"); + } + + @Test + void shouldAppendTracesPathToCommonEndpointWithTrailingSlash() { + OtlpTracingProperties properties = new OtlpTracingProperties(); + OtlpProperties otlpProperties = new OtlpProperties(); + otlpProperties.setEndpoint("http://localhost:4318/"); + PropertiesOtlpTracingConnectionDetails connectionDetails = new PropertiesOtlpTracingConnectionDetails( + properties, otlpProperties, null); + assertThat(connectionDetails.getUrl(Transport.HTTP)).isEqualTo("http://localhost:4318/v1/traces"); + } + @Test void testConnectionFactoryWithOverridesWhenUsingCustomConnectionDetails() { this.contextRunner.withUserConfiguration(ConnectionDetailsConfiguration.class).run((context) -> { 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 6bc97903bc0..80112706c25 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 @@ -18,10 +18,12 @@ package org.springframework.boot.opentelemetry.autoconfigure; import java.util.LinkedHashMap; import java.util.Map; +import java.util.function.Function; import org.jspecify.annotations.Nullable; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.util.StringUtils; /** * Common configuration properties for OpenTelemetry Protocol (OTLP) exporters. @@ -67,6 +69,57 @@ public class OtlpProperties { this.compression = compression; } + /** + * Resolves the endpoint to use, falling back to this common endpoint (with + * {@code path} appended) when {@code signalEndpoint} is not set. + * @param signalEndpoint the signal-specific endpoint, or {@code null} if not set + * @param path the path to append to the common endpoint when used as a fallback, or + * {@code null} if no path should be appended + * @return the resolved endpoint, or {@code null} if neither endpoint is set + */ + public @Nullable String resolveEndpoint(@Nullable String signalEndpoint, @Nullable String path) { + if (StringUtils.hasLength(signalEndpoint)) { + return signalEndpoint; + } + if (this.endpoint == null || path == null) { + return this.endpoint; + } + return this.endpoint.endsWith("/") ? this.endpoint + path : this.endpoint + "/" + path; + } + + /** + * Merges the given signal-specific headers with these common headers. Entries in + * {@code signalHeaders} take precedence over common headers with the same key. + * @param signalHeaders the signal-specific headers + * @return the merged headers + */ + public Map mergeHeaders(Map signalHeaders) { + Map merged = new LinkedHashMap<>(this.headers); + merged.putAll(signalHeaders); + return merged; + } + + /** + * Resolves the compression to use, falling back to this common compression, mapped + * through {@code mapper}, when {@code signalCompression} is not set. + * @param the signal-specific compression type + * @param signalCompression the signal-specific compression, or {@code null} if not + * set + * @param defaultCompression the compression to use when neither is set + * @param mapper maps this common compression to the signal-specific type + * @return the resolved compression + */ + public T resolveCompression(@Nullable T signalCompression, T defaultCompression, + Function mapper) { + if (signalCompression != null) { + return signalCompression; + } + if (this.compression != null) { + return mapper.apply(this.compression); + } + return defaultCompression; + } + /** * Compression methods. */ 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 0ef2d0a1055..4628b1383be 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,9 +16,7 @@ package org.springframework.boot.opentelemetry.autoconfigure.logging.otlp; -import java.util.LinkedHashMap; import java.util.Locale; -import java.util.Map; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; @@ -112,13 +110,8 @@ final class OtlpLoggingConfigurations { 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(); - if (endpoint != null && transport == Transport.HTTP) { - endpoint = endpoint.endsWith("/") ? endpoint + "v1/logs" : endpoint + "/v1/logs"; - } - } + String path = (transport == Transport.HTTP) ? "v1/logs" : null; + String endpoint = this.otlpProperties.resolveEndpoint(this.properties.getEndpoint(), path); Assert.state(endpoint != null, "'endpoint' must not be null"); return endpoint; } @@ -156,9 +149,7 @@ final class OtlpLoggingConfigurations { .setTimeout(properties.getTimeout()) .setConnectTimeout(properties.getConnectTimeout()) .setCompression(resolveCompression(properties, otlpProperties).name().toLowerCase(Locale.US)); - Map headers = new LinkedHashMap<>(otlpProperties.getHeaders()); - headers.putAll(properties.getHeaders()); - headers.forEach(builder::addHeader); + otlpProperties.mergeHeaders(properties.getHeaders()).forEach(builder::addHeader); meterProvider.ifAvailable(builder::setMeterProvider); configureSsl(connectionDetails, builder::setSslContext); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); @@ -176,9 +167,7 @@ final class OtlpLoggingConfigurations { .setTimeout(properties.getTimeout()) .setConnectTimeout(properties.getConnectTimeout()) .setCompression(resolveCompression(properties, otlpProperties).name().toLowerCase(Locale.US)); - Map headers = new LinkedHashMap<>(otlpProperties.getHeaders()); - headers.putAll(properties.getHeaders()); - headers.forEach(builder::addHeader); + otlpProperties.mergeHeaders(properties.getHeaders()).forEach(builder::addHeader); meterProvider.ifAvailable(builder::setMeterProvider); configureSsl(connectionDetails, builder::setSslContext); customizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); @@ -187,18 +176,11 @@ final class OtlpLoggingConfigurations { private OtlpLoggingProperties.Compression resolveCompression(OtlpLoggingProperties properties, OtlpProperties otlpProperties) { - OtlpLoggingProperties.Compression compression = properties.getCompression(); - if (compression != null) { - return compression; - } - OtlpProperties.Compression commonCompression = otlpProperties.getCompression(); - if (commonCompression != null) { - return switch (commonCompression) { - case GZIP -> OtlpLoggingProperties.Compression.GZIP; - case NONE -> OtlpLoggingProperties.Compression.NONE; - }; - } - return OtlpLoggingProperties.Compression.NONE; + return otlpProperties.resolveCompression(properties.getCompression(), + OtlpLoggingProperties.Compression.NONE, (commonCompression) -> switch (commonCompression) { + case GZIP -> OtlpLoggingProperties.Compression.GZIP; + case NONE -> OtlpLoggingProperties.Compression.NONE; + }); } private void configureSsl(OtlpLoggingConnectionDetails connectionDetails, diff --git a/module/spring-boot-opentelemetry/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/module/spring-boot-opentelemetry/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 771da43d854..42733a6b4a5 100644 --- a/module/spring-boot-opentelemetry/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/module/spring-boot-opentelemetry/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -7,6 +7,12 @@ "defaultValue": true, "description": "Whether auto-configuration of logging is enabled to export logs over OTLP." }, + { + "name": "management.opentelemetry.logging.export.otlp.compression", + "type": "org.springframework.boot.opentelemetry.autoconfigure.logging.otlp.OtlpLoggingProperties$Compression", + "description": "Method used to compress the payload.", + "defaultValue": "none" + }, { "name": "management.opentelemetry.map-environment-variables", "type": "java.lang.Boolean", diff --git a/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpPropertiesTests.java b/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpPropertiesTests.java new file mode 100644 index 00000000000..7286d7258f1 --- /dev/null +++ b/module/spring-boot-opentelemetry/src/test/java/org/springframework/boot/opentelemetry/autoconfigure/OtlpPropertiesTests.java @@ -0,0 +1,124 @@ +/* + * 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.util.Map; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link OtlpProperties}. + * + * @author Moritz Halbritter + */ +class OtlpPropertiesTests { + + private final OtlpProperties properties = new OtlpProperties(); + + @Test + void shouldUseSignalEndpointWhenSet() { + this.properties.setEndpoint("http://common:4318"); + assertThat(this.properties.resolveEndpoint("http://signal:4318/custom", "v1/traces")) + .isEqualTo("http://signal:4318/custom"); + } + + @Test + void shouldFallBackToCommonEndpointWithPathWhenSignalEndpointIsNotSet() { + this.properties.setEndpoint("http://common:4318"); + assertThat(this.properties.resolveEndpoint(null, "v1/traces")).isEqualTo("http://common:4318/v1/traces"); + } + + @Test + void shouldFallBackToCommonEndpointWithPathWhenSignalEndpointIsEmpty() { + this.properties.setEndpoint("http://common:4318"); + assertThat(this.properties.resolveEndpoint("", "v1/traces")).isEqualTo("http://common:4318/v1/traces"); + } + + @Test + void shouldNotDuplicateSlashWhenCommonEndpointHasTrailingSlash() { + this.properties.setEndpoint("http://common:4318/"); + assertThat(this.properties.resolveEndpoint(null, "v1/traces")).isEqualTo("http://common:4318/v1/traces"); + } + + @Test + void shouldReturnCommonEndpointUnchangedWhenPathIsNull() { + this.properties.setEndpoint("http://common:4318"); + assertThat(this.properties.resolveEndpoint(null, null)).isEqualTo("http://common:4318"); + } + + @Test + void shouldReturnNullWhenNeitherEndpointIsSet() { + assertThat(this.properties.resolveEndpoint(null, "v1/traces")).isNull(); + } + + @Test + void shouldMergeCommonAndSignalHeaders() { + this.properties.getHeaders().put("common-header", "common-value"); + assertThat(this.properties.mergeHeaders(Map.of("signal-header", "signal-value"))) + .containsEntry("common-header", "common-value") + .containsEntry("signal-header", "signal-value"); + } + + @Test + void shouldPreferSignalHeaderWhenKeyIsPresentInBoth() { + this.properties.getHeaders().put("shared-header", "common-value"); + assertThat(this.properties.mergeHeaders(Map.of("shared-header", "signal-value"))).containsEntry("shared-header", + "signal-value"); + } + + @Test + void shouldReturnSignalHeadersWhenNoCommonHeadersAreSet() { + assertThat(this.properties.mergeHeaders(Map.of("signal-header", "signal-value"))) + .containsExactly(Map.entry("signal-header", "signal-value")); + } + + @Test + void shouldUseSignalCompressionWhenSet() { + this.properties.setCompression(OtlpProperties.Compression.GZIP); + assertThat(this.properties.resolveCompression(TestCompression.NONE, TestCompression.GZIP, this::map)) + .isEqualTo(TestCompression.NONE); + } + + @Test + void shouldFallBackToMappedCommonCompressionWhenSignalCompressionIsNotSet() { + this.properties.setCompression(OtlpProperties.Compression.GZIP); + assertThat(this.properties.resolveCompression(null, TestCompression.NONE, this::map)) + .isEqualTo(TestCompression.GZIP); + } + + @Test + void shouldReturnDefaultCompressionWhenNeitherIsSet() { + assertThat(this.properties.resolveCompression(null, TestCompression.NONE, this::map)) + .isEqualTo(TestCompression.NONE); + } + + private TestCompression map(OtlpProperties.Compression compression) { + return switch (compression) { + case GZIP -> TestCompression.GZIP; + case NONE -> TestCompression.NONE; + }; + } + + private enum TestCompression { + + GZIP, NONE + + } + +}