diff --git a/extensions/opentelemetry/opentelemetry/deployment/pom.xml b/extensions/opentelemetry/opentelemetry/deployment/pom.xml index 5427563d3b711..da340ef230fb2 100644 --- a/extensions/opentelemetry/opentelemetry/deployment/pom.xml +++ b/extensions/opentelemetry/opentelemetry/deployment/pom.xml @@ -64,6 +64,11 @@ quarkus-vertx-http-deployment test + + io.quarkus + quarkus-smallrye-health-deployment + test + io.quarkus diff --git a/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsDisabledTest.java b/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsDisabledTest.java new file mode 100644 index 0000000000000..7c8864961aaa7 --- /dev/null +++ b/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsDisabledTest.java @@ -0,0 +1,52 @@ +package io.quarkus.opentelemetry.deployment; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +import java.util.List; + +import javax.inject.Inject; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class NonAppEndpointsDisabledTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClass(TracerRouter.class) + .addClass(TestSpanExporter.class)); + + @Inject + TestSpanExporter testSpanExporter; + + @Test + void testHealthEndpointNotTraced() throws InterruptedException { + RestAssured.when().get("/q/health").then() + .statusCode(200) + .body(containsString("\"status\": \"UP\"")); + + RestAssured.when().get("/q/health/live").then() + .statusCode(200) + .body(containsString("\"status\": \"UP\"")); + + RestAssured.when().get("/q/health/ready").then() + .statusCode(200) + .body(containsString("\"status\": \"UP\"")); + + RestAssured.when().get("/tracer").then() + .statusCode(200) + .body(is("Hello Tracer!")); + + List spans = testSpanExporter.getFinishedSpanItems(); + + Assertions.assertEquals(2, spans.size()); + } +} diff --git a/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsEnabledTest.java b/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsEnabledTest.java new file mode 100644 index 0000000000000..25b9ceef02d4f --- /dev/null +++ b/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/NonAppEndpointsEnabledTest.java @@ -0,0 +1,53 @@ +package io.quarkus.opentelemetry.deployment; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +import java.util.List; + +import javax.inject.Inject; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.opentelemetry.sdk.trace.data.SpanData; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class NonAppEndpointsEnabledTest { + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .overrideConfigKey("quarkus.opentelemetry.tracer.suppress-non-application-uris", "false") + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClass(TracerRouter.class) + .addClass(TestSpanExporter.class)); + + @Inject + TestSpanExporter testSpanExporter; + + @Test + void testHealthEndpointNotTraced() throws InterruptedException { + RestAssured.when().get("/q/health").then() + .statusCode(200) + .body(containsString("\"status\": \"UP\"")); + + RestAssured.when().get("/q/health/live").then() + .statusCode(200) + .body(containsString("\"status\": \"UP\"")); + + RestAssured.when().get("/q/health/ready").then() + .statusCode(200) + .body(containsString("\"status\": \"UP\"")); + + RestAssured.when().get("/tracer").then() + .statusCode(200) + .body(is("Hello Tracer!")); + + List spans = testSpanExporter.getFinishedSpanItems(); + + Assertions.assertEquals(5, spans.size()); + } +} diff --git a/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySamplerConfigTest.java b/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySamplerConfigTest.java index 1548afe7f1f38..176a64f720064 100644 --- a/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySamplerConfigTest.java +++ b/extensions/opentelemetry/opentelemetry/deployment/src/test/java/io/quarkus/opentelemetry/deployment/OpenTelemetrySamplerConfigTest.java @@ -21,6 +21,7 @@ public class OpenTelemetrySamplerConfigTest { .overrideConfigKey("quarkus.opentelemetry.tracer.sampler", "ratio") .overrideConfigKey("quarkus.opentelemetry.tracer.sampler.ratio", "0.5") .overrideConfigKey("quarkus.opentelemetry.tracer.sampler.parent-based", "false") + .overrideConfigKey("quarkus.opentelemetry.tracer.suppress-non-application-uris", "false") .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class).addClass(TestUtil.class)); @Inject diff --git a/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/NonApplicationEndpointSampler.java b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/NonApplicationEndpointSampler.java new file mode 100644 index 0000000000000..bae2db995d28e --- /dev/null +++ b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/NonApplicationEndpointSampler.java @@ -0,0 +1,35 @@ +package io.quarkus.opentelemetry.runtime.tracing; + +import java.util.List; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingDecision; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; + +public class NonApplicationEndpointSampler implements Sampler { + private static final SamplingResult NEGATIVE_SAMPLING_RESULT = SamplingResult.create(SamplingDecision.DROP); + + private final Sampler root; + + public NonApplicationEndpointSampler(Sampler root) { + this.root = root; + } + + @Override + public SamplingResult shouldSample(Context parentContext, String traceId, String name, SpanKind spanKind, + Attributes attributes, List parentLinks) { + if (name.startsWith("q/") && spanKind == SpanKind.SERVER) { + return NEGATIVE_SAMPLING_RESULT; + } + return root.shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); + } + + @Override + public String getDescription() { + return "NonApplicationEndpointBased{/q}"; + } +} diff --git a/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerRecorder.java b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerRecorder.java index 0b72fdb4e7802..66de886fa0edd 100644 --- a/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerRecorder.java +++ b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerRecorder.java @@ -140,7 +140,7 @@ public void setupSampler(TracerRuntimeConfig config) { lateBoundSampler.setSamplerDelegate(samplerBean.get()); } else { // Define Sampler using config - lateBoundSampler.setSamplerDelegate(TracerUtil.mapSampler(config.sampler)); + lateBoundSampler.setSamplerDelegate(TracerUtil.mapSampler(config.sampler, config.suppressNonApplicationUris)); } } } diff --git a/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerRuntimeConfig.java b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerRuntimeConfig.java index 38be78991da0f..590979af87c7a 100644 --- a/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerRuntimeConfig.java +++ b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerRuntimeConfig.java @@ -22,6 +22,18 @@ public class TracerRuntimeConfig { /** Config for sampler */ public SamplerConfig sampler; + /** + * Suppress non-application uris from trace collection. + * This will suppress tracing of `/q` endpoints. + *

+ * Providing a custom {@code io.opentelemetry.sdk.trace.samplers.Sampler} CDI Bean + * will ignore this setting. + *

+ * Suppressing non-application uris is enabled by default. + */ + @ConfigItem(defaultValue = "true") + public boolean suppressNonApplicationUris; + @ConfigGroup public static class SamplerConfig { /** diff --git a/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerUtil.java b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerUtil.java index a637db71a3fd4..b321d82d0f29e 100644 --- a/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerUtil.java +++ b/extensions/opentelemetry/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/TracerUtil.java @@ -41,9 +41,13 @@ private static Sampler getBaseSampler(String samplerName, Optional ratio } } - public static Sampler mapSampler(TracerRuntimeConfig.SamplerConfig samplerConfig) { + public static Sampler mapSampler(TracerRuntimeConfig.SamplerConfig samplerConfig, boolean suppressNonApplicationUris) { Sampler sampler = getBaseSampler(samplerConfig.samplerName, samplerConfig.ratio); + if (suppressNonApplicationUris) { + sampler = new NonApplicationEndpointSampler(sampler); + } + if (samplerConfig.parentBased) { return Sampler.parentBased(sampler); }