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..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,8 +60,57 @@ 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 = resolveOverride(location, overrides.metrics); + this.logs = resolveOverride(location, overrides.logs); + } + + private URI resolveOverride( + URI location, + String override) + { + URI overrideURI = URI.create(override); + + if (overrideURI.isAbsolute() || overrideURI.getAuthority() != null) + { + return overrideURI; + } + + String basePath = location.getPath(); + if (basePath == null || basePath.isEmpty()) + { + basePath = "/"; + } + + if (!basePath.endsWith("/")) + { + basePath += "/"; + } + + 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() + ); + + return normalizedBase.resolve(overridePath); + } + catch (Exception ex) + { + LangUtil.rethrowUnchecked(ex); + return location; + } } 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..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(); @@ -138,4 +139,103 @@ 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"))); + } + + @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"))); + } }