diff --git a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSampler.java b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSampler.java index 53966c5303204..06aa5ecf48438 100644 --- a/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSampler.java +++ b/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSampler.java @@ -25,8 +25,7 @@ public SamplingResult shouldSample(Context parentContext, String traceId, String if (spanKind.equals(SpanKind.SERVER)) { String target = attributes.get(SemanticAttributes.HTTP_TARGET); - // TODO - radcortez - Match /* endpoints - if (target != null && dropTargets.contains(target)) { + if (shouldDrop(target)) { return SamplingResult.drop(); } } @@ -34,6 +33,36 @@ public SamplingResult shouldSample(Context parentContext, String traceId, String return sampler.shouldSample(parentContext, traceId, name, spanKind, attributes, parentLinks); } + /** + * Determines whether a path should be dropped + * TODO: this can certainly be optimized if we find that it's a hot-path + */ + private boolean shouldDrop(String target) { + if ((target == null) || target.isEmpty()) { + return false; + } + if (safeContains(target)) { // check exact match + return true; + } + if (target.charAt(target.length() - 1) == '/') { // check if the path without the ending slash matched + if (safeContains(target.substring(0, target.length() - 1))) { + return true; + } + } + int lastSlashIndex = target.lastIndexOf('/'); + if (lastSlashIndex != -1) { + if (safeContains(target.substring(0, lastSlashIndex) + "*") + || safeContains(target.substring(0, lastSlashIndex) + "/*")) { // check if a wildcard matches + return true; + } + } + return false; + } + + private boolean safeContains(String target) { + return dropTargets.contains(target); + } + @Override public String getDescription() { return sampler.getDescription(); diff --git a/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSamplerTest.java b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSamplerTest.java new file mode 100644 index 0000000000000..662c0538b2ab4 --- /dev/null +++ b/extensions/opentelemetry/runtime/src/test/java/io/quarkus/opentelemetry/runtime/tracing/DropTargetsSamplerTest.java @@ -0,0 +1,62 @@ +package io.quarkus.opentelemetry.runtime.tracing; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import org.junit.jupiter.api.Test; + +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.SamplingResult; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; + +class DropTargetsSamplerTest { + + @Test + void testDropTargets() { + CountingSampler countingSampler = new CountingSampler(); + var sut = new DropTargetsSampler(countingSampler, List.of("/q/swagger-ui", "/q/swagger-ui*")); + + assertEquals(SamplingResult.recordAndSample(), getShouldSample(sut, "/other")); + assertEquals(1, countingSampler.count.get()); + + assertEquals(SamplingResult.drop(), getShouldSample(sut, "/q/swagger-ui")); + assertEquals(1, countingSampler.count.get()); + + assertEquals(SamplingResult.drop(), getShouldSample(sut, "/q/swagger-ui/")); + assertEquals(1, countingSampler.count.get()); + + assertEquals(SamplingResult.drop(), getShouldSample(sut, "/q/swagger-ui/whatever")); + assertEquals(1, countingSampler.count.get()); + + assertEquals(SamplingResult.recordAndSample(), getShouldSample(sut, "/q/test")); + assertEquals(2, countingSampler.count.get()); + } + + private static SamplingResult getShouldSample(DropTargetsSampler sut, String target) { + return sut.shouldSample(null, null, null, SpanKind.SERVER, + Attributes.of(SemanticAttributes.HTTP_TARGET, target), null); + } + + private static final class CountingSampler implements Sampler { + + final AtomicLong count = new AtomicLong(0); + + @Override + public SamplingResult shouldSample(Context parentContext, String traceId, String name, SpanKind spanKind, + Attributes attributes, List parentLinks) { + count.incrementAndGet(); + return SamplingResult.recordAndSample(); + } + + @Override + public String getDescription() { + return "test"; + } + } +}