diff --git a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/connectivity/HttpEndpointConnectivity.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/connectivity/HttpEndpointConnectivity.kt index 5540b7260..d3c594060 100644 --- a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/connectivity/HttpEndpointConnectivity.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/connectivity/HttpEndpointConnectivity.kt @@ -2,7 +2,6 @@ * Copyright The OpenTelemetry Authors * SPDX-License-Identifier: Apache-2.0 */ - package io.opentelemetry.android.agent.connectivity internal class HttpEndpointConnectivity private constructor( @@ -15,12 +14,13 @@ internal class HttpEndpointConnectivity private constructor( companion object { fun forTraces( baseUrl: String, + fullUrl: Boolean = false, headers: Map, compression: Compression, sslContext: SSLContextConnectivity?, clientTls: ClientTlsConnectivity? ): HttpEndpointConnectivity = HttpEndpointConnectivity( - baseUrl.trimEnd('/') + "/v1/traces", + if (fullUrl) baseUrl else baseUrl.trimEnd('/') + "/v1/traces", headers, compression, sslContext, @@ -29,12 +29,13 @@ internal class HttpEndpointConnectivity private constructor( fun forLogs( baseUrl: String, + fullUrl: Boolean = false, headers: Map, compression: Compression, sslContext: SSLContextConnectivity?, clientTls: ClientTlsConnectivity? ): HttpEndpointConnectivity = HttpEndpointConnectivity( - baseUrl.trimEnd('/') + "/v1/logs", + if (fullUrl) baseUrl else baseUrl.trimEnd('/') + "/v1/logs", headers, compression, sslContext, @@ -43,12 +44,13 @@ internal class HttpEndpointConnectivity private constructor( fun forMetrics( baseUrl: String, + fullUrl: Boolean = false, headers: Map, compression: Compression, sslContext: SSLContextConnectivity?, clientTls: ClientTlsConnectivity? ): HttpEndpointConnectivity = HttpEndpointConnectivity( - baseUrl.trimEnd('/') + "/v1/metrics", + if (fullUrl) baseUrl else baseUrl.trimEnd('/') + "/v1/metrics", headers, compression, sslContext, @@ -57,12 +59,8 @@ internal class HttpEndpointConnectivity private constructor( } override fun getUrl(): String = url - override fun getHeaders(): Map = headers - override fun getCompression(): Compression = compression - override fun getSslContext(): SSLContextConnectivity? = sslContext - override fun getClientTls(): ClientTlsConnectivity? = clientTls -} +} \ No newline at end of file diff --git a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/dsl/EndpointConfiguration.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/dsl/EndpointConfiguration.kt index c925ac651..f23a018c2 100644 --- a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/dsl/EndpointConfiguration.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/dsl/EndpointConfiguration.kt @@ -2,7 +2,6 @@ * Copyright The OpenTelemetry Authors * SPDX-License-Identifier: Apache-2.0 */ - package io.opentelemetry.android.agent.dsl import io.opentelemetry.android.agent.connectivity.Compression @@ -13,9 +12,16 @@ import io.opentelemetry.android.agent.connectivity.Compression @OpenTelemetryDslMarker class EndpointConfiguration internal constructor( /** - * URL for HTTP export requests. + * Base URL for HTTP export requests. The signal-specific path (e.g. /v1/logs) will be + * appended automatically. */ var url: String, + /** + * Full URL for HTTP export requests. When set, this URL is used as-is without appending + * any signal-specific path. Use this to specify a completely custom endpoint path, + * for example "https://example.com/v2/logs". + */ + var fullUrl: String? = null, /** * Headers that should be attached to HTTP export requests. */ @@ -24,4 +30,4 @@ class EndpointConfiguration internal constructor( * Compression algorithm. */ var compression: Compression? = null, -) +) \ No newline at end of file diff --git a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/dsl/HttpExportConfiguration.kt b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/dsl/HttpExportConfiguration.kt index 6672d06be..83a4c0cdd 100644 --- a/android-agent/src/main/kotlin/io/opentelemetry/android/agent/dsl/HttpExportConfiguration.kt +++ b/android-agent/src/main/kotlin/io/opentelemetry/android/agent/dsl/HttpExportConfiguration.kt @@ -2,7 +2,6 @@ * Copyright The OpenTelemetry Authors * SPDX-License-Identifier: Apache-2.0 */ - package io.opentelemetry.android.agent.dsl import io.opentelemetry.android.Incubating @@ -50,31 +49,36 @@ class HttpExportConfiguration internal constructor() { internal fun spansEndpoint(): HttpEndpointConnectivity = HttpEndpointConnectivity.forTraces( chooseUrlSource(spansConfig), + isFullUrl(spansConfig), spansConfig.headers + baseHeaders, chooseCompression(spansConfig.compression), sslContext, - clientTls + clientTls, ) internal fun logsEndpoint(): HttpEndpointConnectivity = HttpEndpointConnectivity.forLogs( chooseUrlSource(logsConfig), + isFullUrl(logsConfig), logsConfig.headers + baseHeaders, chooseCompression(logsConfig.compression), sslContext, - clientTls + clientTls, ) internal fun metricsEndpoint(): HttpEndpointConnectivity = HttpEndpointConnectivity.forMetrics( chooseUrlSource(metricsConfig), + isFullUrl(metricsConfig), metricsConfig.headers + baseHeaders, chooseCompression(metricsConfig.compression), sslContext, - clientTls + clientTls, ) - private fun chooseUrlSource(cfg: EndpointConfiguration): String = cfg.url.ifBlank { baseUrl } + private fun chooseUrlSource(cfg: EndpointConfiguration): String = cfg.fullUrl ?: cfg.url.ifBlank { baseUrl } + + private fun isFullUrl(cfg: EndpointConfiguration): Boolean = cfg.fullUrl != null private fun chooseCompression(signalConfigCompression: Compression?): Compression = signalConfigCompression ?: this.compression @@ -98,4 +102,4 @@ class HttpExportConfiguration internal constructor() { fun metrics(action: EndpointConfiguration.() -> Unit) { metricsConfig.action() } -} +} \ No newline at end of file diff --git a/android-agent/src/test/kotlin/io/opentelemetry/android/agent/dsl/HttpExportConfigurationTest.kt b/android-agent/src/test/kotlin/io/opentelemetry/android/agent/dsl/HttpExportConfigurationTest.kt index b946cbabf..3b373d5fa 100644 --- a/android-agent/src/test/kotlin/io/opentelemetry/android/agent/dsl/HttpExportConfigurationTest.kt +++ b/android-agent/src/test/kotlin/io/opentelemetry/android/agent/dsl/HttpExportConfigurationTest.kt @@ -316,4 +316,103 @@ internal class HttpExportConfigurationTest { return ClientTlsConnectivity(privateKeyPem, certificatePem) } + @Test + fun testFullUrlOverrideForLogs() { + val baseUrl = "http://localhost:4318/" + val customLogsUrl = "http://localhost:4318/v2/logs" + + val config = + otelConfig.exportConfig.apply { + this.baseUrl = baseUrl + logs { + fullUrl = customLogsUrl + } + } + + // logs should use the full custom URL without appending /v1/logs + config.logsEndpoint().assertEndpointConfig( + customLogsUrl, + emptyMap(), + Compression.GZIP, + null + ) + // spans and metrics should still use baseUrl + default path + config.spansEndpoint().assertEndpointConfig( + "${baseUrl}v1/traces", + emptyMap(), + Compression.GZIP, + null + ) + config.metricsEndpoint().assertEndpointConfig( + "${baseUrl}v1/metrics", + emptyMap(), + Compression.GZIP, + null + ) + } + + @Test + fun testFullUrlOverrideForAllSignals() { + val customSpansUrl = "http://traces.example.com/v2/traces" + val customLogsUrl = "http://logs.example.com/v2/logs" + val customMetricsUrl = "http://metrics.example.com/v2/metrics" + + val config = + otelConfig.exportConfig.apply { + spans { + fullUrl = customSpansUrl + } + logs { + fullUrl = customLogsUrl + } + metrics { + fullUrl = customMetricsUrl + } + } + + config.spansEndpoint().assertEndpointConfig( + customSpansUrl, + emptyMap(), + Compression.GZIP, + null + ) + config.logsEndpoint().assertEndpointConfig( + customLogsUrl, + emptyMap(), + Compression.GZIP, + null + ) + config.metricsEndpoint().assertEndpointConfig( + customMetricsUrl, + emptyMap(), + Compression.GZIP, + null + ) + } + + @Test + fun testFullUrlTakesPrecedenceOverUrl() { + val baseUrl = "http://localhost:4318/" + val signalUrl = "http://localhost:4318/logs/" + val customFullUrl = "http://localhost:4318/v2/logs" + + val config = + otelConfig.exportConfig.apply { + this.baseUrl = baseUrl + logs { + url = signalUrl + fullUrl = customFullUrl + } + } + + // fullUrl should take precedence over url + config.logsEndpoint().assertEndpointConfig( + customFullUrl, + emptyMap(), + Compression.GZIP, + null + ) + } + + }