From 2caffd7b6d74b3c5cf0b685db870adee3d13ba8f Mon Sep 17 00:00:00 2001 From: Kunal Sable <118177615+Kunals990@users.noreply.github.com> Date: Thu, 6 Nov 2025 02:21:40 +0530 Subject: [PATCH 1/2] fix path concatenation for non-root base endpoints, added test to check --- .../internal/config/OtlpExporterConfig.java | 35 +++++++++- .../config/OtlpExporterConfigTest.java | 66 +++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/runtime/exporter-otlp/src/main/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfig.java b/runtime/exporter-otlp/src/main/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfig.java index 7d0f8b1769..3d07beec14 100644 --- a/runtime/exporter-otlp/src/main/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfig.java +++ b/runtime/exporter-otlp/src/main/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfig.java @@ -60,8 +60,39 @@ public OtlpExporterConfig( OtlpEndpointConfig endpoint = options.endpoint; URI location = endpoint.location; OtlpOverridesConfig overrides = endpoint.overrides; - this.metrics = location.resolve(overrides.metrics); - this.logs = location.resolve(overrides.logs); + + this.metrics = normalizeAndJoinPaths(location, overrides.metrics); + this.logs = normalizeAndJoinPaths(location, overrides.logs); + } + + private URI normalizeAndJoinPaths(URI base, String path) { + String basePath = base.getPath(); + if (basePath == null) { + basePath = "/"; + } + + String pathToAppend = path; + if (pathToAppend.startsWith("/")) { + pathToAppend = pathToAppend.substring(1); + } + + if (!basePath.endsWith("/")) { + basePath += "/"; + } + + try { + return new URI( + base.getScheme(), + base.getUserInfo(), + base.getHost(), + base.getPort(), + basePath + pathToAppend, + base.getQuery(), + base.getFragment() + ); + } catch (Exception e) { + throw new RuntimeException("Failed to join URI paths", e); + } } public HttpClient supplyHttpClient( diff --git a/runtime/exporter-otlp/src/test/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfigTest.java b/runtime/exporter-otlp/src/test/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfigTest.java index 7335016f97..79e25a7648 100644 --- a/runtime/exporter-otlp/src/test/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfigTest.java +++ b/runtime/exporter-otlp/src/test/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfigTest.java @@ -138,4 +138,70 @@ public void shouldOverrideRelativeMetricsUrl() assertThat(metrics, equalTo(URI.create("http://example.com/v42/metrix"))); assertThat(logs, equalTo(URI.create("http://example.com/v42/logz"))); } + + @Test + public void shouldAppendPathsWithNonRootBase() + { + // GIVEN + OtlpOptionsConfig options = OtlpOptionsConfig.builder() + .interval(Duration.ofSeconds(30L)) + .signals(Set.of(METRICS)) + .endpoint() + .protocol("http") + .location(URI.create("http://localhost:8080/telemetry")) + .overrides() + .metrics("/v1/metrics") + .logs("/v1/logs") + .build() + .build() + .build(); + ExporterConfig exporter = ExporterConfig.builder() + .namespace("test") + .name("oltp0") + .type("oltp") + .options(options) + .build(); + OtlpExporterConfig oltpExporter = new OtlpExporterConfig(config, context, exporter); + + // WHEN + URI metrics = oltpExporter.metrics; + URI logs = oltpExporter.logs; + + // THEN + assertThat(metrics, equalTo(URI.create("http://localhost:8080/telemetry/v1/metrics"))); + assertThat(logs, equalTo(URI.create("http://localhost:8080/telemetry/v1/logs"))); + } + + @Test + public void shouldHandlePathsWithoutLeadingSlash() + { + // GIVEN + OtlpOptionsConfig options = OtlpOptionsConfig.builder() + .interval(Duration.ofSeconds(30L)) + .signals(Set.of(METRICS)) + .endpoint() + .protocol("http") + .location(URI.create("http://localhost:8080/telemetry")) + .overrides() + .metrics("v1/metrics") + .logs("v1/logs") + .build() + .build() + .build(); + ExporterConfig exporter = ExporterConfig.builder() + .namespace("test") + .name("oltp0") + .type("oltp") + .options(options) + .build(); + OtlpExporterConfig oltpExporter = new OtlpExporterConfig(config, context, exporter); + + // WHEN + URI metrics = oltpExporter.metrics; + URI logs = oltpExporter.logs; + + // THEN + assertThat(metrics, equalTo(URI.create("http://localhost:8080/telemetry/v1/metrics"))); + assertThat(logs, equalTo(URI.create("http://localhost:8080/telemetry/v1/logs"))); + } } From 4df4483ca73cd282d0ce239596bbc66b7b029f4d Mon Sep 17 00:00:00 2001 From: Kunal Sable <118177615+Kunals990@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:14:38 +0530 Subject: [PATCH 2/2] Fix path appending for non-root OTLP base endpoints - Add resolveOverride() to handle path-only and full URI overrides - Normalize base path and strip leading slash from override paths - Add tests for non-root base path and full URI override scenarios --- .../internal/config/OtlpExporterConfig.java | 62 ++++++++++++------- .../config/OtlpExporterConfigTest.java | 42 +++++++++++-- 2 files changed, 78 insertions(+), 26 deletions(-) diff --git a/runtime/exporter-otlp/src/main/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfig.java b/runtime/exporter-otlp/src/main/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfig.java index 3d07beec14..9cdab336fc 100644 --- a/runtime/exporter-otlp/src/main/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfig.java +++ b/runtime/exporter-otlp/src/main/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfig.java @@ -60,38 +60,56 @@ public OtlpExporterConfig( OtlpEndpointConfig endpoint = options.endpoint; URI location = endpoint.location; OtlpOverridesConfig overrides = endpoint.overrides; - - this.metrics = normalizeAndJoinPaths(location, overrides.metrics); - this.logs = normalizeAndJoinPaths(location, overrides.logs); + this.metrics = resolveOverride(location, overrides.metrics); + this.logs = resolveOverride(location, overrides.logs); } - private URI normalizeAndJoinPaths(URI base, String path) { - String basePath = base.getPath(); - if (basePath == null) { - basePath = "/"; + private URI resolveOverride( + URI location, + String override) + { + URI overrideURI = URI.create(override); + + if (overrideURI.isAbsolute() || overrideURI.getAuthority() != null) + { + return overrideURI; } - String pathToAppend = path; - if (pathToAppend.startsWith("/")) { - pathToAppend = pathToAppend.substring(1); + String basePath = location.getPath(); + if (basePath == null || basePath.isEmpty()) + { + basePath = "/"; } - if (!basePath.endsWith("/")) { + if (!basePath.endsWith("/")) + { basePath += "/"; } - try { - return new URI( - base.getScheme(), - base.getUserInfo(), - base.getHost(), - base.getPort(), - basePath + pathToAppend, - base.getQuery(), - base.getFragment() + String overridePath = override; + if (overridePath.startsWith("/")) + { + overridePath = overridePath.substring(1); + } + + try + { + URI normalizedBase = new URI( + location.getScheme(), + location.getUserInfo(), + location.getHost(), + location.getPort(), + basePath, + location.getQuery(), + location.getFragment() ); - } catch (Exception e) { - throw new RuntimeException("Failed to join URI paths", e); + + return normalizedBase.resolve(overridePath); + } + catch (Exception ex) + { + LangUtil.rethrowUnchecked(ex); + return location; } } diff --git a/runtime/exporter-otlp/src/test/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfigTest.java b/runtime/exporter-otlp/src/test/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfigTest.java index 79e25a7648..a10f673a7a 100644 --- a/runtime/exporter-otlp/src/test/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfigTest.java +++ b/runtime/exporter-otlp/src/test/java/io/aklivity/zilla/runtime/exporter/otlp/internal/config/OtlpExporterConfigTest.java @@ -15,6 +15,7 @@ package io.aklivity.zilla.runtime.exporter.otlp.internal.config; import static io.aklivity.zilla.runtime.exporter.otlp.config.OtlpOptionsConfig.OtlpSignalsConfig.METRICS; +import static io.aklivity.zilla.runtime.exporter.otlp.config.OtlpOptionsConfig.OtlpSignalsConfig.LOGS; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; @@ -84,8 +85,8 @@ public void shouldOverrideAbsoluteMetricsUrl() .protocol("http") .location(URI.create("http://example.com")) .overrides() - .metrics(URI.create("http://overridden.com/metrics")) - .logs(URI.create("http://overridden.com/logs")) + .metrics("http://overridden.com/metrics") + .logs("http://overridden.com/logs") .build() .build() .build(); @@ -117,8 +118,8 @@ public void shouldOverrideRelativeMetricsUrl() .protocol("http") .location(URI.create("http://example.com")) .overrides() - .metrics(URI.create("/v42/metrix")) - .logs(URI.create("/v42/logz")) + .metrics("/v42/metrix") + .logs("/v42/logz") .build() .build() .build(); @@ -204,4 +205,37 @@ public void shouldHandlePathsWithoutLeadingSlash() assertThat(metrics, equalTo(URI.create("http://localhost:8080/telemetry/v1/metrics"))); assertThat(logs, equalTo(URI.create("http://localhost:8080/telemetry/v1/logs"))); } + + @Test + public void shouldAllowFullUriOverrideWithNonRootBase() + { + // GIVEN + OtlpOptionsConfig options = OtlpOptionsConfig.builder() + .interval(Duration.ofSeconds(30L)) + .signals(Set.of(METRICS, LOGS)) + .endpoint() + .protocol("http") + .location(URI.create("http://localhost:8080/telemetry")) + .overrides() + .metrics("https://metrics-endpoint.com:9090/api/metrics") + .logs("http://logs.other.com/v1/post") + .build() + .build() + .build(); + ExporterConfig exporter = ExporterConfig.builder() + .namespace("test") + .name("oltp0") + .type("oltp") + .options(options) + .build(); + OtlpExporterConfig oltpExporter = new OtlpExporterConfig(config, context, exporter); + + // WHEN + URI metrics = oltpExporter.metrics; + URI logs = oltpExporter.logs; + + // THEN + assertThat(metrics, equalTo(URI.create("https://metrics-endpoint.com:9090/api/metrics"))); + assertThat(logs, equalTo(URI.create("http://logs.other.com/v1/post"))); + } }